请注意赏金 - START:
PARAMETERS SNIFFING(这是在赏金前问题中报告的唯一“想法”)不是问题,因为您可以在问题末尾的“更新”部分阅读。问题实际上与sql server如何在使用distinct时为参数化查询创建执行计划有关。 我上传了一个非常简单的数据库备份(它适用于sql server 2008 R2)here(你必须在下载前等待20秒)。对于此DB,您可以尝试运行以下查询:
-- PARAMETRIZED QUERY
declare @IS_ADMINISTRATOR int
declare @User_ID int
set @IS_ADMINISTRATOR = 1 -- 1 for administrator 0 for normal
set @User_ID = 50
SELECT DISTINCT -- PLEASE REMEMBER DISTINCT MAKES THE DIFFERENCE!!!
DOC.DOCUMENT_ID
FROM
DOCUMENTS DOC LEFT OUTER JOIN
FOLDERS FOL ON FOL.FOLDER_ID = DOC.FOLDER_ID LEFT OUTER JOIN
ROLES ROL ON (FOL.FOLDER_ID = ROL.FOLDER_ID)
WHERE
1 = @IS_ADMINISTRATOR OR ROL.USER_ID = @USER_ID
-- NON PARAMETRIZED QUERY
SELECT DISTINCT -- PLEASE REMEMBER DISTINCT MAKES THE DIFFERENCE!!!
DOC.DOCUMENT_ID
FROM
DOCUMENTS DOC LEFT OUTER JOIN
FOLDERS FOL ON FOL.FOLDER_ID = DOC.FOLDER_ID LEFT OUTER JOIN
ROLES ROL ON (FOL.FOLDER_ID = ROL.FOLDER_ID)
WHERE
1 = 1 OR ROL.USER_ID = 50
最后说明:我注意到DSTINCT是问题,我的目标是在两个查询中达到相同的速度(或至少几乎相同的速度)。
请注意赏金 - 结束:
原始问题:
我注意到
之间的性能差异很大-- Case A
select distinct * from table where id > 1
比较(这是我的Delphi应用程序生成的sql)
-- Case B1
exec sp_executesql N'select distinct * from table where id > @P1',N'@P1 int',1
相当于
-- Case B2
declare @P1 int
set @P1 = 1
select distinct * from table where id > @P1
A的执行速度比B1和B2快得多。如果我删除DISTINCT,性能会变得相同。
你可以对此发表评论吗?
这里我发布了一个简单的查询,我在3 INNER JOIN的查询中注意到了这一点。无论如何不是一个复杂的查询。
注意:在案例A和B1 / B2中,我期望获得完全相同的表现。
使用DISTINCT时有一些注意事项吗?
更新:
我尝试使用DBCC TRACEON (4136, -1)
(禁用参数嗅探的标志)禁用参数嗅探,但没有任何变化。因此,在这种情况下,问题并未与参数SNIFFING相关联。有什么想法吗?
答案 0 :(得分:2)
问题不在于DISTINCT导致参数性能下降,而是在参数化查询中没有优化查询的其余部分,因为优化器不会仅使用1优化掉所有连接= @ IS_ADMINISTRATOR就像它只有1 = 1。它不会优化连接没有不同,因为它需要根据连接的结果返回重复项。
为什么呢?因为抛出所有连接的执行计划对于@IS_ADMINISTRATOR = 1以外的任何值都是无效的。无论你是否在缓存计划,它都不会生成该计划。
这与我2008服务器上的非参数化查询一样:
-- PARAMETRIZED QUERY
declare @IS_ADMINISTRATOR int
declare @User_ID int
set @IS_ADMINISTRATOR = 1 -- 1 for administrator 0 for normal
set @User_ID = 50
IF 1 = @IS_ADMINISTRATOR
BEGIN
SELECT DISTINCT -- PLEASE REMEMBER DISTINCT MAKES THE DIFFERENCE!!!
DOC.DOCUMENT_ID
FROM
DOCUMENTS DOC LEFT OUTER JOIN
FOLDERS FOL ON FOL.FOLDER_ID = DOC.FOLDER_ID LEFT OUTER JOIN
ROLES ROL ON (FOL.FOLDER_ID = ROL.FOLDER_ID)
WHERE
1 = 1
END
ELSE
BEGIN
SELECT DISTINCT -- PLEASE REMEMBER DISTINCT MAKES THE DIFFERENCE!!!
DOC.DOCUMENT_ID
FROM
DOCUMENTS DOC LEFT OUTER JOIN
FOLDERS FOL ON FOL.FOLDER_ID = DOC.FOLDER_ID LEFT OUTER JOIN
ROLES ROL ON (FOL.FOLDER_ID = ROL.FOLDER_ID)
WHERE
ROL.USER_ID = @USER_ID
END
从查询计划中可以清楚地看到,运行您的示例的是@IS_ADMINISTRATOR = 1
未与1=1
进行优化。在非参数化示例中,JOINS已完全优化,它只返回DOCUMENTS表中的每个id(非常简单)。
@IS_ADMINISTRATOR <> 1
时还缺少不同的优化。例如,LEFT OUTER JOIN
S自动更改为INNER JOIN
s 而没有OR
子句,但它们与保持原样 >那个或条款。
另请参阅此答案:SQL LIKE % FOR INTEGERS以获取动态SQL替代方案。
当然,这并不能真正解释原始问题中的性能差异,因为那里没有OR。我认为这是一种疏忽。
答案 1 :(得分:1)
但也要看“参数嗅探”问题。
Why does a parameterized query produces vastly slower query plan vs non-parameterized query
https://groups.google.com/group/microsoft.public.sqlserver.programming/msg/1e4a2438bed08aca?hl=de
答案 2 :(得分:1)
您是否尝试过在没有动态SQL的情况下运行第二个(较慢)查询?您是否清除了缓存并重新运行第一个查询?您可能遇到parameter sniffing参数化动态SQL查询。
我认为DISTINCT
是一个红色的鲱鱼而不是实际的问题。