Introduktion
At opdele relaterede data i separate tabeller kan være fordelagtigt ud fra et synspunkt om sammenhæng, fleksibilitet og visse typer ydeevne. Du har dog stadig brug for en rimelig måde at genintegrere poster på, når de relevante oplysninger strækker sig over flere tabeller.
I relationsdatabaser joins tilbyde en måde at kombinere posterne i to eller flere tabeller baseret på fælles feltværdier. Forskellige typer sammenføjninger kan opnå forskellige resultater afhængigt af, hvordan umatchede rækker skal håndteres. I denne guide vil vi diskutere de forskellige typer joins, som PostgreSQL tilbyder, og hvordan du kan bruge dem til at kombinere tabeldata fra flere kilder.
Hvad er joinforbindelser?
Kort sagt, tilslutter sig er en måde at vise data fra flere tabeller på. Det gør de ved at sammenføje poster fra forskellige kilder baseret på matchende værdier i bestemte kolonner. Hver resulterende række består af en post fra den første tabel kombineret med en række fra den anden tabel, baseret på en eller flere kolonner i hver tabel med samme værdi.
Den grundlæggende syntaks for en join ser således ud:
SELECT *FROM <first_table><join_type> <second_table> <join_condition>;
I en joinforbindelse er hver resulterende række konstrueret ved at inkludere alle kolonnerne i den første tabel efterfulgt af alle kolonnerne fra den anden tabel. SELECT
del af forespørgslen kan bruges til at angive de nøjagtige kolonner, du ønsker at vise.
Flere rækker kan konstrueres ud fra de originale tabeller, hvis værdierne i kolonnerne, der bruges til sammenligning, ikke er unikke. Forestil dig for eksempel, at du har en kolonne, der sammenlignes fra den første tabel, som har to poster med værdien "rød". Matchet med dette er en kolonne fra den anden tabel, der har tre rækker med den værdi. Sammenføjningen vil producere seks forskellige rækker for den værdi, der repræsenterer de forskellige kombinationer, der kan opnås.
Sammenkædningstypen og sammenkædningsbetingelserne bestemmer, hvordan hver række, der vises, er opbygget. Dette påvirker, hvad der sker med rækkerne fra hver tabel, der gør og ikke gør har en match på join-betingelsen.
For nemheds skyld matcher mange joins den primære nøgle på den ene tabel med en tilknyttet fremmednøgle på den anden tabel. Selvom primære og fremmede nøgler kun bruges af databasesystemet til at opretholde konsistensgarantier, gør deres forhold dem ofte til en god kandidat for tilslutningsbetingelser.
Forskellige typer joins
Forskellige typer sammenføjninger er tilgængelige, som hver især vil give forskellige resultater. At forstå, hvordan hver type er konstrueret, hjælper dig med at afgøre, hvilken der er passende til forskellige scenarier.
Indre joinforbindelse
Standardsammenføjningen kaldes en indre joinforbindelse . I PostgreSQL kan dette angives med enten INNER JOIN
eller bare JOIN
.
Her er et typisk eksempel, der viser syntaksen for en indre joinforbindelse:
SELECT *FROM table_1[INNER] JOIN table_2 ON table_1.id = table_2.table_1_id;
En indre joinforbindelse er den mest restriktive type joinforbindelse, fordi den kun viser rækker oprettet ved at kombinere rækker fra hver tabel. Alle rækker i de konstituerende tabeller, der ikke havde en matchende modstykke i den anden tabel, fjernes fra resultaterne. Hvis den første tabel f.eks. har en værdi på "blå" i sammenligningskolonnen, og den anden tabel ikke har nogen registrering med den værdi, vil denne række blive undertrykt fra outputtet.
Hvis du repræsenterer resultaterne som et Venn-diagram af komponenttabellerne, giver en indre sammenføjning dig mulighed for at repræsentere det overlappende område af de to cirkler. Ingen af de værdier, der kun fandtes i en af tabellerne, vises.
Venstre joinforbindelse
En venstre joinforbindelse er en joinforbindelse, der viser alle de poster, der findes i en indre joinforbindelse, plus alle de umatchede rækker fra den første tabel. I PostgreSQL kan dette angives som en LEFT OUTER JOIN
eller som bare en LEFT JOIN
.
Den grundlæggende syntaks for en venstre join følger dette mønster:
SELECT *FROM table_1LEFT JOIN table_2 ON table_1.id = table_2.table_1_id;
En venstre join konstrueres ved først at udføre en indre join for at konstruere rækker fra alle de matchende poster i begge tabeller. Bagefter er de uovertrufne poster fra det første bord også inkluderet. Da hver række i en joinforbindelse inkluderer kolonnerne i begge tabeller, bruger de umatchede kolonner NULL
som værdien for alle kolonnerne i den anden tabel.
Hvis du repræsenterer resultaterne som et Venn-diagram af komponenttabellerne, giver en venstre sammenføjning dig mulighed for at repræsentere hele den venstre cirkel. De dele af den venstre cirkel repræsenteret af skæringspunktet mellem de to cirkler vil have yderligere data suppleret med den højre tabel.
Højre joinforbindelse
En højre joinforbindelse er en joinforbindelse, der viser alle de poster, der findes i en indre joinforbindelse, plus alle de umatchede rækker fra den anden tabel. I PostgreSQL kan dette angives som en RIGHT OUTER JOIN
eller som bare en RIGHT JOIN
.
Den grundlæggende syntaks for en højre join følger dette mønster:
SELECT *FROM table_1RIGHT JOIN table_2 ON table_1.id = table_2.table_1_id;
En højre join konstrueres ved først at udføre en indre join for at konstruere rækker fra alle de matchende poster i begge tabeller. Bagefter er de umatchede poster fra den anden tabel også inkluderet. Da hver række i en joinforbindelse inkluderer kolonnerne i begge tabeller, bruger de umatchede kolonner NULL
som værdien for alle kolonnerne i den første tabel.
Hvis du repræsenterer resultaterne som et Venn-diagram af komponenttabellerne, giver en højresammenføjning dig mulighed for at repræsentere hele den højre cirkel. De dele af den højre cirkel repræsenteret af skæringspunktet mellem de to cirkler vil have yderligere data suppleret med den venstre tabel.
Fuld deltagelse
En fuld joinforbindelse er en joinforbindelse, der viser alle de poster, der findes i en indre joinforbindelse, plus alle de umatchede rækker fra begge komponenttabeller. I PostgreSQL kan dette angives som en FULL OUTER JOIN
eller som bare en FULL JOIN
.
Den grundlæggende syntaks for en fuld join følger dette mønster:
SELECT *FROM table_1FULL JOIN table_2 ON table_1.id = table_2.table_1_id;
En fuld join konstrueres ved først at udføre en indre join for at konstruere rækker fra alle de matchende poster i begge tabeller. Bagefter er de uovertrufne poster fra begge tabeller også inkluderet. Da hver række i en joinforbindelse inkluderer kolonnerne i begge tabeller, bruger de umatchede kolonner NULL
som værdien for alle kolonnerne i den umatchede anden tabel.
Hvis du repræsenterer resultaterne som et Venn-diagram over komponenttabellerne, giver en fuld sammenføjning dig mulighed for at repræsentere begge komponentcirklerne fuldstændigt. Skæringspunktet mellem de to cirkler vil have værdier leveret af hver af komponenttabellerne. Delene af cirklerne uden for det overlappende område vil have værdierne fra den tabel, de tilhører, ved hjælp af NULL
for at udfylde kolonnerne i den anden tabel.
Cross join
En speciel join kaldet en CROSS JOIN
er også tilgængelig. En krydssammenføjning bruger ingen sammenligninger til at bestemme, om rækkerne i hver tabel matcher hinanden. I stedet konstrueres resultater ved blot at tilføje hver af rækkerne fra den første tabel til hver af rækkerne i den anden tabel.
Dette giver et kartesisk produkt af rækkerne i to eller flere tabeller. I realiteten kombinerer denne sammenføjningsstil rækker fra hver tabel ubetinget. Så hvis hver tabel har tre rækker, ville den resulterende tabel have ni rækker indeholdende alle kolonnerne fra begge tabeller.
For eksempel, hvis du har en tabel kaldet t1
kombineret med en tabel kaldet t2
, hver med rækker r1
, r2
og r3
, ville resultatet være ni rækker kombineret således:
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
Tilmeld dig selv
En selvsammenføjning er enhver forbindelse, der kombinerer rækkerne i en tabel med sig selv. Det er måske ikke umiddelbart tydeligt, hvordan dette kunne være nyttigt, men det har faktisk mange almindelige applikationer.
Ofte beskriver tabeller enheder, der kan udfylde flere roller i forhold til hinanden. For eksempel, hvis du har en tabel med people
, kan hver række potentielt indeholde en mother
kolonne, der refererer til andre people
i bordet. En selvsammenføjning ville give dig mulighed for at sy disse forskellige rækker sammen ved at forbinde en anden forekomst af tabellen med den første, hvor disse værdier matcher.
Da self joins refererer til den samme tabel to gange, kræves tabelaliaser for at gøre referencerne uentydige. I eksemplet ovenfor kan du f.eks. forbinde de to forekomster af people
tabel ved hjælp af aliasserne people AS children
og people AS mothers
. På den måde kan du angive, hvilken forekomst af tabellen, du henviser til, når du definerer joinbetingelser.
Her er endnu et eksempel, som denne gang repræsenterer forholdet mellem medarbejdere og ledere:
SELECT *FROM people AS employeeJOIN people AS manager ON employee.manager_id = manager.id;
Forbindelsesbetingelser
Når du kombinerer tabeller, bestemmer sammenføjningsbetingelsen, hvordan rækker matches sammen for at danne de sammensatte resultater. Den grundlæggende forudsætning er at definere de kolonner i hver tabel, der skal matche, for at sammenføjningen kan forekomme i den pågældende række.
ON
klausul
Den mest standard måde at definere betingelserne for tabelsammenføjninger på er med ON
klausul. ON
klausul bruger et lighedstegn til at angive de nøjagtige kolonner fra hver tabel, der vil blive sammenlignet for at bestemme, hvornår en joinforbindelse kan forekomme. PostgreSQL bruger de medfølgende kolonner til at sy rækkerne fra hver tabel sammen.
ON
klausul er den mest udførlige, men også den mest fleksible af de tilgængelige joinbetingelser. Det giver mulighed for specificitet, uanset hvor standardiserede kolonnenavnene er for hver tabel, der kombineres.
Den grundlæggende syntaks for ON
klausul ser sådan ud:
SELECT *FROM table1JOIN table2ON table1.id = table2.ident;
Her er rækkerne fra table1
og table2
vil blive tilsluttet hver gang id
kolonne fra table1
matcher ident
kolonne fra table2
. Fordi der bruges en indre sammenføjning, vil resultaterne kun vise de rækker, der blev sammenføjet. Da forespørgslen bruger jokertegnet *
tegn, vil alle kolonnerne fra begge tabeller blive vist.
Det betyder, at både id
kolonne fra table1
og ident
kolonne fra table2
vil blive vist, selvom de har den samme nøjagtige værdi i kraft af at de opfylder join-betingelsen. Du kan undgå denne duplikering ved at kalde de nøjagtige kolonner, du ønsker at vise i SELECT
kolonneliste.
USING
klausul
USING
klausul er en forkortelse for at specificere betingelserne for en ON
klausul, der kan bruges, når de kolonner, der sammenlignes, har samme navn i begge tabeller. USING
klausul tager en liste, omsluttet i parentes, over de delte kolonnenavne, der skal sammenlignes.
Den generelle syntaks for USING
klausul bruger dette format:
SELECT *FROM table1JOIN table2USING (id, state);
Denne join kombinerer table1
med table2
når to kolonner, som begge tabeller deler (id
og state
) hver har matchende værdier.
Den samme sammenføjning kunne udtrykkes mere detaljeret ved at bruge ON
sådan her:
SELECT *FROM table1JOIN table2ON table1.id = table2.id AND table1.state = table2.state;
Selvom begge ovenstående sammenføjninger ville resultere i, at de samme rækker blev konstrueret med de samme data til stede, ville de blive vist lidt anderledes. Mens ON
klausulen inkluderer alle kolonnerne fra begge tabeller, USING
klausul undertrykker de duplikerede kolonner. Så i stedet for at der er to separate id
kolonner og to separate state
kolonner (en for hver tabel), ville resultaterne kun have en af hver af de delte kolonner, efterfulgt af alle de andre kolonner, der er leveret af table1
og table2
.
Den NATURAL
klausul
Den NATURAL
klausul er endnu en stenografi, der yderligere kan reducere omfanget af USING
klausul. En NATURAL
join specificerer ikke nogle kolonner, der skal matches. I stedet vil PostgreSQL automatisk slutte sig til tabellerne baseret på alle kolonner, der har matchende kolonner i hver database.
Den generelle syntaks for NATURAL
join-klausulen ser sådan ud:
SELECT *FROM table1NATURAL JOIN table2;
Forudsat at table1
og table2
begge har kolonner med navnet id
, state
, og company
, ville ovenstående forespørgsel svare til denne forespørgsel ved at bruge ON
klausul:
SELECT *FROM table1JOIN table2ON table1.id = table2.id AND table1.state = table2.state AND table1.company = table2.company;
Og denne forespørgsel ved hjælp af USING
klausul:
SELECT *FROM table1JOIN table2USING (id, state, company);
Ligesom USING
klausulen, NATURAL
klausul undertrykker duplikerede kolonner, så der ville kun være en enkelt forekomst af hver af de sammenføjede kolonner i resultaterne.
Mens NATURAL
klausul kan reducere omfanget af dine forespørgsler, skal der udvises forsigtighed, når du bruger det. Fordi de kolonner, der bruges til at forbinde tabellerne, beregnes automatisk, hvis kolonnerne i komponenttabellerne ændres, kan resultaterne være vidt forskellige på grund af nye sammenføjningsbetingelser.
Forbindelsesbetingelser og WHERE
klausul
Sammenføjningsbetingelser deler mange karakteristika med sammenligninger, der bruges til at filtrere rækker af data ved hjælp af WHERE
klausuler. Begge konstruktioner definerer udtryk, der skal evalueres til sande, for at rækken kan tages i betragtning. På grund af dette er det ikke altid intuitivt, hvad forskellen er mellem at inkludere yderligere sammenligninger i en WHERE
konstruktion kontra at definere dem i selve join-klausulen.
For at forstå de forskelle, der vil resultere, er vi nødt til at se på den rækkefølge, hvori PostgreSQL behandler forskellige dele af en forespørgsel. I dette tilfælde behandles prædikaterne i join-betingelsen først for at konstruere den virtuelle sammenføjede tabel i hukommelsen. Efter dette trin vil udtrykkene i WHERE
klausul evalueres for at filtrere de resulterende rækker.
Antag som et eksempel, at vi har to tabeller kaldet customer
og order
at vi skal slutte os sammen. Vi ønsker at forbinde de to tabeller ved at matche customer.id
kolonne med order.customer_id
kolonne. Derudover er vi interesserede i rækkerne i order
tabel, der har et product_id
af 12345.
På baggrund af ovenstående krav har vi to forhold, som vi bekymrer os om. Den måde, vi udtrykker disse betingelser på, vil imidlertid afgøre, hvilke resultater vi modtager.
Lad os først bruge begge som joinbetingelser for en 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;
Resultaterne kunne potentielt se nogenlunde sådan her ud:
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 nåede frem til dette resultat ved at udføre følgende handlinger:
- Kombiner alle rækker i
customer
tabel medorder
tabel hvor:customer.id
matcherorder.customer_id
.order.product_id
matcher 12345
- Fordi vi bruger en venstre joinforbindelse, skal du inkludere alle umatchede rækker fra den venstre tabel (
customer
), udfyldning af kolonnerne fra den højre tabel (order
) medNULL
værdier. - Vis kun de kolonner, der er angivet i
SELECT
kolonnespecifikation.
Resultatet er, at alle vores samlede rækker matcher begge de betingelser, som vi leder efter. Den venstre join bevirker dog, at PostgreSQL også inkluderer alle rækker fra den første tabel, der ikke opfyldte join-betingelsen. Dette resulterer i "tilbageværende" rækker, der ikke ser ud til at følge den tilsyneladende hensigt med forespørgslen.
Hvis vi flytter den anden forespørgsel (order.product_id
=12345) til en WHERE
klausul, i stedet for at inkludere det som en joinbetingelse, får vi forskellige resultater:
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;
Denne gang vises kun tre rækker:
customer_id | name | order_id | product_id ------------+----------+----------+------------ 4380 | Acme Co | 480 | 12345 4380 | Acme Co | 182 | 12345 320 | Other Co | 680 | 12345(3 rows)
Den rækkefølge, som sammenligningerne udføres i, er årsagen til disse forskelle. Denne gang behandler PostgreSQL forespørgslen sådan her:
- Kombiner alle rækker i
customer
tabel medorder
tabel hvorcustomer.id
matcherorder.customer_id
. - Fordi vi bruger en venstre joinforbindelse, skal du inkludere alle umatchede rækker fra den venstre tabel (
customer
), udfyldning af kolonnerne fra den højre tabel (order
) medNULL
værdier. - Evaluer
WHERE
klausul for at fjerne alle rækker, der ikke har 12345 som værdien fororder.product_id
kolonne. - Vis kun de kolonner, der er angivet i
SELECT
kolonnespecifikation.
Denne gang, selvom vi bruger en venstre join, er WHERE
klausul trunkerer resultaterne ved at bortfiltrere alle rækkerne uden det korrekte product_id
. Fordi alle umatchede rækker ville have product_id
indstillet til NULL
, fjerner dette alle de umatchede rækker, der blev udfyldt af den venstre joinforbindelse. Det fjerner også enhver af rækkerne, der blev matchet af join-betingelsen, som ikke bestod denne anden runde af kontroller.
At forstå den grundlæggende proces, som PostgreSQL bruger til at udføre dine forespørgsler, kan hjælpe dig med at undgå nogle nemme at lave, men svære at fejlfinde, mens du arbejder med dine data.
Konklusion
I denne vejledning dækkede vi, hvordan joinforbindelser gør det muligt for relationelle databaser at kombinere data fra forskellige tabeller for at give mere værdifulde svar. Vi talte om de forskellige joinforbindelser, som PostgreSQL understøtter, den måde, hver type samler sine resultater på, og hvad man kan forvente, når man bruger specifikke typer joins. Bagefter gennemgik vi forskellige måder at definere joinbetingelser på og så på, hvordan samspillet mellem joins og WHERE
klausul kan føre til overraskelser.
Joins er en væsentlig del af det, der gør relationsdatabaser kraftfulde og fleksible nok til at håndtere så mange forskellige typer forespørgsler. Organisering af data ved hjælp af logiske grænser, mens du stadig er i stand til at rekombinere dataene på nye måder fra sag til sag, giver relationelle databaser som PostgreSQL en utrolig alsidighed. At lære, hvordan du udfører denne sammensætning mellem tabeller, vil give dig mulighed for at oprette mere komplekse forespørgsler og stole på, at databasen skaber komplette billeder af dine data.