2013-05-25 12 views
6

ho ottenuto un database PostgreSQL con 4 tavoli:tabelle Unire se il riferimento esiste

Tabella A

--------------------------- 
| ID | B_ID | C_ID | D_ID | 
--------------------------- 
| 1 | 1 | NULL | NULL | 
--------------------------- 
| 2 | NULL | 1 | NULL | 
--------------------------- 
| 3 | 2 | 2 | 1 | 
--------------------------- 
| 4 | NULL | NULL | 2 | 
--------------------------- 

Tabella B

------------- 
| ID | DATA | 
------------- 
| 1 | 123 | 
------------- 
| 2 | 456 | 
------------- 

Tabella C

------------- 
| ID | DATA | 
------------- 
| 1 | 789 | 
------------- 
| 2 | 102 | 
------------- 

Tabella D

------------- 
| ID | DATA | 
------------- 
| 1 | 654 | 
------------- 
| 2 | 321 | 
------------- 

Sto cercando di recuperare un set di risultati che ha aderito i dati dalla tabella B ed i dati dalla tabella C, solo se uno degli ID di cabina non è nullo.

SELECT "Table_A"."ID", "Table_A"."ID_B", "Table_A"."ID_C", "Table_A"."ID_D", "Table_B"."DATA", "Table_C"."DATA" 
    FROM "Table_A" 
     LEFT JOIN "Table_B" on "Table_A"."ID_B" = "Table_B"."ID" 
     LEFT JOIN "Table_C" on "Table_A"."ID_C" = "Table_C"."ID" 
    WHERE "Table_A"."ID_B" IS NOT NULL OR "Table_A"."ID_C" IS NOT NULL; 

È questo consigliato o dovrei meglio dividere questo in più query?

C'è un modo per fare un join interno tra queste tabelle?

Il risultato mi aspetto è:

------------------------------------------------- 
| ID | ID_B | ID_C | ID_D | DATA (B) | DATA (C) | 
------------------------------------------------- 
| 1 | 1 | NULL | NULL | 123 | NULL | 
------------------------------------------------- 
| 2 | NULL | 1 | NULL | NULL | 789 | 
------------------------------------------------- 
| 3 | 2 | 2 | NULL | 456 | 102 | 
------------------------------------------------- 

EDIT:ID_B, ID_C, ID_D sono chiavi esterne alle tabelle table_b, table_c, table_d

+0

Ho modificato la mia risposta e aggiunto il risultato che mi aspetto. – wiizzard

+1

Non hai mostrato i vincoli FK. Gli FK table_a.id_b e table_a.id_c alle tabelle B e C? (sembra che lo siano, ma questo non è menzionato nella domanda) – wildplasser

+0

Hai ragione. Sono FK. – wiizzard

risposta

5

il WHERE "Table_A"."ID_B" IS NOT NULL OR "Table_A"."ID_C" IS NOT NULL; può essere sostituito con la clausola corrispondente sul Tabelle B e C: WHERE "Table_B"."ID" IS NOT NULL OR "Table_C"."ID" IS NOT NULL;. Ciò funzionerebbe anche se table_a.id_b e table_a.id_c non fossero FK delle tabelle B e C. Altrimenti, una riga table_a con {5, 5,5,5} recupera due righe NULL dalle tabelle B e C.

SELECT ta."ID" AS a_id 
     , ta."ID_B" AS b_id 
     , ta."ID_C" AS c_id 
     , ta."ID_D" AS d_id 
     , tb."DATA" AS bdata 
     , tc."DATA" AS cdata 
FROM "Table_a" ta 
LEFT JOIN "Table_B" tb on ta."ID_B" = tb."ID" 
LEFT JOIN "Table_C" tc on ta."ID_C" = tc."ID" 
WHERE tb."ID" IS NOT NULL OR tc."ID" IS NOT NULL 
     ; 
+0

Meglio :-). A parte la tua richiesta di essere una soluzione migliore in caso di mancati vincoli di chiave esterna, hai qualche benchmark delle prestazioni per le due varianti? Tendo a pensare che con gli indici messi sulla chiave esterna, la query originale sarebbe più veloce (e più leggibile), a meno che l'ottimizzatore non possa effettuare la trasformazione necessaria ... –

+0

Sì, differisce dalla query dell'OQ, tranne che nel caso dove ra.id_b e ta.id_c hanno vincoli FK alle tabelle B e C. Vedi il mio commento sull'OQ. Altrimenti: la semantica sarà diversa, i piani saranno diversi, le prestazioni saranno diverse. (nei casi semplici/piccoli, verranno utilizzati due hash-join e la differenza sarà molto piccola) – wildplasser

2

Dato le vostre esigenze, la query sembra bene me.

Un'alternativa sarebbe quella di utilizzare selezioni nidificate nella proiezione, ma a seconda dei dati, degli indici e dei vincoli, che potrebbero essere più lenti, poiché i selettori nidificati di solito generano cicli nidificati, mentre i join possono essere eseguiti come join di unione o nidificati loop:

SELECT 
    "Table_A"."ID", 
    "Table_A"."ID_B", 
    "Table_A"."ID_C", 
    "Table_A"."ID_D", 
    (SELECT "DATA" FROM "Table_B" WHERE "Table_A"."ID_B" = "Table_B"."ID"), 
    (SELECT "DATA" FROM "Table_C" WHERE "Table_A"."ID_C" = "Table_C"."ID") 
FROM "Table_A" 
WHERE "Table_A"."ID_B" IS NOT NULL OR "Table_A"."ID_C" IS NOT NULL; 

Se Postgres fa scalar subquery caching (come fa Oracle), seleziona quindi annidati potrebbe aiutare nel caso in cui si dispone di un sacco di ripetizioni dati in Table_A

0

Generalmente spealking il metodo consigliato è quello di farlo in una query solo, e lasciare che il database di fare il lavoro il più possibile, soprattutto se si aggiungono altre operazioni come l'ordinamento (ORDER BY) o impaginazione successiva (limite ... offset ...) più tardi. Abbiamo effettuato alcune misurazioni e non è possibile ordinare/impaginare più rapidamente in Java/Scala, se si utilizzano raccolte di livello superiore come elenchi ecc.

RDBMS si comportano molto bene con istruzioni a singolo complesso, ma hanno difficoltà nel gestire molte piccole domande.Ad esempio, se si interrogano "uno" e "molti parenti" in una query, sarà più veloce di quanto avviene in 1 + n istruzioni select.

Per quanto riguarda l'unione esterna, abbiamo eseguito misurazioni e non vi è alcuna penalità di prestazioni effettiva rispetto ai join interni. Quindi se il tuo modello dati e/o la tua query richiedono un join esterno, fallo e basta. Se si trattava di un problema di prestazioni, è possibile sintonizzarlo in un secondo momento.

Per quanto riguarda i confronti nulli, è indicare che il modello di dati potrebbe essere ottimizzato, ma è solo un'ipotesi. È probabile che tu possa migliorare la progettazione in modo tale che null non sia consentito in queste colonne.

+0

Riguardante l'ultimo paragrafo: è normale e perfettamente normale avere valori nulli in tali colonne. Non vedo come ciò indicherebbe un problema. –

+0

@Erwin Questo è ciò che intendevo descrivere. Dal momento che l'inglese non è la mia lingua madre, cosa posso migliorare per renderlo chiaro? – Beryllium

+0

Il tuo inglese sembra a posto. Vorrei eliminare l'ultimo paragrafo. –

2

Poiché sono presenti vincoli di chiave esterna, l'integrità referenziale è garantita e la query nella Q è già la migliore risposta.

Sono indicati anche gli indici su Table_B.ID e Table_C.ID.

If casi corrispondenti nel Table_A sono rare (meno del 5% circa, a seconda fila con e distribuzione dei dati) un partial multi-column index aiuterebbe prestazioni:

CREATE INDEX table_a_special_idx ON "Table_A" ("ID_B", "ID_C") 
WHERE "ID_B" IS NOT NULL OR "ID_C" IS NOT NULL; 

In PostgreSQL 9.2 un indice di copertura (index-only scan in gergo Postgres) potrebbe aiutare ancora di più - nel qual caso dovresti includere tutte le colonne di interesse nell'indice (non nel mio esempio). Dipende da diversi fattori come la larghezza delle righe e la frequenza degli aggiornamenti nella tabella.