基于C#List过滤sql而不是过滤表

假设我有一个包含以下数据的表:

在此处输入图像描述

现在我想按主键部门和号码进行过滤。 我有一个必须在代码中过滤的部门和数字组合列表。 在我看来,我会创建一个导致以下结果的连接:

select * from employee e inner join dynamicTable dyn on e.Department = dyn.Department and e.Number = dyn.Number; 

dynamicTable是我在C#代码中的List ,它有主要的过滤键,但我不知道如何将这个列表传递给数据库级别。

我不想从我的员工表中加载所有内容,并通过linq或其他方式在代码中过滤,因为我的数据库中有数百万名员工。

我已经考虑过组合primary_keys并where in (...)创建一个where in (...) ,但是firebird对其中的最多1500条记录有限制。

使用的数据库是Firebird 2.1版

我个人可以看到你可以追求的两个技巧。 还有一个“过去的爆炸”。

路线#1。 使用GTT:GLOBAL TEMPORARY TABLE

GTT中引入了GTT(并使用它),可以是每个连接或每个事务。 你会想要每个交易一个。 这种差异是关于数据(行),架构(结构和索引,元数据)是持久的。 请参阅GTT文档中的ON COMMIT DELETE ROWS选项。

等等。

通过这种方式,您打开事务,用列表中的数据填充GTT(将1500个数据对的数据从工作站复制到服务器),运行查询JOINing over GTT,然后执行COMMIT事务和表内容是自动删除的。

如果您可以在会话中运行许多几乎相似的查询,那么可以根据需要进行GTT每个连接并根据需要修改数据,而不是在每个下一个事务中为每个下一个查询重新填充它,但这是一种更复杂的方法。 在每个COMMIT的早期COMMIT是我更喜欢的默认方法,直到争论为什么在这种特定情况下每个连接会更好。 只是不要在查询之间将垃圾留在服务器上。

路线#2。 使用字符串搜索 – 反向LIKE匹配。

在其基本forms中,此方法适用于搜索一些巨大且任意的整数列表。 你的情况有点复杂,你匹配数字对,而不是单数。

简单的想法是这样的,我们假设我们想要获取ID列可以是1,4,12,24的行。直接的方法是对每个值进行4次查询,或者使WHERE ID = 1 or ID = 4 or ...或使用WHERE id IN (1,4,12,24) 。 在内部, IN将展开到非常= or = or =然后最有可能作为四个查询执行。 长列表效率不高。

所以相反 – 对于真正长的匹配列表 – 我们可以形成一个特殊的字符串。 并将其作为文本进行匹配。 这使得匹配本身的效率低得多,并且禁止使用任何索引,服务器在整个表上运行自然扫描 – 但它进行一次扫描。 当匹配列表非常大时,一次通过全表扫描比数千个索引提取更有效。 但是 – 仅当列表与表的比例非常大时,才取决于您的具体数据。

我们将文本列入所有目标值,并将AND穿插在一个分隔符中:“~1~4~12~24~”。 现在我们为ID列生成相同的delimiter-number-delimiter字符串,看看是否可以找到这样的子字符串。

LIKE / CONTAINING的通常用法是将列与下面的数据进行匹配: SELECT * from the_table WHERE column_name CONTAINING value_param
我们SELECT * from the_table WHERE value_param CONTAINING column_name-based-expression反转它, SELECT * from the_table WHERE value_param CONTAINING column_name-based-expression

  SELECT * from the_table WHERE '~1~4~12~24~' CONTAINING '~' || ID || '~' 

这假设ID将从整数自动转换为字符串。 如果不是你必须手动完成: .... CONTAINING '~' || CAST( ID as VARCHAR(100) ) || '~' .... CONTAINING '~' || CAST( ID as VARCHAR(100) ) || '~'

你的情况有点复杂,你需要匹配两个数字,部门和数字,所以你必须使用两个不同的分隔符,如果你按照这种方式。 就像是

 SELECT * FROM employee e WHERE '~1@10~1@11~2@20~3@7~3@66~' CONTAINING '~' || e.Department || '@' || e.Number || '~' 

问:你说你的目标列表是1500个元素。 目标线将是……很长。 多长时间???

Firebird中的VARCHAR限制为32KB AFAIR,较长的文本应作为文本BLOB制作,function减少。 LIKE是否可以对抗FB2.1中的BLOB? 我不记得了,请查看发行说明。 还要检查您的库是否甚至允许您将参数类型指定为BLOB而不是字符串。 现在,你的CONNECTION CHARSET是什么? 如果它像Windows-1250或Windows-1251那样 – 那么一个字符就是一个字节,你可以将32K字符装入32KB字节。 但是如果你的应用程序设置的CONNECTION CHARSET是UTF-8 – 则每个字母占用4个字节,最大VARCHARable字符串减少到8K字母。

您可以尝试避免对此长字符串使用参数,并将目标字符串常量内联到SQL语句中。 但是,您可能会达到最大SQL语句长度的限制。

另请参阅: c:\ Program Files \ Firebird \ Firebird_2_1 \ doc \ README.monitoring_tables.txt中的 MON$CHARACTER_SET_ID ,然后FB文档中的SYSTEM TABLES部分介绍如何将ID映射到charset文本名称。

路线#3穷人的GTT。 输入伪表。

在引入GTT之前,有时可以在较旧的IB / FB版本中使用此技巧。

亲:您不需要更改持久性SCHEMA。
Con:不更改SCHEME – 您无法创建索引,也无法使用索引连接。 而且,您可以再次达到单个SQL语句的长度限制。

真的,不要认为这适用于你的情况,只是为了使答案完整我认为这个技巧也应该被提及。

 select * from employee e, ( SELECT 1 as Department, 10 as Number FROM RDB$DATABASE UNION ALL SELECT 1, 11 FROM RDB$DATABASE UNION ALL SELECT 2, 20 FROM RDB$DATABASE UNION ALL SELECT 3, 7 FROM RDB$DATABASE UNION ALL SELECT 3, 66 FROM RDB$DATABASE ) t, where e.Department = t.Department and e.Number = t.Number 

原油和丑陋,但有时这个伪表可能会有所帮助。 什么时候? 主要是它有助于批量INSERT-from-SELECT,不需要索引:-D它很少适用于SELECT – 但只知道技巧。