2014-09-25 73 views
5

使用MSSQL Server 2008企业版,以及最有可能的其他版本的MSSQL,这里是一个概念证明,根据您使用JOIN或LEFT JOIN,使临时表和NEWID()具体化(即使我们匹配两排正好。为什么left join会导致NEWID()比join更早实现?

如果查看执行计划,可以看到使用JOIN最后执行的计算标量为NEWID(),但使用LEFT JOIN时不执行。我会期待LEFT JOIN的行为。这是由于执行计划中的天真或者有更多事情发生吗?

示范用临时表:

Create Table #Temp 
(
    ChildGuid uniqueidentifier, 
    ParentGuid uniqueidentifier 
) 
insert into #Temp (ChildGuid, ParentGuid) Values('5E3211E8-D382-4775-8F96-041BF419E70F', '96031FA0-829F-43A1-B5A6-108362A37701') 
insert into #Temp (ChildGuid, ParentGuid) Values('FFFFFFFF-D382-4775-8F96-041BF419E70F', '96031FA0-829F-43A1-B5A6-108362A37701') 

--Use a join. Get different NewIDs. 
select * from #Temp 
join 
(
    select ParentGuid, NewParentGuid from(
    select ParentGuid, NEWID() as NewParentGuid from #Temp 
    group by ParentGuid 
    ) tb2 
) temp2 on #Temp.ParentGuid = temp2.ParentGuid 

--Do exactly as above, but use a left join. Get a pair of the same NewIDs. 
select * from #Temp 
left join 
(
    select ParentGuid, NewParentGuid from(
    select ParentGuid, NEWID() as NewParentGuid from #Temp 
    group by ParentGuid 
    ) tb2 
) temp2 on #Temp.ParentGuid = temp2.ParentGuid 

随着加入,NewParentGuid是两行不同。

与左连接,NewParentGuid是相同的。

编辑2:如果将此附加到左连接,结果会更改。

where temp2.ParentGuid = temp2.ParentGuid 

或者正如另一位用户指出的那样,该列不为空。他们在进行其他栏目的比较时或在1 = 1的情况下保持不变.Schroedinger的专栏?

参见:

Why does newid() materialize at the very end of a query?

+0

之所以如此,是奇怪的,我本来期望的左连接导致这两种情况,因为你只有一个父母身份证。另一方面,如果你想单独使用newguids,则不需要任何代码。为什么不能:从#Temp – HLGEM 2014-09-25 18:07:12

+0

中选择*,NEWID()作为NewParentGuid我也很惊讶地看到JOIN行为,而我期望它的行为像左连接,所以我更新了我的帖子以提及这一点。 – John 2014-09-25 18:41:41

+0

一个是散列连接,另一个是合并连接,但如果强制它们都相同,结果是相同的。 – Paparazzi 2014-09-25 20:27:40

回答

0

我不知道为什么它物化在查询内部联接结束,但左边的行为加入ASLO的变化,如果你把一个where子句就像这样(有效地将其更改为内部连接)

select * from #Temp 
left join 
(
    select ParentGuid, NewParentGuid from(
    select ParentGuid, NEWID() as NewParentGuid from #Temp 
    group by ParentGuid 
    ) tb2 
) temp2 on #Temp.ParentGuid = temp2.ParentGuid 
where temp2.ParentGuid is not null 

对任何where子句可能会这样做。嗯,看起来如果你希望GUID在上面的左连接条件下如预期的那样工作,那么完全在select之外完成派生表并将结果放到临时表中是最安全的。然后,后来的维护不会意外地改变它的工作方式。

1

不是一个真正的答案,但观察

这个返回重复

select * from #Temp 
inner hash join 
(
    select ParentGuid, NEWID() as NewParentGuid 
    from #Temp 
    group by ParentGuid 
    union 
    select null, NEWID()  
) temp2 
    on #Temp.ParentGuid = temp2.ParentGuid 

--Do exactly as above, but use a left join. Get a pair of the same NewIDs. 
select * from #Temp 
left hash join 
(
    select ParentGuid, NEWID() as NewParentGuid 
    from #Temp 
    group by ParentGuid 
) temp2 
    on #Temp.ParentGuid = temp2.ParentGuid 

这迫使他们两个是不同的

select * from #Temp join (
    select ParentGuid, NEWID() as NewParentGuid 
    from #Temp 
    group by ParentGuid ) temp2 
    on #Temp.ParentGuid = temp2.ParentGuid 

--Do exactly as above, but use a left join. Get a pair of the same NewIDs. select * from #Temp left join (
    select ParentGuid, NEWID() as NewParentGuid 
    from #Temp 
    group by ParentGuid) temp2 
    on #Temp.ParentGuid = temp2.ParentGuid 
    and temp2.ParentGuid is not null