2014-09-25 10 views
5

Utilizzo di MSSQL Server 2008 Enterprise Edition e molto probabilmente altre versioni di MSSQL, ecco una dimostrazione di concetto che crea una tabella temporanea e materializza NEWID() in modo diverso a seconda se si è utilizzato JOIN o LEFT JOIN, anche se stiamo abbinando due file esattamente.Perché l'unione sinistra fa sì che NEWID() si materializzi prima di unirsi?

Se si guarda il piano di esecuzione, è possibile vedere che lo scalare di calcolo per ottenere NEWID() viene eseguito per ultimo utilizzando JOIN, ma non quando si utilizza LEFT JOIN. Mi sarei aspettato il comportamento LEFT JOIN. Questa stranezza è dovuta all'ingenuità nel piano di esecuzione o c'è qualcosa di più in corso?

Dimostrazione Con Tabella Temp:

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 

Con Join, NewParentGuid è diverso per entrambe le righe.

Con Join a sinistra, NewParentGuid è la stessa.

EDIT2: se si aggiunge questo al join di sinistra, i risultati cambiano.

where temp2.ParentGuid = temp2.ParentGuid 

O come altro utente indicate, ove tale colonna non è nullo. Rimarranno gli stessi quando si effettuano confronti su altre colonne o dove 1 = 1. colonna di Schroedinger?

Consulta anche:

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

+0

Questo è così strano, mi sarei aspettato il LEFT JOIN risultato in entrambi i casi poiché hai solo un id genitore. D'altra parte non è necessario alcun codice se si vogliono newguid separati. Perché no: seleziona *, NEWID() come NewParentGuid da #Temp – HLGEM

+0

Sono stato sorpreso di vedere anche il comportamento JOIN, mentre mi aspettavo che si comportasse come il LEFT JOIN, quindi ho aggiornato il mio post per menzionarlo. – John

+0

Uno è un join hash e l'altro un join di tipo merge ma se si impone che entrambi siano uguali, i risultati sono gli stessi. – Paparazzi

risposta

0

non so il motivo per cui si materializzò alla fine della query per aderire al interno, ma il comportamento per la sinistra join giardini.L modifiche se si mette una clausola in cui su di esso in questo modo (efficacemente cambiando in un join interno)

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 

Potrebbe farlo per qualsiasi clausola where. Sembra che se si desidera che il GUID funzioni come previsto nella condizione di join di sinistra sopra, è più sicuro eseguire la tabella derivata al di fuori della selezione e inserire i risultati in una tabella temporanea. In seguito, maintence non cambierà accidentalmente il modo in cui funziona.

1

Non proprio una risposta, ma una constatazione

Ciò restituisce duplicare

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 

questo li costringe ad esser diverso

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 
Problemi correlati