【问题标题】:Filter sql based on C# List instead of a filter table基于 C# List 而不是过滤表过滤 sql
【发布时间】:2017-05-16 09:27:57
【问题描述】:

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

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

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 (...),但 firebird 限制在 where in 中最多 1500 条记录。

使用的数据库是 Firebird 2.1 版

【问题讨论】:

  • “我的数据库中有数百万员工。” - 作为一个有趣的问题,你的数据库文件有多大,你有多少表。 select count(*) from RDB$RELATIONS where rdb$relation_type=0 and ( rdb$System_flag is null or rdb$System_flag=0 )也许你真的会考虑升级FB...
  • 我有 92 张桌子,其中有几张桌子有大量的行。这是因为有许多支付交易和用户操作需要保存和评估...更新 FB 并不容易,因为有近百个客户端使用 2.1 版本,更新意味着测试!但是更新有什么好处呢?
  • 和文件大小?根据 www.translate.ru -> ibase.ru/profitmed FB 2.5 的索引和通用 I/O 更快,这应该有利于那些非常“长”的表。 FB 3.0 可能会更好,但我个人确实怀疑它的打磨程度。此外,2.5 制作了更多的监控表并引入了跟踪 API 来调查瓶颈。见 2.5 相关注释。 2.1 客户端通常可以在不做任何更改的情况下与 2.5 服务器一起使用,但不建议这样做。所以如果你有一台服务器,那么也许在测试之后你可以只做一个切换,然后慢慢拉起客户端。
  • 让 interbase/firebird 通过公共网络的典型解决方案是 ZeeBeDee - 但是这要求连接至少是稳定的。它加密、压缩,但在连接经常断开/恢复时不适用于类似移动的网络。坦率地说,对于预定的同步,我宁愿制作像加密的 xxxx.json.7z 这样的增量文件,然后通过任何传输方式传输这些文件。不希望使并非总是可预测的 WAN 条件对数据库服务器产生直接影响
  • 是的,我正在制作一个同步机制,但这在每一点上都不是微不足道的,我正在为此开发一个好的架构。但是 te delta 文件的观点对我来说是新的,你的想法有什么真实的例子吗?

标签: c# sql firebird firebird2.1


【解决方案1】:

就个人而言,我可以看到您可以追求的两个技巧。还有一个“来自过去的爆炸”。

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

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

等等。

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

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

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

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

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

所以相反 - 对于要匹配的非常长的列表 - 我们可以形成一个特殊的字符串。并将其作为文本进行匹配。这使得匹配本身的效率大大降低,并禁止使用任何索引,服务器在整个表上运行 NATURAL SCAN - 但它会进行一次扫描。当匹配列表非常大时,一次性全表扫描比数千次按索引获取更有效。 但是 - 仅当列表与表格的比率非常大时,取决于您的具体数据。

我们让文本列出我们所有的目标值,并用 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 个字符放入 32KBytes 中。但是,如果您的应用程序设置的 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 映射到字符集文本名字。

Route #3 穷人的 GTT。输入伪表。

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

专业人士:您无需更改持久性 SCHEMA。
缺点:不更改 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 - 但只要知道诀窍。

【讨论】:

  • 给定可能性的完美答案!!我认为大型过滤器 ID 列表(最多 100.000 或更多)最优雅的方式是全局临时表。我将创建几个整数和几个字符串列,并将它们组合(连接)到一个键。在结果加载表中,我还将组合作为复合键一部分的列。这需要一点时间,我会报告....
  • @Franki1986 如果您使用非整数键,如字符串列,那么方法 #2 将无效,它不会扩展到整数之外。并且列出多达 100K 的元素会使 #3 不起作用,那么 GTT 似乎就是你所拥有的。但是 - 如果您制作像 100K 元素一样大的过滤器 - 那么也许是时候重新考虑手头的任务了。也许那些 100K 元素可以制成一个适当的持久表并保留在那里?带有过滤列,如“filter_id”或(穷人的)“attachment_id”(参见CURRENT_CONNECTION FB var
  • 或者您可以尝试将过滤分成两个阶段,在服务器上对一些列进行初步过滤,然后将这个半过滤的数据流获取到客户端,然后进行最后的精细 -程序中使用的所有列的粒度过滤。
猜你喜欢
  • 1970-01-01
  • 2020-03-07
  • 1970-01-01
  • 2021-12-19
  • 1970-01-01
  • 2020-06-28
  • 1970-01-01
  • 1970-01-01
  • 2020-06-22
相关资源
最近更新 更多