2014-10-30 13 views
6

Ho una tabella Clienti e una tabella ordini. Non tutti i clienti hanno effettuato un ordine, quindi non tutti gli ID cliente sono nella tabella degli ordini. Voglio che il mio risultato mostri SOLO i valori della tabella clienti che non hanno effettuato un ordine, quindi la colonna orderID deve essere visualizzata come null. Il seguente codice funziona:Perché questa istruzione funziona solo con un WHERE?

SELECT c.CustomerID, c.CustomerName, o.OrderID 
    FROM Customers c 
    LEFT OUTER JOIN Orders o 
ON c.CustomerID = o.CustomerID WHERE o.OrderID IS NULL 

Ma quella che ho provato in origine non lo fa:

SELECT c.CustomerID, c.CustomerName, o.OrderID 
    FROM Customers c 
    LEFT OUTER JOIN Orders o 
ON c.CustomerID = o.CustomerID AND o.OrderID IS NULL 

Questo mostra invece tutti i valori sul tavolo clienti, ma tutti hanno nulla su per il loro orderID

Non credo di capire davvero la differenza, poiché ritengo che sia ragionevole per entrambi restituire la stessa cosa.

+0

Capisco intuitivamente perché lo pensereste, ma penso che la ragione sia che 'ON' è usato per unire le tabelle su una determinata condizione. Quello che stai cercando di fare è cercare le righe DOVE una condizione è vera. Non so se questo è vicino alla verità, ma è come sono arrivato a pensarci e sapere quando usare cosa. – AdamMc331

+0

l'AND o.OrderID è quello che sta rovinando tutto. Funzionerebbe correttamente finché non colpisce la condizione AND .... – rvphx

risposta

2

Questa è la natura di un join esterno (in questo caso un join sinistro). Un join di sinistra prende la tabella principale (Clienti), la confronta con i criteri nella tabella unita (Ordini). Per ogni riga in Clienti che non ha una corrispondenza, a differenza di un join interno, non rimuove la riga. Invece, aggiunge tutti i campi dagli ordini ma inserisce nulla in essi.

Guardate questo esempio:

 
Table A   Table B 
┌──────┬──────┐ ┌──────┬──────┐ 
│field1│field2│ │field3│field4│ 
├──────┼──────┤ ├──────┼──────┤ 
│A  │1  │ │1  │One │ 
│B  │2  │ │3  │Three │ 
│C  │3  │ └──────┴──────┘ 
└──────┴──────┘ 

dei tavoli join interno (tra il campo2 e field3) è:

 
┌──────┬──────┬──────┬──────┐ 
│field1│field2│field3│field4│ 
├──────┼──────┤──────┼──────┤ 
│A  │1  │1  │One │ 
│C  │3  │3  │Three │ 
└──────┴──────┴──────┴──────┘ 

Ma le tabelle outer join deve dare ogni record, e se non vi è alcuna corrispondenza, inserire i null.

 
┌──────┬──────┬──────┬──────┐ 
│field1│field2│field3│field4│ 
├──────┼──────┤──────┼──────┤ 
│A  │1  │1  │One │ 
│B  │2  │NULL │NULL │⬅︎ No match 
│C  │3  │3  │Three │ 
└──────┴──────┴──────┴──────┘ 

Ora cosa succederebbe se non ci fossero corrispondenze in tabella2? Ad esempio, se hai aggiunto una condizione impossibile nella clausola ON? Poi tutti i record nel risultato sarà simile al "Nessuna corrispondenza"

 
┌──────┬──────┬──────┬──────┐ 
│field1│field2│field3│field4│ 
├──────┼──────┤──────┼──────┤ 
│A  │1  │NULL │NULL │⬅︎ No match (because of impossible condition) 
│B  │2  │NULL │NULL │⬅︎ No match (because of impossible condition) 
│C  │3  │NULL │NULL │⬅︎ No match (because of impossible condition) 
└──────┴──────┴──────┴──────┘ 

Quindi non importa se non ci fosse partita, perché non vi era alcuna traccia nel Table2 con il dato ID, o se non ci fosse corrisponde perché hai aggiunto una condizione impossibile. Il risultato di un join esterno è che i campi che dovevano provenire da Table2 sarebbero stati sostituiti con valori null. Perché è così che viene definito un join esterno.


Ora ai tavoli del mondo reale:

in realtà non hanno alcun record di ordini la cui IDOrdine è nullo (a meno che non avete progettato molto male). Quindi, se si inserisce questa condizione nella clausola ON, non troverà record che soddisfano i criteri.

In tal caso, poiché si tratta di un join esterno (a sinistra), si ottengono tutti i record dei clienti originali e, poiché non vi sono corrispondenze, ognuno di essi ha i campi da Ordini tutti nulli.

Nel caso in cui si inserisse la condizione nello WHERE, si stava effettivamente sfruttando questo comportamento di un join sinistro. Hai abbinato ogni cliente con il suo ordine. Se c'era una corrispondenza - bene, hai l'ID ordine reale. Ma nei casi in cui non c'era corrispondenza - quelli che stai cercando - aggiunge un ID ordine nullo.

La clausola where indica quindi di fornire solo i record in cui è successo. Cioè, i record che non avevano un ordine corrispondente in Ordini.

+0

Ok, ora capisco perché l'AND non funzionerebbe come voglio. Guarderebbe solo la tabella degli ordini e guarderebbe la colonna ordersID e scoprirà che nessuno è nullo. Ma non capisco perché li imposta come null @RealSkeptic – FrostyStraw

+0

@FrostyStraw Il valore NULL in OrderID significa che nessun record corrisponde ai criteri 'ON c.CustomerID = o.CustomerID AND o.OrderID IS NULL'. –

+0

@FrostyStraw Ho aggiunto alcune spiegazioni sulla natura dei join esterni alla mia risposta, che spero ti aiutino a capire il motivo. – RealSkeptic

1

Semplicemente non è come è stato progettato SQL. Da http://dev.mysql.com/doc/refman/5.0/en/join.html

La conditional_expr utilizzato con ON è qualsiasi espressione condizionale della forma che può essere utilizzato in una clausola WHERE. In generale, è necessario utilizzare la clausola ON per le condizioni che specificano come unire le tabelle e la clausola WHERE per limitare le righe desiderate nel set di risultati.

1

Il primo è l'unione POST delle tabelle. Il secondo fa parte del join.

Così da mettere in inglese.

Primo:

Get CustomerID, Name, and OrderID from 
Customers Link to Orders where CustomerID matches 
Show where link failed 

Secondo:

Get CustomerID, Name, and OrderID from 
Customers Link to Orders where CustomerID matches and Order ID is null 

Tutti i link fallire nel secondo, senza lasciare nulla per filtrare i risultati.

3

Utilizzando LEFT OUTER JOIN si ottiene tutti i record dal tavolo Customers. Quindi la tua condizione AND o.OrderID IS NULL filtra solo i record dalla tabella Orders ma non i record da Customers. Ma quello che ti serve è filtrare la tabella Customers e non funziona a causa del tipo di JOIN.

Nel frattempo, la condizione WHERE viene applicata all'intero recordset indipendentemente dal tipo di JOIN. Prova a sostituire LEFT OUTER JOIN con INNER JOIN e ottieni gli stessi risultati per entrambi i SELECT.

Nel secondo SELECT si ottengono valori NULL per o.OrderID perché è stato specificato NULL nelle condizioni AND o.OrderID IS NULL. Nessun record di questo tipo esiste nella tabella Orders e quindi il valore NULL indica che nessun record corrisponde ai criteri ON c.CustomerID = o.CustomerID AND o.OrderID IS NULL.

1

Per la seconda query la condizione di join è falsa per ogni riga. Se la tabella Ordine ha un CustomerId allora avrà anche un OrderId. Dopo che ogni riga è stata esclusa, la parte "sinistra" del join sta riportando tutte le righe escluse dalla tabella sinistra (Cliente). In questo caso, riporta tutti loro.

Per la prima query è solo falso sulle righe che si desidera, quindi il join di sinistra ripristina solo quelle righe. E poi la condizione where può eliminare le righe che non vuoi.

1

Crea una tabella ## clienti (Id nvarchar (5)) Crea una tabella ## (int order_no, Id nvarchar (5)) Order

Inserire in ## valori del cliente ('A'), (' B '), (' C ')

inserire in ## valori di ordine (1, 'A'), (2, 'B'), (3, 'B')

Select c. * da ## customer c left join ## Order o su c.Id = o.id dove o.Order_No è null

Seleziona ci d, o.order_no da ## clienti c left join ## Order o su c.Id = o.id e o.Order_No è nullo

Select c.id, o.order_no da ## clienti c lEFT jOIN ## Order o su c.Id = o.id e o.Order_No non è nullo

provare voi stessi vedrete la differenza, in cui la clausola sta dando un filtro

1

Un LIVELLO ESTERNO SINISTRO conserva le righe non corrispondenti dalla tabella sinistra (prima), unendole con una riga NULL nella forma della tabella destra (seconda). Facciamo un esempio concreto. Say, tabella clienti ha {c1,"n1"}, {c2,"n2"} e ordini ha {c1, o1} 1 ° caso: join esterno sinistro si tradurrà:

{c1,"n1", o1}, {c2,"n2", null} 

Ora, si sa la differenza di dove e "E". Dove trova dove è stato ordinato l'ID null e ID ordine è nullo

6

È importante verificare che le due query siano molto diverse da. La prima domanda è:

SELECT c.CustomerID, c.CustomerName, o.OrderID 
FROM Customers c LEFT OUTER JOIN 
    Orders o 
    ON c.CustomerID = o.CustomerID 
WHERE o.OrderID IS NULL; 

Esso restituisce tutti i clienti che non hanno ordini corrispondenti (o o.OrderId è nullo). Lo fa perché il left outer join mantiene tutte le righe nella prima tabella. Se non c'è corrispondenza, tutte le colonne della seconda tabella sono NULL e la clausola where sceglierebbe queste colonne.

La seconda query:

SELECT c.CustomerID, c.CustomerName, o.OrderID 
FROM Customers c LEFT OUTER JOIN 
    Orders o 
    ON c.CustomerID = o.CustomerID AND 
     o.OrderID IS NULL; 

trova tutte le righe di tutti i clienti e ottiene anche informazioni ordine dove OrderId è nullo, se esistono tali registrazioni. Non c'è alcun filtro di Customers, perché lo left outer join garantisce che tutte le righe della prima tabella si trovino nel set di risultati.

Sarei sorpreso se un campo chiamato OrderId assuma mai i valori NULL. Ma ogni query è SQL valida e ognuno fa qualcosa di utile. Solo uno di loro, tuttavia, fa ciò che intendi.

Problemi correlati