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

Operatori di insiemi SQL di MariaDB

Gli operatori di insiemi sono gli operatori SQL che si occupano di combinare, in modi diversi, insiemi di risultati diversi. Supponi di avere due diversi SELECT Se vuoi combinare in un unico set di risultati, entrano in gioco gli operatori di set. MariaDB ha supportato UNION e UNION ALL operatori di insiemi per molto tempo, e questi sono di gran lunga gli operatori di insiemi SQL più comuni.

Ma qui stiamo andando avanti, vorrei prima spiegare gli operatori di insiemi SQL che abbiamo e come funzionano. Se vuoi fare un tentativo, puoi utilizzare la tua distribuzione esistente di MariaDB Server o provarlo in un database cloud MariaDB SkySQL.

UNION e UNION ALL

Il UNION e UNION ALL gli operatori di insiemi aggiungono il risultato di due o più insiemi di risultati. Iniziamo con UNION ALL e UNION sarà quindi una variazione di UNION ALL .

Diamo un'occhiata a come appare in SQL. Supponiamo di gestire un negozio online e di avere un inventario per i prodotti che vendiamo. Ora vogliamo vedere tutti i prodotti in ordine o in inventario, una query per questo sarebbe simile a questa:

 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;

Questo è, in teoria degli insiemi, il UNION degli insiemi di prodotti che sono stati ordinati e degli insiemi di prodotti che sono in inventario. Che in teoria va bene, ma c'è un problema con il risultato di questa query. Il problema è che un prodotto che appare sia negli ordini che nell'inventario, o in più posizioni nell'inventario, apparirà più di una volta nell'output. Questo è il motivo per cui UNION ALL non è molto usato e invece UNION DISTINCT (DISTINCT è l'impostazione predefinita e può essere ignorata). Ad esempio:

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;

Con questa query, un prodotto in ordine o presente nell'inventario viene elencato solo una volta. Nota che quando rimuoviamo i duplicati qui, sono i valori che vengono confrontati, quindi due righe con gli stessi valori nella stessa colonna sono considerate uguali, anche se i valori provengono da tabelle o colonne diverse.

Ad essere onesti, tuttavia, non c'è nulla nella query precedente che non possa essere eseguita con un normale SELECT dai prodotti tabella e alcuni join. In qualche modo un UNION potrebbe essere più facile da leggere. D'altra parte, se vogliamo avere un elenco di prodotti in ordine o nell'inventario e vogliamo anche sapere quale fosse, allora una query sarebbe simile a questa:

 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
   FROM inventory i JOIN products p ON i.prod_id = p.id;

Ecco una query che non è facile da eseguire con un semplice SELECT dai prodotti tabella poiché stiamo guardando la stessa riga dalla tabella dei prodotti due volte (una volta per order_items e una volta per l'inventario ).

Più operatori di insiemi SQL

Con MariaDB Server 10.3 sono arrivati ​​due nuovi operatori di set SQL, in gran parte introdotti per migliorare la compatibilità Oracle, ma questi operatori sono utili di per sé. MariaDB Server 10.4 aggiunge quindi la possibilità di controllare la precedenza degli operatori impostati. Daremo un'occhiata anche a quello. Senza la possibilità di controllare la precedenza dell'operatore, gli operatori degli insiemi non funzionano sempre come vorresti o ti aspetteresti.

I nuovi operatori di insiemi SQL sono INTERSECT e EXCEPT e sono utili, in particolare quando si utilizza l'analisi. Inoltre, sebbene JOIN Se invece è possibile utilizzare spesso altri costrutti, gli operatori di insiemi SQL consentono una sintassi SQL che può essere più facile da leggere e comprendere. E, se hai applicazioni Oracle che stai migrando a MariaDB, l'utilità di questi operatori è ovvia.

L'operatore del set INTERSECT

Il INTERSECT l'operatore restituirà tutti gli elementi che esistono in due o più set o, in termini SQL, tutte le righe che esistono in due set di risultati. In questo caso viene creata una sezione trasversale dei due insiemi di elementi. In termini SQL significa che vengono restituite solo le righe che esistono in entrambi i set, quindi se voglio controllare quali prodotti ho in ordine e quali sono anche in inventario, una query potrebbe essere simile a questa:

 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
   FROM inventory i JOIN products p ON i.prod_id = p.id;

Anche in questo caso, questa query potrebbe essere costruita utilizzando un JOIN sui prodotti tabella, ma la query sopra è un po' più chiara su ciò che stiamo cercando di ottenere.

L'operatore EXCEPT set

Nel caso di EXCEPT operatore, vogliamo gli elementi che si trovano in uno dei set, ma non nell'altro. Quindi, sempre usando l'esempio sopra, se vogliamo vedere i prodotti che abbiamo in ordine ma per i quali non abbiamo inventario, potremmo scrivere una query come questa:

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

Anche in questo caso, ci sono altri modi per scrivere questa particolare query, ma per altre query più avanzate quando stiamo combinando i dati di due tabelle diverse, questo non è il caso.

Combinazione di più operatori di insiemi

È possibile combinare più di 2 operatori di insiemi se ciò risulta utile. Ad esempio, vediamo se riusciamo a trovare prodotti che sono in ordine e sono stati consegnati o sono in stock. L'SQL per questo sarebbe simile a questo:

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
   FROM deliveries d JOIN products p ON d.prod_id = p.id
 UNION
 SELECT i.prod_id, p.prod_name
   FROM inventory i JOIN products p ON i.prod_id = p.id;

Per esprimere questo in un linguaggio semplice, quello che sta succedendo è che prima controllo quali prodotti sono in ordine e che sono stati consegnati, quindi combino questo set di prodotti con tutti i prodotti nell'inventario. Qualsiasi prodotto non presente nel set di risultati non è nell'inventario ma potrebbe essere in ordine o potrebbe essere stato consegnato, ma non entrambi.

Ma ora, esprimiamolo in modo diverso e vediamo cosa succede. Voglio un elenco di tutti i prodotti che sono in stock o sono stati consegnati e sono in ordine. L'SQL sarebbe quindi qualcosa del genere, simile all'SQL sopra ma leggermente diverso:

 SELECT i.prod_id, p.prod_name
   FROM 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
   FROM deliveries d JOIN products p ON d.prod_id = p.id;

Come lo interpreti allora? Elenca i prodotti che sono in stock e che sono in ordine e i prodotti che vengono consegnati? Ecco come appare, giusto? È solo che INTERSECT (e EXCEPT del resto) ha la precedenza su UNION . Le due istruzioni SQL producono lo stesso set di risultati, almeno in MariaDB ed è così che lo standard SQL dice che le cose dovrebbero funzionare. Ma c'è un'eccezione, Oracle.

Come funziona in Oracle

Oracle ha avuto tutti e quattro gli operatori di insiemi SQL (UNION , UNION ALL , INTERSECT e EXCEPT ) per molto tempo, molto prima che fossero standardizzati, quindi la loro implementazione è leggermente diversa. Proviamo con le tabelle sopra e inseriamo alcuni dati al loro interno. I dati sono molto semplici e riflettono un'azienda di scarso successo, ma funzionano come esempio e qui mostriamo solo le colonne pertinenti.

Tabella prodotti ordina_articoli inventario consegne
Colonna id_prod nome_prod ID_ordine id_prod id_prod id_prod
Dati 1 Vaso Blu 1 1 1 2
2 Vaso Rosso 2 1 2 3
3 Tappeto Rosso 2 3

Con i dati in atto, diamo un'occhiata all'ultima istruzione SQL sopra. C'è una funzione che ti permette di controllare la precedenza, e cioè usare parentesi o parentesi (introdotta in MariaDB 10.4, vedere https://jira.mariadb.org/browse/MDEV-11953) e usarle per illustrare cosa sta succedendo, la dichiarazione sarebbe simile a questa:

 MariaDB> SELECT i.prod_id, p.prod_name
     ->   FROM 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
     ->   FROM deliveries d JOIN products p ON d.prod_id = p.id);
 +---------+------------+
 | prod_id | prod_name  |
 +---------+------------+
 |       1 | Vase Blue  |
 |       2 | Vase Red   |
 |       3 | Carpet Red |
 +---------+------------+
 3 rows in set (0.001 sec)

Ora, utilizziamo la stessa tecnica per imporre ai tre componenti della query di operare in un ordine rigoroso:

 MariaDB> (SELECT i.prod_id, p.prod_name
     ->   FROM 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
     ->   FROM deliveries d JOIN products p ON d.prod_id = p.id;
 +---------+------------+
 | prod_id | prod_name  |
 +---------+------------+
 |       2 | Vase Red   |
 |       3 | Carpet Red |
 +---------+------------+
 2 rows in set (0.001 sec)

E infine senza parentesi:

 MariaDB [test]> SELECT i.prod_id, p.prod_name
     ->   FROM 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
     ->   FROM deliveries d JOIN products p ON d.prod_id = p.id;
 +---------+------------+
 | prod_id | prod_name  |
 +---------+------------+
 |       1 | Vase Blue  |
 |       2 | Vase Red   |
 |       3 | Carpet Red |
 +---------+------------+
 3 rows in set (0.001 sec)

Vediamo che MariaDB, seguendo lo standard, presumeva che INTERSECT ha la precedenza su UNION . Il che ci porta a Oracle. Proviamo l'SQL sopra in Oracle usando sqlplus:

 SQL> SELECT i.prod_id, p.prod_name
   2   FROM inventory 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  SELECT d.prod_id, p.prod_name
   8   FROM deliveries d JOIN products p ON d.prod_id = p.id;
 
    PROD_ID PROD_NAME
 ---------- ------------------------------
          2 Vase Red
          3 Carpet Red

Cosa sta succedendo qui, chiedi? Bene, Oracle non segue lo standard. I diversi operatori di insiemi vengono trattati come uguali e nessuno ha la precedenza sull'altro. Questo è un problema quando si migrano le applicazioni da Oracle a MariaDB e, quel che è peggio, è che questa differenza è piuttosto difficile da trovare. Non viene prodotto alcun errore e vengono restituiti i dati e, in molti casi, vengono restituiti i dati corretti. Ma, in alcuni casi, dove i dati sono come nel nostro esempio sopra, vengono restituiti i dati sbagliati, il che è un problema.

Effetto sulla migrazione dei dati

Quindi, come gestiamo questo problema se stiamo migrando un'applicazione da Oracle a MariaDB? Ci sono alcune opzioni:

  • Riscrivi l'applicazione in modo che assuma che i dati restituiti da una query come questa siano in linea con lo standard SQL e MariaDB.
  • Riscrivi le istruzioni SQL, usando le parentesi, in modo che MariaDB restituisca gli stessi dati di Oracle
  • Oppure, e questo è il modo più intelligente, usa MariaDB SQL_MODE=Oracle impostazione.

Per l'ultimo, e il modo più intelligente, di funzionare, dobbiamo eseguire MariaDB 10.3.7 o versioni successive (questo è stato suggerito dal tuo veramente in https://jira.mariadb.org/browse/MDEV-13695). Controlliamo come funziona. Confronta il risultato di questo SELECT con quello di Oracle sopra (che produce lo stesso risultato) e quello di MariaDB sopra quello (che non lo fa):

 MariaDB> set SQL_MODE=Oracle;
 Query OK, 0 rows affected (0.001 sec)
 
 MariaDB> SELECT i.prod_id, p.prod_name
     ->   FROM 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
     ->   FROM deliveries d JOIN products p ON d.prod_id = p.id;
 +---------+------------+
 | prod_id | prod_name  |
 +---------+------------+
 |       2 | Vase Red   |
 |       3 | Carpet Red |
 +---------+------------+
 2 rows in set (0.002 sec)

Come puoi vedere, quando SQL_MODE è impostato su Oracle , MariaDB si comporta davvero come Oracle. Questa non è l'unica cosa che SQL_MODE=Oracle sì, ovviamente, ma è una delle aree meno conosciute.

Conclusione

Gli operatori degli insiemi INTERSECT e EXCEPT non sono usati molto, anche se appaiono qua e là, e ci sono alcuni usi per loro. Gli esempi in questo blog servono più per illustrare come funzionano questi operatori che per mostrarne un uso davvero efficace. Ci sono probabilmente esempi migliori. Quando si esegue la migrazione da Oracle a MariaDB, tuttavia, gli operatori di insiemi SQL sono davvero utili poiché molte applicazioni Oracle li utilizzano e, come si può vedere, MariaDB può essere indotta a funzionare proprio come Oracle, che non è standard ma serve comunque a uno scopo. Ma, per impostazione predefinita, ovviamente MariaDB funziona come dovrebbe e segue lo standard SQL.

Buon SQL'ing
/Karlsson