sql >> Database teknologi >  >> RDS >> MariaDB

MariaDB SQL Set Operators

Sætoperatorer er de SQL-operatorer, der beskæftiger sig med at kombinere forskellige resultatsæt på forskellige måder. Lad os sige, at du har to forskellige SELECT Hvis du ønsker at kombinere til et enkelt resultatsæt, kommer sætoperatørerne i spil. MariaDB har understøttet UNION og UNION ALL sæt operatorer i lang tid, og disse er langt de mest almindelige SQL-sætoperatorer.

Men vi er på vej foran os selv her, lad mig først forklare de SQL-sætoperatorer, vi har, og hvordan de fungerer. Hvis du vil prøve dette, kan du bruge din eksisterende implementering af MariaDB Server eller prøve dette i en MariaDB SkySQL-skydatabase.

UNION og UNION ALL

UNION og UNION ALL sætoperatorer tilføjer resultatet af to eller flere resultatsæt. Lad os starte med UNION ALL og UNION vil så være en variant af UNION ALL .

Lad os se på, hvordan det ser ud i SQL. Lad os antage, at vi driver en webshop, og at vi for de produkter, vi sælger, har et lager. Nu vil vi se alle de produkter, der er på bestilling eller er på lager, en forespørgsel herom ville se sådan ud:

 SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN products p ON oi.prod_id =p.id UNION ALL SELECT i.prod_id, p.prod_name FROM inventory i JOIN products p ON i.prod_id =p.id; 

Dette er i mængdeteorien UNION af de produktsæt, der er bestilt, og de produktsæt, der er på lager. Hvilket er fint i teorien, men der er et problem med resultatet af denne forespørgsel. Problemet er, at et produkt, der optræder i både ordrer og lager, eller flere steder i lager, vil optræde mere end én gang i output. Dette problem er grunden til UNION ALL er ikke brugt meget og i stedet UNION DISTINCT (DISTINCT er standard og kan ignoreres) bruges. For eksempel:

SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN products p ON oi.prod_id =p.id UNION SELECT i.prod_id, p.prod_name FROM inventory i JOIN products p ON i.prod_id =p.id;

Med denne forespørgsel er et produkt, der enten er i bestilling eller findes i beholdningen, kun opført én gang. Bemærk, at når vi fjerner dubletter her, er det værdierne, der sammenlignes, så to rækker med de samme værdier i samme kolonne betragtes som lige store, selvom værdierne kommer fra forskellige tabeller eller kolonner.

For at være ærlig er der dog intet i forespørgslen ovenfor, der ikke kan gøres med en almindelig SELECT fra produkterne bord og et par joins. På nogle måder en UNION kan være lettere at læse. På den anden side, hvis vi ønsker at have en liste over produkter på bestilling eller på lageret, og også vil vide, hvilken det var, så ville en forespørgsel være sådan her:

 SELECT 'On order', oi.prod_id, p.prod_name FROM order_items oi JOIN products p ON oi.prod_id =p.id UNION SELECT 'Inventory', i.prod_id, p.prod_name FRA inventory i JOIN products p ON i.prod_id =p.id;

Her er en forespørgsel, der ikke er let at lave med en simpel SELECT fra produkterne tabel, da vi ser på den samme række fra produkttabellen to gange (én gang for ordre_items og én gang for beholdningen ).

Flere SQL-sætoperatorer

Med MariaDB Server 10.3 kom to nye SQL-sæt-operatører, der stort set blev introduceret for at forbedre Oracle-kompatibiliteten, men disse operatører er nyttige i sig selv. MariaDB Server 10.4 tilføjer derefter muligheden for at kontrollere sæt operatørprioritet. Det vil vi også se på. Uden muligheden for at kontrollere operatørens forrang fungerer de indstillede operatører ikke altid, som du ønsker eller forventer.

De nye SQL-sæt-operatorer er INTERSECT og EXCEPT og de er nyttige, især når du bruger analyser. Også selvom JOIN s og andre konstruktioner kan ofte bruges i stedet, SQL-sæt-operatorer giver mulighed for en SQL-syntaks, der kan være lettere at læse og forstå. Og hvis du har Oracle-applikationer, du migrerer til MariaDB, er nytten af ​​disse operatører indlysende.

INTERSECT sæt-operatøren

INTERSECT operatør vil returnere alle elementer, der findes i to eller flere sæt, eller i SQL-termer, alle rækker, der findes i to resultatsæt. I dette tilfælde oprettes et tværsnit af de to sæt elementer. I SQL-termer betyder det, at kun rækker, der findes i begge sæt, returneres, så hvis jeg vil tjekke, hvilke produkter jeg har på bestilling, og hvilke der også er på lager, kan en forespørgsel se sådan ud:

 SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN products p ON oi.prod_id =p.id INTERSECT SELECT i.prod_id, p.prod_name FRA inventory i JOIN products p ON i.prod_id =p.id;

Igen kunne denne forespørgsel konstrueres ved hjælp af en JOINprodukterne tabel, men forespørgslen ovenfor er lidt klarere om, hvad vi forsøger at opnå.

EXCEPT set-operatoren

I tilfælde af EXCEPT operatør, vil vi have de varer, der er i et af sættene, men ikke i det andet. Så igen ved at bruge eksemplet ovenfor, hvis vi ønsker at se de produkter, vi har på ordre, men som vi ikke har lager for, kunne vi skrive en forespørgsel som denne:

 SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN products p ON oi.prod_id =p.id UNDTAGET SELECT i.prod_id, p.prod_name FRA inventory i JOIN products p ON i.prod_id =p.id;

Igen er der andre måder at skrive denne specifikke forespørgsel på, men for andre, mere avancerede forespørgsler, når vi kombinerer data fra to forskellige tabeller, er dette ikke tilfældet.

Kombinering af flere sæt-operatorer

Du kan kombinere mere end 2 sæt operatorer, hvis dette er nyttigt. Lad os for eksempel se, om vi kan finde produkter, der er på bestilling og er leveret eller er på lager. SQL'en til dette ville se nogenlunde sådan ud:

SELECT oi.prod_id, p.prod_name FRA order_items oi JOIN products p ON oi.prod_id =p.id SKIFT SELECT d.prod_id, p.prod_name FRA leveringer d JOIN products p ON d.prod_id =p.id UNION SELECT i.prod_id, p.prod_name FRA lager i JOIN produkter p ON i.prod_id =p.id;

For at udtrykke dette i et almindeligt sprog, er det, der foregår, at jeg først tjekker, hvilke produkter der er på bestilling, og som er leveret, og derefter kombinerer jeg dette produktsæt med alle produkter på lageret. Ethvert produkt, der ikke er i resultatsættet, er ikke på lageret men kan være på bestilling eller være blevet leveret, men ikke begge dele.

Men lad os nu udtrykke dette anderledes og se, hvad der sker. Jeg ønsker en liste over alle produkter, der er på lager eller er leveret og er på bestilling. SQL'en ville så være noget som dette, svarende til SQL'en ovenfor, men lidt anderledes:

 SELECT i.prod_id, p.prod_name FRA inventory i JOIN products p ON i.prod_id =p.id UNION SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN products p ON oi.prod_id =p.id INTERSECT SELECT d.prod_id, p.prod_name FRA leveringer d JOIN produkter p ON d.prod_id =p.id;

Hvordan tolker du det så? Lister du produkter, der er på lager, og som er på bestilling, og de produkter, der bliver leveret? Sådan ser det ud, ikke? Det er bare den INTERSECT (og EXCEPT for den sags skyld) har prioritet over UNION . De to SQL-sætninger producerer det samme resultatsæt, i det mindste i MariaDB, og det er sådan, SQL-standarden siger, at tingene skal fungere. Men der er en undtagelse, Oracle.

Sådan fungerer det i Oracle

Oracle har haft alle fire SQL-sætoperatører (UNION , UNION ALL , INTERSECT og EXCEPT ) i lang tid, længe før de blev standardiseret, så deres implementering er lidt anderledes. Lad os prøve med tabellerne ovenfor og indsætte nogle data i dem. Dataene er meget enkle og afspejler en ikke så succesfuld virksomhed, men den fungerer som et eksempel, og vi viser kun de relevante kolonner her.

Tabel produkter ordre_items beholdning leverancer
Kolonne prod_id prod_name ordre_id prod_id prod_id prod_id
Data 1 Vase blå 1 1 1 2
2 Rød vase 2 1 2 3
3 Rødt tæppe 2 3

Med data på plads, lad os se på den sidste SQL-sætning ovenfor igen. Der er en funktion, der giver dig mulighed for at kontrollere forrangen, og det er at bruge parenteser eller parenteser (Introduceret i MariaDB 10.4, se https://jira.mariadb.org/browse/MDEV-11953), og bruge disse til at illustrere hvad der foregår, vil erklæringen se sådan ud:

 MariaDB> VÆLG i.prod_id, p.prod_name -> FRA lager i JOIN products p ON i.prod_id =p.id -> UNION -> (SELECT oi.prod_id, p.prod_name -> FROM order_items oi JOIN products p ON oi.prod_id =p.id -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FRA leverancer d JOIN products p ON d.prod_id =p.id); +--------+------------+ | prod_id | prod_name | +--------+------------+ | 1 | Vase Blå | | 2 | Vase Rød | | 3 | Gulvtæppe rødt | +--------+------------+ 3 rækker i sæt (0,001 sek.)

Lad os nu bruge den samme teknik til at tvinge de tre komponenter i forespørgslen til at fungere i streng rækkefølge:

 MariaDB> (SELECT i.prod_id, p.prod_name -> FRA inventory i JOIN products p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> FROM order_items oi JOIN products p ON oi.prod_id =p.id) -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FRA leveringer d JOIN products p ON d.prod_id =p.id; +--------+------------+ | prod_id | prod_name | +--------+------------+ | 2 | Vase Rød | | 3 | Gulvtæppe rødt | +--------+------------+ 2 rækker i sæt (0,001 sek.)

Og til sidst uden parentes:

 MariaDB [test]> VÆLG i.prod_id, p.prod_name -> FRA lager i JOIN products p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> FROM order_items oi JOIN products p ON oi.prod_id =p.id -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FRA leverancer d JOIN products p ON d.prod_id =p.id; +--------+------------+ | prod_id | prod_name | +--------+------------+ | 1 | Vase Blå | | 2 | Vase Rød | | 3 | Gulvtæppe rødt | +--------+------------+ 3 rækker i sæt (0,001 sek.)

Vi ser, at MariaDB, efter standarden, antog, at INTERSECT har forrang over UNION . Hvilket bringer os til Oracle. Lad os prøve ovenstående SQL i Oracle ved hjælp af sqlplus:

 SQL> VÆLG i.prod_id, p.prod_name 2 FRA lager i JOIN products p ON i.prod_id =p.id 3 UNION 4 SELECT oi.prod_id, p.prod_name 5 FROM order_items oi JOIN products p ON oi.prod_id =p.id 6 INTERSECT 7 VÆLG d.prod_id, p.prod_name 8 FRA leveringer d JOIN produkter p ON d.prod_id =p.id; PROD_ID PROD_NAME ---------- ------------------------------------ 2 vase rød 3 tæppe rød 

Hvad sker der her, spørger du? Nå, Oracle følger ikke standarden. De forskellige sætoperatorer behandles som lige, og ingen har forrang frem for den anden. Dette er et problem, når du migrerer applikationer fra Oracle til MariaDB, og hvad værre er, er, at denne forskel er lidt svær at finde. Der produceres ingen fejl, og data returneres, og i mange tilfælde returneres de rigtige data. Men i nogle tilfælde, hvor data er som i vores eksempel ovenfor, returneres de forkerte data, hvilket er et problem.

Effekt på migrering af data

Så hvordan håndterer vi dette, hvis vi migrerer en applikation fra Oracle til MariaDB? Der er et par muligheder:

  • Omskriv applikationen, så den antager, at de data, der returneres fra en forespørgsel som denne, er i overensstemmelse med SQL Standard og MariaDB.
  • Omskriv SQL-sætningerne ved hjælp af parenteser, så MariaDB returnerer de samme data som Oracle
  • Eller, og dette er den smarteste måde, brug MariaDB SQL_MODE=Oracle indstilling.

For den sidste og smarteste måde at fungere på, skal vi køre med MariaDB 10.3.7 eller nyere (dette blev foreslået af yours truly i https://jira.mariadb.org/browse/MDEV-13695). Lad os tjekke, hvordan dette fungerer. Sammenlign resultatet af denne SELECT med Oracle-en ovenfor (som giver det samme resultat) og den fra MariaDB ovenover (som ikke gør):

 MariaDB> sæt SQL_MODE=Oracle; Forespørgsel OK, 0 rækker påvirket (0,001 sek.) MariaDB> VÆLG i.prod_id, p.prod_name -> FRA lager i JOIN products p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> FRA ordre_varer oi JOIN produkter p ON oi.prod_id =p.id -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FRA leveringer d JOIN products p ON d.prod_id =p.id; +--------+------------+ | prod_id | prod_name | +--------+------------+ | 2 | Vase Rød | | 3 | Gulvtæppe rødt | +--------+------------+ 2 rækker i sæt (0,002 sek.)

Som du kan se, når SQL_MODE er indstillet til Oracle , MariaDB opfører sig virkelig som Oracle. Dette er ikke det eneste, der SQL_MODE=Oracle gør, selvfølgelig, men det er et af de mindre kendte områder.

Konklusion

Sætteoperatorerne INTERSECT og EXCEPT bliver ikke brugt så meget, selvom de forekommer hist og her, og der er nogle anvendelsesmuligheder for dem. Eksemplerne i denne blog er der mere for at illustrere, hvordan disse operatører fungerer, end for at vise rigtig gode anvendelser for dem. Der er sikkert bedre eksempler. Når du migrerer fra Oracle til MariaDB, er SQL-sætoperatorer virkelig nyttige, da mange Oracle-applikationer bruger dem, og som det kan ses, kan MariaDB narre til at fungere ligesom Oracle, som er ikke-standard, men stadig tjener et formål. Men som standard fungerer MariaDB selvfølgelig som det skal og følger SQL Standard.

Glad SQL'ing
/Karlsson


  1. Hvordan opdeler jeg en afgrænset streng i SQL Server uden at oprette en funktion?

  2. Docker - Hvordan kan man køre kommandoen psql i postgres containeren?

  3. Udførelse af sekvenser og serier i Postgres-XL

  4. Oracle bordskifteskærm