加速查询的方法

Bel*_*igh 3 sql-server optimization cte t-sql sql-server-2008-r2

我的查询返回大约 590 行和 8 列。我遇到的问题是从开始到结束查询需要 2 分 30 秒才能完成。这里的一群人教会了我很多如何编写更有效的查询,所以这里是另一个!

我使用的是date变量而不是变量,datetime因为我的变量只包含一个日期 - 我还使用Aaron Bertrand - Bad Habits To Kickyyyymmdd建议的格式存储我的日期。

我可以做些什么来优化此查询并使结果返回得更快?

DECLARE @Startdate date = '20170101', 
        @Enddate   date = '20170131';

WITH fc As
(
    Select
    Teacher
    ,Team
    ,fc
    FROM [Helper].[dbo].[fc]
)
,ia As
(
    Select
    Teacher
    ,tia
    FROM dbo.ia
    WHERE [hiredate] >= @Startdate
    AND [hiredate] <  DATEADD(DAY,1,@Enddate)
)
,inb As
(
    Select 
    Teacher
    ,tinb
    FROM inb
),
ripcord As
(
    Select
    Teacher
    ,pit
    FROM [homebase].[dbo].[rip]
)
,YRTR As
(
    Select
    Teacher
    ,totamt
    FROM totamt
    WHERE CAST([begindate] As DATE) BETWEEN CAST(DateAdd(yy, -1, @startdate) As Date) 
                                     AND CAST(DateAdd(yy, -1, @enddate) As Date)
)
Select 
DISTINCT rost.[Teacher] As Teacher
,[Team Name] = fc.team
,[TIA] = ROUND(SUM(ISNULL(ia.tia,0)),0)
,[SUM] = CAST(ROUND(SUM(ISNULL(inb.tinb,0))+SUM(ISNULL(rip.pit,0)),0) As INT)
,[YR] = ROUND(SUM(ISNULL(tr.totamt,0)),0)
,[fc] = ISNULL(fc,0)
,[1st Count] = COALESCE(b.[students],0)
,[2nd Count] = COALESCE(c.[potstudents],0)
FROM dbo.roster rost
LEFT JOIN fc fc
ON rost.Teacher = fc.Teacher
LEFT JOIN ia ia
ON ia.Teacher = rost.Teacher
LEFT JOIN inb inb
ON rost.Teacher = inb.Teacher
LEFT JOIN ripcord rip
ON rost.Teacher = rip.Teacher
LEFT JOIN YRTR tr
ON rost.Teacher = tr.Teacher
OUTER APPLY
( SELECT 
        DISTINCT Teacher [Teacher]
        , COUNT(students) AS students
    FROM students
    WHERE Teacher = rost.Teacher
    AND regdate >= @Startdate
    AND regdate <  DATEADD(DAY,1,@Enddate)
    GROUP BY Teacher
) AS b
OUTER APPLY
(
    Select
    DISTINCT Teacher Teacher
    ,COUNT(potstudents) As potstudents
    FROM dbo.potstudents
    WHERE Teacher = rost.Teacher
    AND returndate >= @Startdate
    AND returndate <  DATEADD(DAY,1,@Enddate)
    GROUP BY Teacher
) AS c
GROUP BY rost.[Teacher],fc.TEAM, fc.fc,b.[students],c.[potstudents]
ORDER BY rost.[Teacher] ASC
Run Code Online (Sandbox Code Playgroud)

编辑
(忘记链接,抱歉)在@sabin bio 的请求下 - 这里是查询执行计划的链接

此外,CTE 的查询视图上没有索引。

Joe*_*ish 5

首先了解查询优化器由您的查询生成的计划。在您的问题中,您说查询返回大约 590 行,但包含的查询计划仅返回 18 行。您是否附上了正确的计划?无论如何,我都会走过它。从右到左阅读:

进行全表扫描roster并取回 18 行。此结果集用作所有以下嵌套循环连接的外部部分。对于外部结果集中的每一行:

  • ia桌子进行全面扫描
  • inb桌子进行全面扫描
  • ripcord桌子进行全面扫描
  • YRTR桌子进行全面扫描
  • students桌子进行全面扫描
  • potstudents桌子进行全面扫描

在某些时候,行数会增加到 19。GROUP BY实现为SORT节点 id 为 4 的 。DISTINCT实现为Sort (Distinct Sort)节点 id 为 0 的 。

作为程序员,您无法直接控制查询计划,但您可以做很多事情来影响它。你说查询执行时间太慢了。上述计划对您来说是否有效?如果您可以选择查询优化器的操作,您会怎么做?您正在执行大约 140 次表扫描,只是为了返回 18 行。对于极少数行,这可能没问题,但听起来您在生产中拥有更多数据。

优化查询的一种方法是减少该查询的 IO 要求。在这里,您将进行大量表扫描,因此实施起来应该很简单。创建索引,以便查询优化器可以更有效地获取相关数据。即使您正在引用视图,您也可以在视图使用的表上创建索引以提高性能。举个例子,这里是表访问的谓词fc

[Test].[dbo].[roster].[Teacher] as [rost].[Teacher]=[Test].[dbo].[fc].[Teacher]
Run Code Online (Sandbox Code Playgroud)

如果在该Teacher列上创建索引fc可能会导致对该表的表访问方法不同,并且可能更有效。或者,您可以将教师表使用的所有列添加为INCLUDE列。

仅查看您的代码,您应该了解在查询中使用局部变量的影响。创建查询计划时,查询优化器将不知道这些局部变量的值。它将根据硬编码规则进行默认基数估计。对于某些查询,添加OPTION (RECOMPILE)提示或用硬编码值替换局部变量可以产生更好的查询计划,因为查询优化器在创建计划之前就知道变量的值。

此外,不要随意添加DISTINCT和添加GROUP BY到您的查询中。看起来查询优化器优化了其中的一些,但大多数时候,如果您同时拥有这两个查询GROUP BY并且DISTINCT在同一个查询中您做错了。目前尚不清楚进行该更改会对这个查询产生多大的性能影响。