Mar*_*lli 4 performance sql-server optimization execution-plan sort-operator query-performance
在处理以下查询以回答此问题时:
有以下表格:
CREATE TABLE [dbo].[#foo] (
[creation] DATETIME NOT NULL,
[value] MONEY NULL,
[DT] AS (CONVERT([date],[CREATION])) PERSISTED)
-- add a clustered index on the dt column
CREATE CLUSTERED INDEX CI_FOO ON #FOO(DT)
GO
Run Code Online (Sandbox Code Playgroud)
和另一个加入表:
create table #bar (dt date primary key clustered)
go
Run Code Online (Sandbox Code Playgroud)
但是在运行以下查询时:
WITH RADHE AS (
SELECT THE_ROW=ROW_NUMBER() OVER(PARTITION BY B.DT ORDER BY B.DT),
THE_DATE=B.dt,
THE_NUMBER_OF_RECORDS_ON_THIS_DAY=CASE WHEN F.DT IS NULL THEN 0 ELSE COUNT(*) OVER (PARTITION BY F.DT ) END ,
THE_TOTAL_VALUE_FOR_THE_DAY=COALESCE(SUM(F.VALUE) OVER (PARTITION BY b.DT ),0)
FROM #BAR B
LEFT OUTER JOIN #FOO F
ON B.dt = F.dt
)
--get rid of the duplicates and present the result
SELECT
THE_DATE,
THE_NUMBER_OF_RECORDS_ON_THIS_DAY,
THE_TOTAL_VALUE_FOR_THE_DAY
FROM RADHE
WHERE THE_ROW = 1
Run Code Online (Sandbox Code Playgroud)
我得到了类似下面这张图片的东西,这正是我要找的。
但是生成的执行计划有几个排序和嵌套循环操作,如下图所示。
这是一个非常简单的操作,两个表之间的左外连接,索引已经排序,因此我想知道是否可以简化查询计划。
或者,我可以更改查询代码。
为什么我们在查询计划中需要nested loops
2 次和sort
2 次?
您有一个提供排序依据的索引,B.DT
但是
THE_ROW
使用此顺序进行评估F.DT
进行评估THE_NUMBER_OF_RECORDS_ON_THIS_DAY
B.DT
为了使THE_TOTAL_VALUE_FOR_THE_DAY
。您可以通过简单地更改 CTE 中列的顺序来摆脱其中一种排序,使F.DT
最后一个出现(此不必要排序的连接项在此处)
WITH RADHE AS (
SELECT THE_ROW=ROW_NUMBER() OVER(PARTITION BY B.DT ORDER BY B.DT),
THE_DATE=B.dt ,
THE_TOTAL_VALUE_FOR_THE_DAY=COALESCE(SUM(F.VALUE) OVER (PARTITION BY b.DT ),0),
THE_NUMBER_OF_RECORDS_ON_THIS_DAY=CASE WHEN F.DT IS NULL THEN 0 ELSE COUNT(*) OVER (PARTITION BY F.DT ) END
FROM #BAR B
LEFT OUTER JOIN #FOO F
ON B.dt = F.dt
)
Run Code Online (Sandbox Code Playgroud)
但是您可以通过将定义更改THE_NUMBER_OF_RECORDS_ON_THIS_DAY
为
CASE WHEN F.DT IS NULL THEN 0 ELSE COUNT(*) OVER (PARTITION BY B.DT ) END
Run Code Online (Sandbox Code Playgroud)
因此它使用与其余函数相同的分区定义。
这不应该改变您的示例中的任何内容,因为您的CASE
表达式0
无论如何只会分配给任何不匹配的行。
至于计划的其余部分,请参阅分区和公共子表达式假脱机
(事后计划,没有排序)
我认为你让这件事变得比它需要的要困难得多
SELECT THE_DATE = B.dt,
THE_NUMBER_OF_RECORDS_ON_THIS_DAY = COUNT(F.DT),
THE_TOTAL_VALUE_FOR_THE_DAY = SUM(ISNULL(F.VALUE, 0))
FROM #BAR B
LEFT OUTER JOIN #FOO F
ON B.dt = F.dt
GROUP BY B.dt
Run Code Online (Sandbox Code Playgroud)