【发布时间】:2022-01-09 08:49:15
【问题描述】:
我正在查看其中两个语句的执行计划,我有点困惑为什么 LEFT JOIN 语句比 CROSS JOIN 语句执行得更好:
表定义:
CREATE TABLE [Employee] (
[ID] int NOT NULL IDENTITY(1,1),
[FirstName] varchar(40) NOT NULL,
CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED ([ID] ASC)
);
CREATE TABLE [dbo].[Numbers] (
[N] INT IDENTITY (1, 1) NOT NULL,
CONSTRAINT [PK_Numbers] PRIMARY KEY CLUSTERED ([N] ASC)
); --The Numbers table contains numbers 0 to 100,000.
我为每位员工加入“一天”的问题查询:
DECLARE @PeriodStart AS date = '2019-11-05';
DECLARE @PeriodEnd AS date = '2019-11-05';
SELECT E.FirstName, CD.ClockDate
FROM Employee E
CROSS JOIN (SELECT DATEADD(day, N.N, @PeriodStart) AS ClockDate
FROM Numbers N
WHERE N.N <= DATEDIFF(day, @PeriodStart, @PeriodEnd)
) CD
WHERE E.ID > 2000;
SELECT E.FirstName, CD.ClockDate
FROM Employee E
LEFT JOIN (SELECT DATEADD(day, N.N, @PeriodStart) AS ClockDate
FROM Numbers N
WHERE N.N <= DATEDIFF(day, @PeriodStart, @PeriodEnd)
) CD ON CD.ClockDate = CD.ClockDate
WHERE E.ID > 2000;
执行计划: https://www.brentozar.com/pastetheplan/?id=B139JjPKK
如您所见,根据优化器的说法,带有看似冗余谓词的第二个(左连接)查询的成本似乎比第一个(交叉连接)查询要低。当周期日期跨越多天时也是如此。
奇怪的是,如果我将 LEFT JOIN 的谓词更改为不同的东西,例如 1 = 1,它将像 CROSS APPLY 一样执行。我还尝试将 LEFT JOIN 的 SELECT 部分更改为 SELECT N 并加入 CD.N = CD.N ...但这似乎也表现不佳。
根据执行计划,第二个查询有一个索引查找,它只从 Numbers 表中读取 3000 行,而第一个查询是读取的 10 倍。第二个查询的索引搜索也有这个谓词(我假设它来自 LEFT JOIN):
dateadd(day,[Numbers].[N] as [N].[N],[@PeriodStart])=dateadd(day,[Numbers].[N] as [N].[N],[@PeriodStart])
我想了解为什么第二个查询似乎执行得这么好,即使我不会除此之外?这与我加入 DATEADD 函数的结果有关吗? SQL 是否在加入前评估 DATEADD 的结果?
【问题讨论】:
-
CD.ClockDate 是否为空?
-
您的主要问题似乎是表假脱机:您应该通过仅选择所需的行数来优化它
CROSS JOIN (SELECT TOP (DATEDIFF(day, @PeriodStart, @PeriodEnd)) DATEADD(day, N.N, @PeriodStart) AS ClockDate FROM Numbers N ORDER BY N.N)。请将这两个查询计划上传到brentozar.com/pastetheplan,以便我们了解实际情况。 -
@CaiusJard 实际上,
DATEADD返回一个可为空的,参见小提琴dbfiddle.uk/…。正如您正确猜测的那样,这可能导致行估计值降低 -
作为“查询成本(相对于批次)”给出的百分比是估计值,而不是实际值。在您的第一个查询中,它预计要处理 633,000 行,而在第二个查询中,它预计只有 63,000 行。因此,它估计底部的工作要少得多,即使它可能是相同的。要正确测试,请尝试
SET STATISTICS TIME, IO ON;THEN 运行查询并查看扫描次数、读取次数等进行比较。查看查询计划,我希望它们实际上是非常相似的工作量。 -
@Charlieface 这是计划:brentozar.com/pastetheplan/?id=B139JjPKK 我也使用您的优化运行它,它似乎比我的两个查询都执行得更好。谢谢!
标签: sql-server sql-server-2008 join