SQL在性能上不好?

我有一个查询做类似于:

SELECT FieldX, FieldY FROM A WHERE FieldW IN (108, 109, 113, 138, 146, 160, 307, 314, 370, 371, 441, 454 ,457, 458, 479, 480, 485, 488, 490, 492, 519, 523, 525, 534, 539, 543, 546, 547, 550, 564, 573, 629, 642, 643, 649, 650, 651, 694, 698, 699, 761, 762, 768, 772, 773, 774, 775, 778, 784, 843, 844, 848, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 868, 869, 871, 872, 873, 891) 

具有这么多选项的IN子句,对查询性能不利? 我在应用程序中遇到了很多超时错误,我相信这可能是这类问题的根源。 我可以优化查询而不删除数字,使用任何良好的SQL提示?

编辑:

@KM这些是在不同的表中的键。 这是一个论坛应用程序,简要解释:c#从数据库获取所有论坛并将其存储在应用程序caching中。 在C#调用获取这些论坛和该用户的线程的过程之前,c#会考虑权限和一些业务逻辑,对“所有论坛”集合进行一些逻辑过滤。 超时发生在数据库而不是应用程序本身。 在查询中执行所有这些逻辑将需要大量的内部连接,而且我不能100%确定可以在这个过程中完成所有这些工作。

我正在使用SQL Server 2000

使用IN运算符编写查询可能会影响性能时,有几个注意事项。

首先,IN子句通常由大多数数据库在内部重写,以使用OR逻辑连接符。 因此col IN ('a','b','c')被重写为: (COL = 'a') OR (COL = 'b') or (COL = 'c') 。 这两个查询的执行计划可能是等效的,假设你有一个索引在col

其次,在使用IN或者OR来使用可变数量的参数时,导致数据库必须重新parsing查询,并在每次参数更改时重新生成一个执行计划。 构build查询的执行计划可能是一个昂贵的步骤。 大多数数据库都将使用EXACT查询文本作为关键字运行的查询的执行计划caching。 如果执行一个类似的查询,但在谓词中使用不同的参数值 – 那么很可能会导致数据库花费大量的时间parsing和构build执行计划。 这就是强烈build议绑定variables作为确保最佳查询性能的一种方式。

第三,许多数据库对它们可以执行的查询的复杂性有限制 – 其中一个限制是可以包含在谓词中的逻辑连接词的数量。 在你的情况下,几十个值不太可能达到数据库的内置限制,但是如果你希望将数百或数千个值传递给IN子句 – 肯定会发生。 在这种情况下,数据库将简单地取消查询请求。

第四,在谓词中包含IN和OR的查询不能总是在并行环境中被最优地重写。 并行服务器优化无法应用的情况有多种 – MSDN有一个体面的介绍来优化并行性查询。 一般来说,使用UNION ALL操作符的查询在大多数数据库中是可平行的,并且在可能的情况下优先于逻辑连接(比如OR和IN)。

如果你在FieldW上有一个好的索引,那么使用这个IN是完全正确的。

我刚刚经过testing,SQL 2000在使用IN时进行了群集索引扫描。

您可以尝试创build一个临时表,将值插入到临时表中,并在IN谓词中使用该表。

AFAIK, SQL Server 2000不能build立一组常量的散列表,这就剥夺了优化器使用HASH SEMI JOIN的可能性。

这只有在你没有FieldW (你应该有)的索引时FieldW帮助。

您也可以尝试将您的FieldXFieldY引:

 CREATE INDEX ix_a_wxy ON a (FieldW, FieldX, FieldY) 

以便查询只能通过使用索引来提供。

SQL Server 2000缺lessCREATE INDEX INCLUDE选项,这可能会降低DML性能,但会提高查询性能。

更新:

从你的执行计划中我看到,你需要一个复合索引(SettingsID, SectionID)

SQL Server 2000实际上可以从一个常量列表中构build一个哈希表(并且这样做),但是对于查询查询, Hash Semi Join最有可能效率会低于Nested Loop

只是一个侧面说明:如果您需要知道满足WHERE条件的行数,请不要使用COUNT(column) ,而应使用COUNT(*)

COUNT(column)不计算column值为NULL

这意味着,首先,您可以得到您没有想到的结果;其次,如果列没有被服务于WHERE条件的索引覆盖,那么优化器将需要执行额外的Key Lookup / Bookmark Lookup

由于ThreadId似乎是一个CLUSTERED PRIMARY KEY ,所以对于这个查询来说是正确的,但是尽量避免它。

根据您的数据分布情况,WHERE子句中的其他谓词可能会提高性能。 例如,如果一组ID相对于表中的总数很小,并且您知道ID相对较近(也许它们通常是最近添加的,因此聚集在该范围的高端),你可以尝试并包含谓词“AND FieldW BETWEEN 109 AND 891”(在确定C#代码中的最小和最大id之后)。 对这些列进行范围扫描(如果编入索引的话)可能比当前使用的更快。

有更好的方法来编码,但我怀疑这是超时的原因,特别是如果它只是一个SELECT。 您应该能够通过查看您的查询跟踪来确定。 但重新编码这将是猜测优化,并在此不可能的猜测。

让我们从实际上超时的查询的查询计划开始。 你确定知道哪个查询是?

IN与编写一个大的OR列表完全一样。 OR常常使查询变得不可查询,因此您的索引可能会被忽略,并且计划会进行全面扫描。

通常情况下,IN子句对性能有害,但什么是“坏”取决于应用程序,数据,数据库大小等。您需要testing自己的应用程序以查看哪些是最好的。

使用此语句时,表格的大小将决定速度。 如果它不是一个非常大的表…这个声明不影响你的performance。

这是你的答案

http://www.4guysfromrolla.com/webtech/031004-1.shtml

基本上,你想要创build一个函数来分割一个string,并用分割的内容填充一个临时表。 然后你可以join到临时表并操纵你的数据。 上面的解释很好。 我使用这种技术很多。

在你的具体情况下,使用临时表而不是in子句的联接,要快得多。

我通常会使用用户定义的表types来查询这样的查询。

 CREATE TYPE [dbo].[udt_int] AS TABLE ( [id] [int] NOT NULL ) 

使用一个表格variables并用行填充每个数字,你可以这样做:

 SELECT FieldX, FieldY FROM A INNER JOIN @myIds B ON A.FieldW = B.id 

基本上where子句所做的是“FieldW = 108或Fi​​eldW = 109或FieldW = 113 …”。 有时候你可以通过做多个select来获得更好的performance,并把它们和union结合起来。 例如:

 SELECT FieldX, FieldY FROM A WHERE FieldW = 108 UNION ALL SELECT FieldX, FieldY FROM A WHERE FieldW = 109 

但是,当你比较这么多的价值时,这当然是不切实际的。

另一个选项可能是将这些值插入临时表,然后将A表连接到该临时表。

绩效只能根据你所要做的事来判断。 在这种情况下,你要求检索大约70行(假设它们是唯一的值),所以你可以期望得到像检索单个值的持续时间的70倍。 这可能不是由于caching,或当然。

但是,查询优化器可能需要或select执行全表扫描以检索值,在这种情况下,性能将与通过相同的访问计划检索单个值的方法稍有不同。

如果你可以使用IN以外的其他东西:做(在某些情况下我使用的不是真正的好方法:我可以很容易地用存在代替,速度更快)

在你的情况下:似乎不是那么糟糕。

你可能会尝试这样的:

 select a.FieldX, a.FieldY from ( select FieldW = 108 union select FieldW = 109 union select FieldW = 113 union ... select FieldW = 891 ) _a join A a on a.FieldW = _a.FieldW 

这可能适合您的情况,例如当您想要dynamic生成单个SQL语句时。 在我的机器(SQL Server 2008 Express)上,使用less量(5个)FieldW值和A中的大量(100,000)行进行testing,这使用A上的索引查找,并在A和_a之间嵌套循环连接,这可能是你在找什么。