Sql server 2008 Sql Server意外笛卡尔积

Sql server 2008 Sql Server意外笛卡尔积,sql-server-2008,Sql Server 2008,这是SQL Server 2008。我有两个表和一个连接: DECLARE @EmployeeCrossDay TABLE ( EmployeeId UNIQUEIDENTIFIER, WorkDate DATE, OtherStuff... ) DECLARE @ET TABLE ( EmployeeId UNIQUEIDENTIFIER, WorkDate DATE, DifferentOtherStuff... ) SELECT

这是SQL Server 2008。我有两个表和一个连接:

DECLARE @EmployeeCrossDay TABLE
(
    EmployeeId UNIQUEIDENTIFIER, 
    WorkDate DATE, OtherStuff...
)

DECLARE @ET TABLE
(
    EmployeeId      UNIQUEIDENTIFIER,
    WorkDate        DATE, DifferentOtherStuff...
)

SELECT *
FROM @EmployeeCrossDay ecd
LEFT JOIN @ET et ON et.EmployeeId = ecd.EmployeeId
    AND et.WorkDate = ecd.WorkDate 
第一个表有5680行(一个范围内的每个日期对应一名员工),第二个表有397行(员工实际工作的每一天对应一行或多行)。(因此,EmployeeId/WorkDate在第一个表中是唯一的组合,但在第二个表中不是。)我的查询结果是正确的(每个员工的列表中有一行或多行表示他工作的天数,一行表示他没有工作的天数),但这大约需要3秒钟,我的个人资料显示了一个笛卡尔积(2254960行)。有没有办法重新构造此查询以防止完全交叉连接

*已编辑* 按照建议添加主键后,将Showplan_Text设置为On可提供以下信息:

  |--Compute Scalar(DEFINE:([Expr1007]=isnull(@ET.[StartTime] as [et].[StartTime],[Expr1010]), [Expr1008]=isnull(@ET.[EndTime] as [et].[EndTime],[Expr1010])))
       |--Nested Loops(Left Outer Join, OUTER REFERENCES:([et].[ServiceCallId]))
            |--Compute Scalar(DEFINE:([Expr1006]=isnull(@ET.[TypeId] as [et].[TypeId],(8)), [Expr1009]=isnull(@ET.[Interrupt] as [et].[Interrupt],($0.0000))))
            |    |--Nested Loops(Left Outer Join, WHERE:(@ET.[EmployeeId] as [et].[EmployeeId]=@EmployeeCrossDay.[EmployeeId] as [ecd].[EmployeeId] AND @ET.[WorkDate] as [et].[WorkDate]=@EmployeeCrossDay.[WorkDate] as [ecd].[WorkDate]))
            |         |--Compute Scalar(DEFINE:([Expr1010]=CONVERT_IMPLICIT(datetime,@EmployeeCrossDay.[WorkDate] as [ecd].[WorkDate],0)))
            |         |    |--Sort(ORDER BY:([ecd].[Number] ASC, [ecd].[WorkDate] ASC))
            |         |         |--Clustered Index Scan(OBJECT:(@EmployeeCrossDay AS [ecd]))
            |         |--Clustered Index Scan(OBJECT:(@ET AS [et]))
            |--Clustered Index Seek(OBJECT:([Snapper].[dbo].[ServiceCalls].[PK_Jobs] AS [sc]), SEEK:([sc].[ServiceCallId]=@ET.[ServiceCallId] as [et].[ServiceCallId]) ORDERED FORWARD)

我所说的“沿途显示笛卡尔积”的意思来自于设置统计配置文件。它显示的内容太多,无法粘贴到此处,但对于计划中的下一个最后一项(聚集索引扫描),它在行下显示2254960(我的逗号),在执行下显示5680。我是不是误读了,说我有笛卡尔积?

我得到了三个很好的答案,但都是评论,所以我在这里发布了“答案”

Aaron建议我为我添加的每个表变量添加一个主键

EmployeeTimeId  UNIQUEIDENTIFIER PRIMARY KEY
到@ET和

PRIMARY KEY (EmployeeId, WorkDate)
因为在这两种情况下,EmployeeId都不是唯一的

即使empmloyetimeid没有参与联接,但仅此一项就可以将我的查询从3秒以上减少到1秒以下。然而,我的统计资料显示,执行计划中的一个步骤运行了5680次,达到了220多万行(5680*397)。尽管响应时间可以接受,但我对此感到好奇

马丁接着建议,我的@ET钥匙需要由EmployeeId引导。所以我把钥匙换成了

PRIMARY KEY( EmployeeId, EmployeeTimeId )
此时,执行计划显示前一个交叉连接减少到仅命中397行(而不是超过200万行),尽管它仍在为每个@ET行(5680)执行该过程,但它现在正在执行聚集索引查找,而不是完整表扫描

一路上,戈登建议加上

OPTION( HASH JOIN, MERGE JOIN )
我删除了所有以前应用的索引,结果计划中的任何步骤都不会执行多次


这三个建议在不到一秒钟的时间内都返回了相同的数据,所以我给每个建议打分(现在,我要表示感谢)。

为每个表添加一个主键?我将
主键(EmployeeId,WorkDate)
添加到第一个表,并将
EmployeeTimeId UNIQUEIDENTIFIER主键添加到第二个表。我的执行时间降到了次秒,这很好(谢谢!),但是中间的笛卡尔积仍然存在!有点让人担心,嗯?你有没有试着使用查询提示?在查询末尾添加“Option(HASH-JOIN,MERGE-JOIN)”。不确定短语“my profile沿途显示笛卡尔积”是什么意思。你能发布实际的执行计划吗?@KellyCline-在
@ET
上没有索引可以用来搜索
EmployeeId
,因此它正在进行5680次全表扫描。它可能选择嵌套循环计划,因为它假设对表变量的操作只会发出一行。您可以添加一个
选项(重新编译)
提示,使其考虑实际基数。此外,您还可以使用前导列
EmployeeId
向表变量添加约束,以提供有用的索引。