我知道这个话题已经被打死了,但似乎互联网上的许多文章往往是在寻找最优雅的方法,而不是最有效的方法如何解决它。这是问题所在。我们正在构建一个应用程序,其中一个常见的数据库查询将涉及基于用户提供的ID列表的操作(SELECT和UPDATE)。有问题的表预计将有数十万行,用户提供的ID列表可能是无界的,但它们很可能是数十或数百行(出于性能原因,我们稍后可能会限制它)。
如果我对数据库一般工作原理的理解是正确的,那么最有效的方法就是简单地使用WHERE ID IN (1, 2, 3, 4, 5, ...)构造并动态构建查询。问题的核心是ID的输入列表实际上是任意的,所以无论数据库有多聪明,或者我们实现它有多聪明,我们总是从一个随机的整数子集开始,所以最终每种方法都必须在内部归结为像WHERE ID IN (1, 2, 3, 4, 5, ...)这样的东西。
人们可以在web上找到许多方法。例如,需要声明一个表变量,将ID列表作为逗号分隔的字符串传递给存储过程,在存储过程中将其拆分,将ID插入表变量并在表变量上连接主表,即如下所示:
-- 1. Temporary table for ID’s:
DECLARE @IDS TABLE (ID int);
-- 2. Split the given string of ID’s, and each ID to @IDS.
-- Omitted for brevity.
-- 3. Join the main table to @ID’s:
SELECT MyTable.ID, MyTable.SomeColumn
FROM MyTable INNER JOIN @IDS ON MyTable.ID = @IDS.ID;把字符串操作的问题放在一边,我认为在这种情况下,在第三步中SQL Server会说:“谢谢,这很好,但我只需要ID的列表”,它扫描表变量@IDS,然后在MyTable中执行n次查找,其中n是ID的数量。我已经做了一些初步的性能评估,并检查了查询计划,看起来就是这样发生的。因此,表变量、字符串连接和拆分以及所有额外的INSERT都是没有意义的。
我说的对吗?还是我错过了什么?真的有更聪明、更有效的方法吗?基本上,我要说的是SQL Server无论如何都要做n次索引查找,将查询格式化为WHERE ID IN (1, 2, 3, 4, 5, ...)是请求它的最直接的方式。
发布于 2009-10-05 20:12:34
嗯,这取决于到底发生了什么。用户如何选择这些ID?
此外,这不仅仅是效率问题;还需要担心安全性和正确性。用户何时以及如何将他们的ID选择告知数据库?如何将它们合并到查询中?
将选定的ID放入一个单独的表中可能会更好,您可以针对该表进行连接(或使用WHERE EXISTS针对该表)。
我会告诉您,对于一个很小的(用户生成的) n,您在性能方面不可能比IN (1,2,3..n)好很多,但是您需要考虑如何生成查询。你打算使用动态SQL吗?如果是这样的话,你将如何保护它不被注入?服务器是否能够缓存执行计划?
此外,使用额外的表通常会更容易。假设您正在为一个eCommerce站点构建购物车。与其担心跟踪购物车客户端或会话中的情况,不如在用户每次做出选择时更新ShoppingCart表。这也避免了如何安全地为查询设置参数值的整个问题,因为您一次只进行一次更改。
别忘了这句古老的格言(向Benjamin Franklin道歉):
愿意用正确性来换取性能的人,既不配获得
发布于 2009-10-05 20:13:21
要小心;在许多数据库中,IN (...)在in子句中限制为固定数量的内容。例如,我认为在Oracle中是1000。这很重要,但可能值得了解。
发布于 2009-10-05 20:23:51
IN子句不能保证INDEX SEEK。在内存非常少的Pocket中使用SQL Mobile版本之前,我就遇到过这个问题。用OR子句列表替换IN ( list )使我的查询速度提高了400%。
另一种方法是使用一个临时表来存储ID并将其连接到目标表上,但是如果这个操作使用得太频繁,那么永久/索引表可以帮助优化器。
https://stackoverflow.com/questions/1522119
复制相似问题