选择DISTINCT使用WHERE子句非常慢,而选择DISTINCT与INNER JOIN和子句

时间:2014-02-28 22:35:13

标签: sql-server stored-procedures sql-server-2008-r2 database-performance sql-execution-plan

我们有一个生产存储过程,除了一个客户端的环境外,在2-3秒内执行。

他们的环境看起来很健康,有24个内核,64 GB RAM,而且容量不足。 SQL Server是2008 r2 SP2。

我已经在测试环境中恢复了数据库,并且sproc在2秒内返回,但在客户端环境中需要20-50分钟。

今天我在同一台服务器上设置了一个新的SQL Server实例,执行存储过程也需要20-50分钟。

我们的DBA找到了问题陈述并设计了一种解决方法。

原始

--This returns in 30 minutes.
SELECT DISTINCT
          P.ProjectID, P.ProjectName
FROM DocumentRoute DR

LEFT JOIN Routes R
ON R.RouteID = DR.RouteID

INNER JOIN Documents D
ON D.DocumentID = DR.DocumentID
AND D.Status = 1

INNER JOIN Files F
ON F.FileID = D.FileID
AND F.Status = 1

INNER JOIN Projects P
ON P.ProjectID = F.ProjectID
AND P.Status = 1

LEFT OUTER JOIN Users U
ON U.UserID = DR.UserID

WHERE DR.Status = 1

原始执行计划http://screencast.com/t/xGcRIE9o

解决方法

--This returns in 2 seconds.
SELECT DISTINCT
          P.ProjectID, P.ProjectName
FROM DocumentRoute DR

LEFT JOIN Routes R
ON R.RouteID = DR.RouteID
AND DR.Status = 1

INNER JOIN Documents D
ON D.DocumentID = DR.DocumentID
AND D.Status = 1

INNER JOIN Files F
ON F.FileID = D.FileID
AND F.Status = 1

INNER JOIN Projects P
ON P.ProjectID = F.ProjectID
AND P.Status = 1

LEFT OUTER JOIN Users U
ON U.UserID = DR.UserID

此处修订执行计划http://screencast.com/t/Fqg90w6NDyZd

客户环境中的哪些内容可能会导致执行时间的巨大差异?

其他信息:当我从客户端获得执行计划时,问题陈述本身在17秒内完成,但整个sproc已经运行了15+分钟,可能需要另外15分钟完成。

2 个答案:

答案 0 :(得分:1)

第一个问题是这两个查询的含义不同,可能会返回不同的结果。第一个查询只会生成DocumentRouteStatus = 1。第二个查询将生成所有 DocumentRoute行,并且Status为null或不等于1时,将不会执行到Routes的连接。 / p>

第二个问题是,如果您只是使用DISTINCT从Projects表中选择列,则LEFT JOIN无法以任何方式更改查询 - 因此您也可以删除它们。

最后,在没有让我们对两个查询的执行计划有所了解的情况下,可能还有一些关于所涉及表的结构的更多细节,没有人能够明确地给出你正在发生的事情的答案。此外, 环境中的执行计划(需要2秒)并不会有所帮助。我们必须知道运行缓慢的环境中的执行计划。

要获得执行计划,首先运行SET STATISTICS XML ON;,运行所需的查询,并查看在查询结果集之后给出的结果集中显示的执行计划。

关于可能导致问题的一些想法:

  • 统计信息是否设置为自动更新?如果没有,服务器可以选择一个糟糕的计划。
  • 查询中涉及的表的碎片是什么?
  • 客户端数据库服务器是否正确设置了tempdb文件 - 如果有多个,它们的大小是否相同?

答案 1 :(得分:0)

根据@ErikE的建议,我仔细检查了客户端对存储过程中多个语句的执行计划,并注意到了几个并行操作符。

因此,知道他们在服务器上有24个处理器而在我的服务器上有1个处理器,灯泡点亮了,我决定尝试MAXDOP为1,并且执行时间从45分钟变为2秒。

sp_configure 'show advanced options', 1;
RECONFIGURE WITH OVERRIDE;
GO

sp_configure 'max degree of parallelism', 1;
GO
RECONFIGURE WITH OVERRIDE