2014-09-25 70 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