基于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
选项。
- https://www.firebirdsql.org/refdocs/langrefupd21-ddl-table.html
- http://firebirdsql.su/doku.php?id=create_global_temporary_table和www.translate.ru
- Firebird全局临时表(GTT),触摸其他表?
等等。
通过这种方式,您打开事务,用列表中的数据填充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 – 但只知道技巧。