就我个人而言,我认为有两个可以采用的技巧。还有一首“来自过去的爆炸”。
路线#1。使用 GTT:全局临时表
GTT 是在 FB 2.1 中引入的(并且您使用它),并且可以是按连接或按事务。您可能需要每笔交易的数据。这种差异与数据(行)有关,模式(结构和索引,元数据)是持久的。看ON COMMIT DELETE ROWS
GTT 文档中的选项。
- 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 个数据值对从工作站复制到服务器),在该 GTT 上运行 JOINing 查询,然后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 列相同的分隔符-数字-分隔符字符串,并查看是否可以找到这样的子字符串。
通常使用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 而不是字符串。
现在,您的连接字符集是什么?如果它类似于 Windows-1250 或 Windows-1251 - 那么一个字符就是一个字节,您可以将 32K 字符放入 32KBytes 中。但是,如果您的应用程序设置的 CONNECTION CHARSET 是 UTF-8 - 那么每个字母占用 4 个字节,并且您的最大 VARCHARable 字符串将减少到 8K 个字母。
您可以尝试避免对这个长字符串使用参数,并将目标字符串常量内联到 SQL 语句中。但随后您可能会遇到最大 SQL 语句长度的限制。
也可以看看:MON$CHARACTER_SET_ID
in c:\Program Files\Firebird\Firebird_2_1\doc\README.monitoring_tables.txt然后 FB 文档中的 SYSTEM TABLES 部分介绍了如何将 ID 映射到字符集文本名称。
Route #3穷人的 GTT。输入伪表。
在引入 GTT 之前,有时可以在较旧的 IB/FB 版本中使用此技巧。
优点:您不需要更改持久模式。
缺点:在不更改 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 - 但只需知道技巧即可。