Introduzione
La suddivisione dei dati correlati in tabelle separate può essere vantaggiosa dal punto di vista della coerenza, della flessibilità e di alcuni tipi di prestazioni. Tuttavia, è comunque necessario un modo ragionevole per reintegrare i record quando le informazioni pertinenti si estendono su più tabelle.
Nei database relazionali, join offrono un modo per combinare i record in due o più tabelle in base a valori di campo comuni. Diversi tipi di join possono ottenere risultati diversi a seconda di come devono essere gestite le righe non corrispondenti. In questa guida, discuteremo i vari tipi di join offerti da PostgreSQL e come puoi usarli per combinare i dati delle tabelle da più origini.
Cosa sono i join?
In breve, si unisce sono un modo per visualizzare i dati da più tabelle. Lo fanno unendo insieme record provenienti da fonti diverse in base ai valori corrispondenti in determinate colonne. Ciascuna riga risultante è costituita da un record della prima tabella combinato con una riga della seconda tabella, in base a una o più colonne di ciascuna tabella aventi lo stesso valore.
La sintassi di base di un join è simile alla seguente:
SELECT *FROM <first_table><join_type> <second_table> <join_condition>;
In un join, ogni riga risultante viene costruita includendo tutte le colonne della prima tabella seguite da tutte le colonne della seconda tabella. Il SELECT
parte della query può essere utilizzata per specificare le colonne esatte che desideri visualizzare.
È possibile creare più righe dalle tabelle originali se i valori nelle colonne utilizzate per il confronto non sono univoci. Ad esempio, immagina di avere una colonna da confrontare dalla prima tabella che ha due record con un valore di "rosso". Abbinata a questa è una colonna della seconda tabella che ha tre righe con quel valore. Il join produrrà sei righe diverse per quel valore che rappresentano le varie combinazioni che possono essere ottenute.
Il tipo di join e le condizioni di join determinano la modalità di costruzione di ciascuna riga visualizzata. Ciò influisce su ciò che accade alle righe di ogni tabella che fanno e non avere una corrispondenza nella condizione di unione.
Per comodità, molti join corrispondono alla chiave primaria su una tabella con una chiave esterna associata sulla seconda tabella. Sebbene le chiavi primarie ed esterne siano utilizzate dal sistema di database solo per mantenere le garanzie di coerenza, la loro relazione spesso le rende un buon candidato per le condizioni di unione.
Diversi tipi di join
Sono disponibili vari tipi di join, ognuno dei quali produrrà potenzialmente risultati diversi. Capire come è costruito ogni tipo ti aiuterà a determinare quale è appropriato per diversi scenari.
Inner join
Il join predefinito è chiamato inner join . In PostgreSQL, questo può essere specificato usando INNER JOIN
o semplicemente JOIN
.
Ecco un tipico esempio che dimostra la sintassi di un inner join:
SELECT *FROM table_1[INNER] JOIN table_2 ON table_1.id = table_2.table_1_id;
Un inner join è il tipo di join più restrittivo perché visualizza solo le righe create combinando le righe di ciascuna tabella. Tutte le righe nelle tabelle costituenti che non avevano una controparte corrispondente nell'altra tabella vengono rimosse dai risultati. Ad esempio, se la prima tabella ha un valore "blu" nella colonna di confronto e la seconda tabella non ha record con quel valore, quella riga verrà eliminata dall'output.
Se rappresenti i risultati come diagramma di Venn delle tabelle dei componenti, un inner join ti consente di rappresentare l'area di sovrapposizione dei due cerchi. Non viene visualizzato nessuno dei valori che esistevano solo in una delle tabelle.
Unisci a sinistra
Un left join è un join che mostra tutti i record trovati in un inner join, più tutti i non corrispondenti righe della prima tabella. In PostgreSQL, questo può essere specificato come LEFT OUTER JOIN
o semplicemente come LEFT JOIN
.
La sintassi di base di un join sinistro segue questo schema:
SELECT *FROM table_1LEFT JOIN table_2 ON table_1.id = table_2.table_1_id;
Un left join viene costruito eseguendo prima un inner join per costruire righe da tutti i record corrispondenti in entrambe le tabelle. Successivamente, vengono inclusi anche i record non corrispondenti della prima tabella. Poiché ogni riga in un join include le colonne di entrambe le tabelle, le colonne non corrispondenti utilizzano NULL
come valore per tutte le colonne della seconda tabella.
Se rappresenti i risultati come diagramma di Venn delle tabelle dei componenti, un join sinistro ti consente di rappresentare l'intero cerchio sinistro. Le parti del cerchio sinistro rappresentate dall'intersezione tra i due cerchi avranno dati aggiuntivi integrati dalla tabella di destra.
Unisciti a destra
Un join destro è un join che mostra tutti i record trovati in un inner join, più tutti i non corrispondenti righe della seconda tabella. In PostgreSQL, questo può essere specificato come RIGHT OUTER JOIN
o semplicemente come RIGHT JOIN
.
La sintassi di base di un join destro segue questo schema:
SELECT *FROM table_1RIGHT JOIN table_2 ON table_1.id = table_2.table_1_id;
Un join destro viene costruito eseguendo prima un inner join per costruire righe da tutti i record corrispondenti in entrambe le tabelle. Successivamente, vengono inclusi anche i record non corrispondenti della seconda tabella. Poiché ogni riga in un join include le colonne di entrambe le tabelle, le colonne non corrispondenti utilizzano NULL
come valore per tutte le colonne della prima tabella.
Se rappresenti i risultati come un diagramma di Venn delle tabelle dei componenti, un join destro ti consente di rappresentare l'intero cerchio destro. Le parti del cerchio di destra rappresentate dall'intersezione tra i due cerchi avranno dati aggiuntivi integrati dalla tabella di sinistra.
Partecipazione completa
Un full join è un join che mostra tutti i record trovati in un inner join, più tutti i non corrispondenti righe da entrambe le tabelle dei componenti. In PostgreSQL, questo può essere specificato come FULL OUTER JOIN
o semplicemente come FULL JOIN
.
La sintassi di base di un join completo segue questo schema:
SELECT *FROM table_1FULL JOIN table_2 ON table_1.id = table_2.table_1_id;
Un full join viene costruito eseguendo prima un inner join per costruire righe da tutti i record corrispondenti in entrambe le tabelle. Successivamente, vengono inclusi anche i record non corrispondenti di entrambe le tabelle. Poiché ogni riga in un join include le colonne di entrambe le tabelle, le colonne non corrispondenti utilizzano NULL
come valore per tutte le colonne nell'altra tabella senza corrispondenza.
Se rappresenti i risultati come diagramma di Venn delle tabelle dei componenti, un full join ti consente di rappresentare interamente entrambi i cerchi dei componenti. L'intersezione dei due cerchi avrà valori forniti da ciascuna delle tabelle componenti. Le parti dei cerchi al di fuori dell'area di sovrapposizione avranno i valori della tabella a cui appartengono, utilizzando NULL
per compilare le colonne che si trovano nell'altra tabella.
Cross join
Un join speciale chiamato CROSS JOIN
è anche disponibile. Un cross join non utilizza alcun confronto per determinare se le righe in ogni tabella corrispondono. Invece, i risultati vengono costruiti semplicemente aggiungendo ciascuna delle righe della prima tabella a ciascuna delle righe della seconda tabella.
Questo produce un prodotto cartesiano delle righe in due o più tabelle. In effetti, questo stile di join combina le righe di ogni tabella incondizionatamente. Quindi, se ogni tabella ha tre righe, la tabella risultante avrà nove righe contenenti tutte le colonne di entrambe le tabelle.
Ad esempio, se hai una tabella chiamata t1
combinato con una tabella chiamata t2
, ciascuno con righe r1
, r2
e r3
, il risultato sarebbe nove righe combinate in questo modo:
t1.r1 + t2.r1t1.r1 + t2.r2t1.r1 + t2.r3t1.r2 + t2.r1t1.r2 + t2.r2t1.r2 + t2.r3t1.r3 + t2.r1t1.r3 + t2.r2t1.r3 + t2.r3
Partecipazione automatica
Un self join è qualsiasi join che combina le righe di una tabella con se stesso. Potrebbe non essere immediatamente chiaro come questo possa essere utile, ma in realtà ha molte applicazioni comuni.
Spesso le tabelle descrivono entità che possono svolgere più ruoli in relazione tra loro. Ad esempio, se hai una tabella di people
, ogni riga potrebbe potenzialmente contenere una mother
colonna che fa riferimento ad altre people
sul tavolo. Un'unione automatica ti consentirebbe di unire queste diverse righe unendo una seconda istanza della tabella alla prima in cui questi valori corrispondono.
Poiché i join automatici fanno riferimento due volte alla stessa tabella, sono necessari alias di tabella per disambiguare i riferimenti. Nell'esempio sopra, ad esempio, potresti unire le due istanze di people
tabella usando gli alias people AS children
e people AS mothers
. In questo modo, puoi specificare a quale istanza della tabella ti riferisci quando definisci le condizioni di unione.
Ecco un altro esempio, questa volta che rappresenta le relazioni tra dipendenti e manager:
SELECT *FROM people AS employeeJOIN people AS manager ON employee.manager_id = manager.id;
Condizioni di unione
Quando si combinano le tabelle, la condizione di unione determina in che modo le righe verranno abbinate insieme per formare i risultati compositi. La premessa di base è definire le colonne in ogni tabella che devono corrispondere affinché il join avvenga su quella riga.
Il ON
clausola
Il modo più standard per definire le condizioni per i join di tabelle è con ON
clausola. Il ON
La clausola utilizza un segno di uguale per specificare le colonne esatte di ciascuna tabella che verranno confrontate per determinare quando potrebbe verificarsi un join. PostgreSQL utilizza le colonne fornite per unire le righe di ciascuna tabella.
Il ON
La clausola è la più dettagliata, ma anche la più flessibile delle condizioni di join disponibili. Consente la specificità indipendentemente dalla standardizzazione dei nomi delle colonne di ciascuna tabella combinata.
La sintassi di base di ON
la clausola si presenta così:
SELECT *FROM table1JOIN table2ON table1.id = table2.ident;
Qui, le righe da table1
e table2
sarà unito ogni volta che il id
colonna da table1
corrisponde a ident
colonna da table2
. Poiché viene utilizzato un inner join, i risultati mostreranno solo le righe che sono state unite. Poiché la query utilizza il carattere jolly *
carattere, verranno visualizzate tutte le colonne di entrambe le tabelle.
Ciò significa che entrambi gli id
colonna da table1
e l'ident
colonna da table2
verranno visualizzati, anche se hanno lo stesso valore esatto in virtù del soddisfacimento della condizione di unione. Puoi evitare questa duplicazione richiamando le colonne esatte che desideri visualizzare nel SELECT
elenco di colonne.
Il USING
clausola
Il USING
clausola è una scorciatoia per specificare le condizioni di un ON
clausola che può essere utilizzata quando le colonne da confrontare hanno lo stesso nome in entrambe le tabelle. Il USING
La clausola accetta un elenco, racchiuso tra parentesi, dei nomi delle colonne condivise che devono essere confrontati.
La sintassi generale di USING
clausola utilizza questo formato:
SELECT *FROM table1JOIN table2USING (id, state);
Questo join combina table1
con table2
quando due colonne condivise da entrambe le tabelle (id
e state
) ognuno ha valori corrispondenti.
Questo stesso join potrebbe essere espresso in modo più dettagliato usando ON
così:
SELECT *FROM table1JOIN table2ON table1.id = table2.id AND table1.state = table2.state;
Sebbene entrambi i join precedenti comporteranno la costruzione delle stesse righe con gli stessi dati presenti, verrebbero visualizzati in modo leggermente diverso. Mentre il ON
La clausola include tutte le colonne di entrambe le tabelle, USING
La clausola elimina le colonne duplicate. Quindi, invece di esserci due id
separati colonne e due state
separati colonne (una per ogni tabella), i risultati avrebbero solo una di ciascuna delle colonne condivise, seguita da tutte le altre colonne fornite da table1
e table2
.
Il NATURAL
clausola
Il NATURAL
clausola è ancora un'altra scorciatoia che può ridurre ulteriormente la verbosità di USING
clausola. Un NATURAL
join non specifica nessuno colonne da abbinare. Invece, PostgreSQL unirà automaticamente le tabelle in base a tutte le colonne che hanno colonne corrispondenti in ogni database.
La sintassi generale del NATURAL
la clausola di unione è simile a questa:
SELECT *FROM table1NATURAL JOIN table2;
Supponendo che table1
e table2
entrambi hanno colonne denominate id
, state
e company
, la query precedente sarebbe equivalente a questa query utilizzando ON
clausola:
SELECT *FROM table1JOIN table2ON table1.id = table2.id AND table1.state = table2.state AND table1.company = table2.company;
E questa query usando USING
clausola:
SELECT *FROM table1JOIN table2USING (id, state, company);
Come il USING
clausola, il NATURAL
La clausola sopprime le colonne duplicate, quindi ci sarebbe solo una singola istanza di ciascuna delle colonne unite nei risultati.
Mentre il NATURAL
la clausola può ridurre la verbosità delle tue domande, è necessario prestare attenzione quando la si utilizza. Poiché le colonne utilizzate per l'unione delle tabelle vengono calcolate automaticamente, se le colonne nelle tabelle dei componenti cambiano, i risultati possono essere molto diversi a causa delle nuove condizioni di unione.
Condizioni di unione e WHERE
clausola
Le condizioni di unione condividono molte caratteristiche con i confronti utilizzati per filtrare le righe di dati utilizzando WHERE
clausole. Entrambi i costrutti definiscono espressioni che devono restituire true affinché la riga venga considerata. Per questo motivo, non è sempre intuitivo quale sia la differenza tra l'inclusione di confronti aggiuntivi in un WHERE
costruire invece di definirli all'interno della clausola join stessa.
Per comprendere le differenze che ne risulteranno, dobbiamo dare un'occhiata all'ordine in cui PostgreSQL elabora le diverse porzioni di una query. In questo caso, i predicati nella condizione di join vengono elaborati per primi per costruire la tabella unita virtuale in memoria. Dopo questa fase, le espressioni all'interno di WHERE
vengono valutate per filtrare le righe risultanti.
Ad esempio, supponiamo di avere due tabelle chiamate customer
e order
che dobbiamo unirci. Vogliamo unire le due tabelle facendo corrispondere il customer.id
colonna con order.customer_id
colonna. Inoltre, siamo interessati alle righe nell'order
tabella che ha un product_id
di 12345.
Dati i requisiti di cui sopra, abbiamo due condizioni a cui teniamo. Il modo in cui esprimiamo queste condizioni, tuttavia, determinerà i risultati che riceveremo.
Per prima cosa, usiamo entrambi come condizioni di unione per un LEFT JOIN
:
SELECT customer.id AS customer_id, customer.name, order.id AS order_id, order.product_idFROM customerLEFT JOIN orderON customer.id = order.customer_id AND order.product_id = 12345;
I risultati potrebbero potenzialmente assomigliare a questo:
customer_id | name | order_id | product_id ------------+----------+----------+------------ 4380 | Acme Co | 480 | 12345 4380 | Acme Co | 182 | 12345 320 | Other Co | 680 | 12345 4380 | Acme Co | | 320 | Other Co | | 20 | Early Co | | 8033 | Big Co | |(7 rows)
PostgreSQL è arrivato a questo risultato eseguendo le seguenti operazioni:
- Combina qualsiasi riga nel
customer
tabella con l'order
tabella dove:customer.id
corrisponde aorder.customer_id
.order.product_id
corrisponde a 12345
- Poiché stiamo utilizzando un join sinistro, includi qualsiasi non corrispondente righe dalla tabella di sinistra (
customer
), riempiendo le colonne della tabella di destra (order
) conNULL
valori. - Visualizza solo le colonne elencate in
SELECT
specifica della colonna.
Il risultato è che tutte le nostre righe unite soddisfano entrambe le condizioni che stiamo cercando. Tuttavia, il join sinistro fa sì che PostgreSQL includa anche tutte le righe della prima tabella che non soddisfano la condizione di join. Ciò si traduce in righe "rimaste" che non sembrano seguire l'intento apparente della query.
Se spostiamo la seconda query (order.product_id
=12345) a un WHERE
clausola, invece di includerla come condizione di join, otteniamo risultati diversi:
SELECT customer.id AS customer_id, customer.name, order.id AS order_id, order.product_idFROM customerLEFT JOIN orderON customer.id = order.customer_idWHERE order.product_id = 12345;
Questa volta vengono visualizzate solo tre righe:
customer_id | name | order_id | product_id ------------+----------+----------+------------ 4380 | Acme Co | 480 | 12345 4380 | Acme Co | 182 | 12345 320 | Other Co | 680 | 12345(3 rows)
L'ordine in cui vengono eseguiti i confronti è la ragione di queste differenze. Questa volta, PostgreSQL elabora la query in questo modo:
- Combina qualsiasi riga nel
customer
tabella con l'order
tabella dovecustomer.id
corrisponde aorder.customer_id
. - Poiché stiamo utilizzando un join sinistro, includi qualsiasi non corrispondente righe dalla tabella di sinistra (
customer
), riempiendo le colonne della tabella di destra (order
) conNULL
valori. - Valuta il
WHERE
clausola per rimuovere tutte le righe che non hanno 12345 come valore perorder.product_id
colonna. - Visualizza solo le colonne elencate in
SELECT
specifica della colonna.
Questa volta, anche se stiamo usando un join sinistro, il WHERE
La clausola tronca i risultati filtrando tutte le righe senza il corretto product_id
. Perché tutte le righe non corrispondenti avrebbero product_id
impostato su NULL
, questo rimuove tutte le righe non corrispondenti che sono state popolate dal join sinistro. Rimuove anche tutte le righe che sono state soddisfatte dalla condizione di unione che non hanno superato questo secondo round di controlli.
Comprendere il processo di base utilizzato da PostgreSQL per eseguire le query può aiutarti a evitare alcuni errori facili da commettere ma difficili da eseguire il debug mentre lavori con i tuoi dati.
Conclusione
In questa guida, abbiamo spiegato come i join consentono ai database relazionali di combinare i dati di tabelle diverse per fornire risposte più preziose. Abbiamo parlato dei vari join supportati da PostgreSQL, del modo in cui ogni tipo assembla i suoi risultati e di cosa aspettarsi quando si utilizzano tipi specifici di join. Successivamente, abbiamo esaminato diversi modi per definire le condizioni di unione e abbiamo esaminato l'interazione tra i join e il WHERE
clausola può portare a sorprese.
I join sono una parte essenziale di ciò che rende i database relazionali sufficientemente potenti e flessibili da gestire così tanti tipi diversi di query. L'organizzazione dei dati utilizzando i confini logici, pur essendo in grado di ricombinare i dati in modi nuovi, caso per caso, offre ai database relazionali come PostgreSQL un'incredibile versatilità. Imparare come eseguire questa cucitura tra le tabelle ti consentirà di creare query più complesse e di fare affidamento sul database per creare immagini complete dei tuoi dati.