2009-02-03 18 views
11

Sto provando a scrivere una query per trovare record che non hanno un record corrispondente in un'altra tabella.Ricerca record senza corrispondenza con SQL

Per esempio, ho un due tabelle le cui strutture simile a questa:

 
Table1 
    State | Product | Distributor | other fields 
    CA | P1  | A   | xxxx 
    OR | P1  | A   | xxxx 
    OR | P1  | B   | xxxx 
    OR | P1  | X   | xxxx 
    WA | P1  | X   | xxxx 
    VA | P2  | A   | xxxx 

Table2 
    State | Product | Version | other fields 
    CA | P1  | 1.0 | xxxx 
    OR | P1  | 1.5 | xxxx 
    WA | P1  | 1.0 | xxxx 
    VA | P2  | 1.2 | xxxx 

(. Stato/prodotto/Distributore insieme formano la chiave per Table1 Stato/prodotto è la chiave per Table2)

Voglio trovare tutte le combinazioni Stato/Prodotto/Versione che non utilizzano il distributore X. (Quindi il risultato in questo esempio è CA-P1-1.0 e VA-P2-1.2.)

Qualsiasi suggerimento su una domanda per farlo?

+0

dal secondo all'ultima frase, dovrebbe table2 anche essere coinvolti in questa ricerca? (tranne forse per ottenere la versione del prodotto). – Tundey

+0

Penso che tu abbia risposto alla tua stessa domanda. La tabella 2 è necessaria per ottenere la versione. –

risposta

24
SELECT 
    * 
FROM 
    Table2 T2 
WHERE 
    NOT EXISTS (SELECT * 
     FROM 
      Table1 T1 
     WHERE 
      T1.State = T2.State AND 
      T1.Product = T2.Product AND 
      T1.Distributor = 'X') 

Questo deve essere conforme ANSI.

+0

Funziona sulla maggior parte dei sistemi SQL; ECCEZIONALE non funzionerà ovunque (anche se è più elegante dove funziona). –

+0

L'utilizzo di "SELECT 1 FROM" nella subquery (invece di "SELECT * FROM") può impedire una scansione della tabella non necessaria. Anche se mi aspetto che il DBMS sia abbastanza intelligente da capirlo da solo, dopo aver visto "ESISTE". – Tomalak

+1

Ho questo con il mio collega MVP di SQL Server tutto il tempo :-) Il * è espanso al momento della compilazione, bt collassa banalmente, ma non il runtime dice. Una volta mi ha mostrato un articolo. Ho visto Itzak Ben-Gan qualche tempo fa e ha detto che * è più veloce. La scelta è tua ... – gbn

1

selezionare * da table1 dove lo stato non in (selezionare lo stato da table1 dove distributore = 'X')

Probabilmente non è la più intelligente, ma che dovrebbe funzionare.

+0

IN non è buono come EXISTS e non gestisce il tasto composito su stato/prodotto – gbn

+1

Inoltre, a seconda di RDBMS, 'NOT IN' non si comporta come ci si aspetterebbe se' state' è nullable. –

8

in T-SQL:

SELECT DISTINCT Table2.State, Table2.Product, Table2.Version 
FROM Table2 
    LEFT JOIN Table1 ON Table1.State = Table2.State AND Table1.Product = Table2.Product AND Table1.Distributor = 'X' 
WHERE Table1.Distributor IS NULL 

Nessun sottointerrogazioni richiesti.

Modifica: come indicano i commenti, il DISTINCT non è necessario. Grazie!

+0

Non userei distinti, ma altrimenti è quello che vuoi. – HLGEM

+0

La distinzione probabilmente renderà la query meno efficiente, ma mai più efficiente. Dipende dai conteggi delle righe della tabella relativa. La distcnt forza anche un aggregato di cui non ha bisogno la subquery. – gbn

+1

Anche le prestazioni e il comportamento di questa query dipendono pesantemente dal fatto che 'Distributore' sia coperto dall'indice utilizzato nel join (se non è possibile richiedere una ricerca o un'operazione di indice può essere convertita in un'operazione di indice cluster) e 'Distributore' è Nullable (nel qual caso può restituire righe corrispondenti a cui manca un valore, oltre alle righe non corrispondenti). –

1
SELECT DISTINCT t2.State, t2.Product, t2.Version 
FROM table2 t2 
JOIN table1 t1 ON t1.State = t2.State AND t1.Product = t2.Product 
       AND t1.Distributor <> 'X' 
1

In Oracle:

SELECT t2.State, t2.Product, t2.Version 
FROM Table2 t2, Table t1 
WHERE t1.State(+) = t2.State 
    AND t1.Product(+) = t2.Product 
    AND t1.Distributor(+) = :distributor 
    AND t1.State IS NULL 
Problemi correlati