Filter sql based on C# List instead of a filter table

时间:2017-05-16 09:27:57

标签: c# sql firebird firebird2.1

Say I have a table with the following data:

enter image description here

Now I want to filter by the primary keys department and number. I have a list of department and number combinations that have to be filtered in code. In my mind, I would create a join that results in the following:

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

dynamicTable is my List in C# code that has the primary keys to filter, but I don't know how to pass this list to the database level.

I dont't want to load everything from my employees table and that filter in code by linq or something else, because I have millions of employees in my database.

I already thought about combining the primary_keys and create a where in (...), but firebird has restriction to max 1500 records in a where in.

Database used is Firebird version 2.1

1 个答案:

答案 0 :(得分:4)

我个人可以看到你可以追求的两个技巧。而过去的一次爆炸"更多。

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

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

等等。

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

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

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

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

简单的想法是这样的,让我们假设我们想要获取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 '~1~4~12~24~' CONTAINING '~' || ID || '~' 

这假设ID将从整数自动转换为字符串。如果不是你必须手动完成:.... 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制作,功能减少。 LIKE是否可以对FB2.1中的BLOB起作用?我不记得了,请查看发行说明。还要检查您的库是否甚至允许您将参数类型指定为BLOB而不是字符串。 现在,你的CONNECTION CHARSET是什么?如果它像Windows-1250或Windows-1251那样 - 那么一个字符就是一个字节,你可以将32K字符装入32KB字节。但是如果您的应用程序设置的CONNECTION CHARSET是UTF-8 - 则每个字母占用4个字节,最大VARCHARable字符串减少到8K字母。

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

另请参阅:{em> 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版本中使用此技巧。

Pro:您无需更改持久性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 - 但只知道技巧。