Neo*_*Neo 16 sql-server-2008 sql-server optimization t-sql execution-plan
我有一个 SQL 查询,我花了两天时间尝试使用试错法和执行计划进行优化,但无济于事。请原谅我这样做,但我会在这里发布整个执行计划。我已经努力使查询和执行计划中的表名和列名通用,既为了简洁又为了保护我公司的 IP。可以使用SQL Sentry Plan Explorer打开执行计划。
我已经完成了大量的 T-SQL,但是使用执行计划来优化我的查询对我来说是一个新领域,我真的试图了解如何去做。所以,如果有人能帮我解决这个问题并解释如何破译这个执行计划以在查询中找到优化它的方法,我将永远感激不尽。我还有更多的查询需要优化——我只需要一个跳板来帮助我完成第一个查询。
这是查询:
DECLARE @Param0 DATETIME = '2013-07-29';
DECLARE @Param1 INT = CONVERT(INT, CONVERT(VARCHAR, @Param0, 112))
DECLARE @Param2 VARCHAR(50) = 'ABC';
DECLARE @Param3 VARCHAR(100) = 'DEF';
DECLARE @Param4 VARCHAR(50) = 'XYZ';
DECLARE @Param5 VARCHAR(100) = NULL;
DECLARE @Param6 VARCHAR(50) = 'Text3';
SET NOCOUNT ON
DECLARE @MyTableVar TABLE
(
B_Var1_PK int,
Job_Var1 varchar(512),
Job_Var2 varchar(50)
)
INSERT INTO @MyTableVar (B_Var1_PK, Job_Var1, Job_Var2)
SELECT B_Var1_PK, Job_Var1, Job_Var2 FROM [fn_GetJobs] (@Param1, @Param2, @Param3, @Param4, @Param6);
CREATE TABLE #TempTable
(
TTVar1_PK INT PRIMARY KEY,
TTVar2_LK VARCHAR(100),
TTVar3_LK VARCHAR(50),
TTVar4_LK INT,
TTVar5 VARCHAR(20)
);
INSERT INTO #TempTable
SELECT DISTINCT
T.T1_PK,
T.T1_Var1_LK,
T.T1_Var2_LK,
MAX(T.T1_Var3_LK),
T.T1_Var4_LK
FROM
MyTable1 T
INNER JOIN feeds.MyTable2 A ON A.T2_Var1 = T.T1_Var4_LK
INNER JOIN @MyTableVar B ON B.Job_Var2 = A.T2_Var2 AND B.Job_Var1 = A.T2_Var3
GROUP BY T.T1_PK, T.T1_Var1_LK, T.T1_Var2_LK, T.T1_Var4_LK
-- This is the slow statement...
SELECT
CASE E.E_Var1_LK
WHEN 'Text1' THEN T.TTVar2_LK + '_' + F.F_Var1
WHEN 'Text2' THEN T.TTVar2_LK + '_' + F.F_Var2
WHEN 'Text3' THEN T.TTVar2_LK
END,
T.TTVar4_LK,
T.TTVar3_LK,
CASE E.E_Var1_LK
WHEN 'Text1' THEN F.F_Var1
WHEN 'Text2' THEN F.F_Var2
WHEN 'Text3' THEN T.TTVar5
END,
A.A_Var3_FK_LK,
C.C_Var1_PK,
SUM(CONVERT(DECIMAL(18,4), A.A_Var1) + CONVERT(DECIMAL(18,4), A.A_Var2))
FROM #TempTable T
INNER JOIN TableA (NOLOCK) A ON A.A_Var4_FK_LK = T.TTVar1_PK
INNER JOIN @MyTableVar B ON B.B_Var1_PK = A.Job
INNER JOIN TableC (NOLOCK) C ON C.C_Var2_PK = A.A_Var5_FK_LK
INNER JOIN TableD (NOLOCK) D ON D.D_Var1_PK = A.A_Var6_FK_LK
INNER JOIN TableE (NOLOCK) E ON E.E_Var1_PK = A.A_Var7_FK_LK
LEFT OUTER JOIN feeds.TableF (NOLOCK) F ON F.F_Var1 = T.TTVar5
WHERE A.A_Var8_FK_LK = @Param1
GROUP BY
CASE E.E_Var1_LK
WHEN 'Text1' THEN T.TTVar2_LK + '_' + F.F_Var1
WHEN 'Text2' THEN T.TTVar2_LK + '_' + F.F_Var2
WHEN 'Text3' THEN T.TTVar2_LK
END,
T.TTVar4_LK,
T.TTVar3_LK,
CASE E.E_Var1_LK
WHEN 'Text1' THEN F.F_Var1
WHEN 'Text2' THEN F.F_Var2
WHEN 'Text3' THEN T.TTVar5
END,
A.A_Var3_FK_LK,
C.C_Var1_PK
IF OBJECT_ID(N'tempdb..#TempTable') IS NOT NULL
BEGIN
DROP TABLE #TempTable
END
IF OBJECT_ID(N'tempdb..#TempTable') IS NOT NULL
BEGIN
DROP TABLE #TempTable
END
Run Code Online (Sandbox Code Playgroud)
我发现第三个语句(评论为慢)是花费最多时间的部分。之前的两个语句几乎立即返回。
执行计划在此链接中以 XML 形式提供。
最好右键单击并保存,然后在 SQL Sentry Plan Explorer 或其他一些查看软件中打开,而不是在浏览器中打开。
如果您需要我提供有关表格或数据的更多信息,请随时询问。
Pau*_*ite 23
在获得主要答案之前,您需要更新两个软件。
第一个是 SQL Server。您正在运行 SQL Server 2008 Service Pack 1(内部版本 2531)。您应该至少修补到当前的 Service Pack(SQL Server 2008 Service Pack 3 - build 5500)。在撰写本文时,SQL Server 2008 的最新版本是 Service Pack 3,累积更新 12(版本 5844)。
第二个软件是SQL Sentry Plan Explorer。最新版本具有重要的新功能和修复,包括能够直接上传查询计划以进行专家分析(无需将 XML 粘贴到任何地方!)
由于语句级重新编译,表变量的基数估计是完全正确的:

不幸的是,表变量不维护分布统计信息,所以优化器只知道有六行;它对这六行中可能存在的值一无所知。考虑到下一个操作是连接到另一个表,此信息至关重要。该连接的基数估计基于优化器的疯狂猜测:

从那时起,优化器选择的计划基于不正确的信息,因此性能如此糟糕也就不足为奇了。特别是,为排序和哈希表预留的用于哈希连接的内存将太小。在执行时,溢出的排序和散列操作将溢出到物理tempdb 磁盘。
SQL Server 2008 没有在执行计划中突出显示这一点;您可以使用 Extended Events 或 Profiler Sort Warnings和Hash Warnings监控溢出。内存是为执行开始前基于基数估计的排序和散列保留的,并且在执行期间不能增加,无论您的 SQL Server 可能有多少空闲内存。因此,准确的行计数估计对于任何涉及工作区内存消耗操作的执行计划都至关重要。
您的查询也已参数化。OPTION (RECOMPILE)如果不同的参数值影响查询计划,您应该考虑添加到查询中。无论如何,您可能应该考虑使用它,以便优化器可以@Param1在编译时看到 的值。如果不出意外,这可能有助于优化器对上面显示的索引查找产生更合理的估计,因为该表非常大且已分区。它还可以启用静态分区消除。
使用临时表而不是表变量和 再次尝试查询OPTION (RECOMPILE)。您还应该尝试将第一次联接的结果具体化到另一个临时表中,然后针对该表运行其余的查询。行数并不是很大(3,285,620),所以这应该相当快。然后优化器将有一个精确的基数估计和连接结果的分布统计信息。幸运的话,计划的其余部分将顺利实施。
根据计划中显示的属性,具体化查询将是:
SELECT
A.A_Var7_FK_LK,
A.A_Var4_FK_LK,
A.A_Var6_FK_LK,
A.A_Var5_FK_LK,
A.A_Var1,
A.A_Var2,
A.A_Var3_FK_LK
INTO #AnotherTempTable
FROM @MyTableVar AS B
JOIN TableA AS A
ON A.Job = B.B_Var1_PK
WHERE
A_Var8_FK_LK = @Param1;
Run Code Online (Sandbox Code Playgroud)
你也可以INSERT进入一个预定义的临时表(正确的数据类型没有显示在计划中,所以我不能做那部分)。新的临时表可能会也可能不会受益于聚集索引和非聚集索引。
| 归档时间: |
|
| 查看次数: |
15117 次 |
| 最近记录: |