2012-06-21 55 views
2

这是SQL Server 2008中我有这两个表和联接:Sql Server的意外笛卡尔乘积

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秒钟,而我的个人资料显示沿途有笛卡儿积(2,254,960行)。有没有办法重构此查询以防止完整的交叉连接?

* EDITED * 添加主键之后,所建议的,集SHOWPLAN_TEXT在给我这个:

|--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][email protected][EmployeeId] as [ecd].[EmployeeId] AND @ET.[WorkDate] as [et].[WorkDate][email protected][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][email protected][ServiceCallId] as [et].[ServiceCallId]) ORDERED FORWARD) 

我的意思是什么“显示沿途笛卡尔积”来自设定统计个人资料。它在这里显示的太多了,但是对于计划中的最后一项(Clustered Index Scan),它在行下显示2,254,960(我的逗号),在执行下显示5680。我误解说我有笛卡尔产品吗?

+1

为每个表添加一个主键? –

+0

我在第一个表中添加了'PRIMARY KEY(EmployeeId,WorkDate)',并且在第二个表中添加了'EmployeeTimeId UNIQUEIDENTIFIER PRIMARY KEY'。我的执行时间降到了亚秒,这太棒了(谢谢!),但笛卡儿临时产品仍然存在!有点让它很难担心,呵? –

+1

你尝试过使用查询提示吗?在查询结尾添加“Option(HASH JOIN,MERGE JOIN)”。 –

回答

1

我得到了三个很好的答案,但他们都是作为评论来发表的,所以我在这里发布'''答案。

亚伦建议我为每个表var添加一个主键。我加

EmployeeTimeId UNIQUEIDENTIFIER PRIMARY KEY 

到@ET和

PRIMARY KEY (EmployeeId, WorkDate) 

,因为@EmployeeCrossDay,在这两种情况下,雇员不是唯一的。

尽管EmpmloyeeTimeId并未参与连接,但这仅仅将我的查询从3秒减少到1以下。但是,我的统计信息显示执行计划中的其中一个步骤运行了5680次,并且超过了2.2次百万行(5,680 * 397)。尽管答复时间可以接受,但我对此很好奇。

Martin然后建议我的@ET密钥需要EmployeeId领先。所以我换成

PRIMARY KEY(EmployeeId, EmployeeTimeId) 

关键在这一点上,执行计划显示,前交叉联接减少到命中率只有397行(而不是超过200万),即使它仍然在做每一个过程@ ET行(5680),它现在正在执行聚簇索引搜索而不是全表扫描。

一路上,戈登建议增加

OPTION(HASH JOIN, MERGE JOIN) 

我打消了我所有以前应用指标和生成的计划没有执行步不止一次。

所有三个建议都在一秒钟内返回相同的数据,所以我给每一个点(现在,我的感谢)。