MariaDB
 sql >> Base de données >  >> RDS >> MariaDB

Opérateurs d'ensemble SQL MariaDB

Les opérateurs d'ensemble sont les opérateurs SQL qui traitent de la combinaison, de différentes manières, de différents ensembles de résultats. Disons que vous avez deux SELECT différents Lorsque vous souhaitez combiner en un seul ensemble de résultats, les opérateurs d'ensemble entrent en jeu. MariaDB supporte l'UNION et UNION ALL opérateurs d'ensemble depuis longtemps, et ce sont de loin les opérateurs d'ensemble SQL les plus courants.

Mais nous prenons de l'avance ici, permettez-moi d'abord d'expliquer les opérateurs d'ensemble SQL que nous avons et comment ils fonctionnent. Si vous souhaitez essayer cela, vous pouvez utiliser votre déploiement existant de MariaDB Server ou l'essayer dans une base de données cloud MariaDB SkySQL.

UNION et UNION TOUS

L'UNION et UNION ALL les opérateurs d'ensemble ajoutent le résultat de deux ensembles de résultats ou plus. Commençons par UNION ALL et UNION sera alors une variante de UNION ALL .

Voyons à quoi cela ressemble en SQL. Supposons que nous gérons une boutique en ligne et que pour les produits que nous vendons, nous avons un inventaire. Maintenant, nous voulons voir tous les produits en commande ou en stock, une requête pour cela ressemblerait à ceci :

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

C'est, en théorie des ensembles, l'UNION des ensembles de produits commandés et des ensembles de produits en stock. Ce qui est bien en théorie, mais il y a un problème avec le résultat de cette requête. Le problème est qu'un produit qui apparaît à la fois dans les commandes et dans l'inventaire, ou à plusieurs endroits dans l'inventaire, apparaîtra plus d'une fois dans la sortie. Ce problème est la raison pour laquelle UNION ALL n'est pas beaucoup utilisé et à la place UNION DISTINCT (DISTINCT est la valeur par défaut et peut être ignorée) est utilisé. Par exemple :

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

Avec cette requête, un produit qui est en commande ou qui existe dans l'inventaire n'est répertorié qu'une seule fois. Notez que lorsque nous supprimons les doublons ici, ce sont les valeurs qui sont comparées, donc deux lignes avec les mêmes valeurs dans la même colonne sont considérées comme égales, même si les valeurs proviennent de tables ou de colonnes différentes.

Pour être honnête cependant, il n'y a rien dans la requête ci-dessus qui ne puisse être fait avec un SELECT normal parmi les produits table et quelques jointures. D'une certaine manière, un UNION peut être plus facile à lire. D'un autre côté, si nous voulons avoir une liste de produits en commande ou en inventaire, et aussi savoir de quel produit il s'agit, alors une requête ressemblerait à ceci :

 SELECT 'Sur commande', oi.prod_id, p.prod_name FROM order_items oi JOIN produits p ON oi.prod_id =p.id UNION SELECT 'Inventaire', i.prod_id, p.prod_name FROM inventaire i JOIN produits p ON i.prod_id =p.id;

Voici une requête pas facile à faire avec un simple SELECT parmi les produits table car nous examinons deux fois la même ligne de la table des produits (une fois pour les order_items et une fois pour l'inventaire ).

Plus d'opérateurs d'ensemble SQL

Avec MariaDB Server 10.3, deux nouveaux opérateurs d'ensemble SQL ont été introduits en grande partie pour améliorer la compatibilité avec Oracle, mais ces opérateurs sont utiles en eux-mêmes. MariaDB Server 10.4 ajoute ensuite la possibilité de contrôler la priorité des opérateurs définis. Nous allons également voir cela. Sans la possibilité de contrôler la priorité des opérateurs, les opérateurs d'ensemble ne fonctionnent pas toujours comme vous le souhaiteriez ou l'attendriez.

Les nouveaux opérateurs d'ensemble SQL sont INTERSECT et EXCEPT et ils sont utiles, en particulier lors de l'utilisation d'analyses. Aussi, bien que JOIN s et d'autres constructions peuvent souvent être utilisées à la place, les opérateurs d'ensemble SQL permettent une syntaxe SQL qui peut être plus facile à lire et à comprendre. Et, si vous avez des applications Oracle que vous migrez vers MariaDB, l'utilité de ces opérateurs est évidente.

L'opérateur d'ensemble INTERSECT

L'INTERSECT renverra tous les éléments qui existent dans deux ensembles ou plus, ou en termes SQL, toutes les lignes qui existent dans deux ensembles de résultats. Dans ce cas, une coupe transversale des deux ensembles d'éléments est créée. En termes SQL, cela signifie que seules les lignes qui existent dans les deux ensembles sont renvoyées, donc si je veux vérifier quels produits j'ai en commande et lesquels sont également en stock, une requête pourrait ressembler à ceci :

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

Encore une fois, cette requête pourrait être construite en utilisant un JOIN sur les produits table, mais la requête ci-dessus est un peu plus claire sur ce que nous essayons d'accomplir.

L'opérateur d'ensemble SAUF

Dans le cas du EXCEPT opérateur, nous voulons les éléments qui sont dans l'un des ensembles, mais pas dans l'autre. Donc, toujours en utilisant l'exemple ci-dessus, si nous voulons voir les produits que nous avons en commande mais pour lesquels nous n'avons pas d'inventaire, nous pourrions écrire une requête comme celle-ci :

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

Encore une fois, il existe d'autres façons d'écrire cette requête particulière, mais pour d'autres requêtes plus avancées lorsque nous combinons des données de deux tables différentes, ce n'est pas le cas.

Combiner plusieurs opérateurs d'ensemble

Vous pouvez combiner plus de 2 opérateurs d'ensemble si cela est utile. Par exemple, voyons si nous pouvons trouver des produits qui sont en commande et qui ont été livrés ou qui sont en stock. Le SQL pour cela ressemblerait à ceci :

SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN produits p ON oi.prod_id =p.id INTERSECT SELECT d.prod_id, p.prod_name FROM livraisons d JOIN produits p ON d.prod_id =p.id UNION SELECT i.prod_id, p.prod_name FROM inventaire i JOIN produits p ON i.prod_id =p.id;

Pour exprimer cela en langage clair, ce qui se passe, c'est que je vérifie d'abord quels produits sont en commande et qui ont été livrés, puis je combine cet ensemble de produits avec tous les produits de l'inventaire. Tout produit ne figurant pas dans l'ensemble de résultats n'est pas dans l'inventaire mais peut être sur commande ou peut avoir été livré, mais pas les deux.

Mais maintenant, exprimons cela différemment et voyons ce qui se passe. Je veux une liste de tous les produits qui sont en stock ou qui ont été livrés et qui sont en commande. Le SQL serait alors quelque chose comme ceci, similaire au SQL ci-dessus mais légèrement différent :

 SELECT i.prod_id, p.prod_name FROM inventaire i JOIN produits p ON i.prod_id =p.id UNION SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN produits p ON oi.prod_id =p.id INTERSECT SELECT d.prod_id, p.prod_name FROM livraisons d JOIN produits p ON d.prod_id =p.id;

Comment interprétez-vous cela alors ? Faites-vous la liste des produits qui sont en stock et qui sont en commande et les produits qui sont en cours de livraison ? C'est à ça que ça ressemble, non ? C'est juste que INTERSECT (et EXCEPT d'ailleurs) a priorité sur UNION . Les deux instructions SQL produisent le même ensemble de résultats, au moins dans MariaDB et c'est ainsi que le standard SQL dit que les choses devraient fonctionner. Mais il y a une exception, Oracle.

Comment cela fonctionne dans Oracle

Oracle a eu les quatre opérateurs d'ensemble SQL (UNION , UNION ALL , INTERSECT et EXCEPT ) pendant longtemps, bien avant qu'ils ne soient standardisés, leur implémentation est donc un peu différente. Essayons avec les tableaux ci-dessus et insérons-y des données. Les données sont très simples et reflètent une entreprise moins performante, mais cela fonctionne comme un exemple, et nous ne montrons ici que les colonnes pertinentes.

Tableau produits articles_de_commande inventaire livraisons
Colonne prod_id prod_name id_commande prod_id prod_id prod_id
Données 1 Vase Bleu 1 1 1 2
2 Vase Rouge 2 1 2 3
3 Tapis Rouge 2 3

Avec les données en place, regardons à nouveau cette dernière instruction SQL ci-dessus. Il existe une fonctionnalité qui vous permet de contrôler la priorité, et qui consiste à utiliser des parenthèses ou des crochets (introduit dans MariaDB 10.4, voir https://jira.mariadb.org/browse/MDEV-11953), et à les utiliser pour illustrer ce qui se passe, la déclaration ressemblerait à ceci :

 MariaDB> SELECT i.prod_id, p.prod_name -> FROM inventaire i JOIN produits 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 livraisons d JOIN produits p ON d.prod_id =p.id); +---------+------------+ | id_prod | nom_prod | +---------+------------+ | 1 | Vase Bleu | | 2 | Vase Rouge | | 3 | Tapis Rouge | +---------+------------+ 3 rangées en série (0.001 sec)

Maintenant, utilisons la même technique pour forcer les trois composants de la requête à fonctionner dans un ordre strict :

 MariaDB> (SELECT i.prod_id, p.prod_name -> FROM inventaire i JOIN produits p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> FROM order_items oi JOIN produits p ON oi.prod_id =p.id) -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FROM livraisons d JOIN produits p ON d.prod_id =p.id; +---------+------------+ | id_prod | nom_prod | +---------+------------+ | 2 | Vase Rouge | | 3 | Tapis Rouge | +---------+------------+ 2 lignes en série (0.001 sec)

Et enfin sans parenthèses :

 MariaDB [test]> SELECT i.prod_id, p.prod_name -> FROM inventaire i JOIN produits p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> FROM order_items oi JOIN produits p ON oi.prod_id =p.id -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FROM livraisons d JOIN products p ON d.prod_id =p.id; +---------+------------+ | id_prod | nom_prod | +---------+------------+ | 1 | Vase Bleu | | 2 | Vase Rouge | | 3 | Tapis Rouge | +---------+------------+ 3 rangées en série (0.001 sec)

Nous voyons que MariaDB, suivant la norme, a supposé que INTERSECT a priorité sur UNION . Ce qui nous amène à Oracle. Essayons le SQL ci-dessus dans Oracle en utilisant sqlplus :

 SQL> SELECT i.prod_id, p.prod_name 2 FROM inventaire i JOIN produits p ON i.prod_id =p.id 3 UNION 4 SELECT oi.prod_id, p.prod_name 5 FROM order_items oi JOIN produits p ON oi.prod_id =p.id 6 INTERSECT 7 SELECT d.prod_id, p.prod_name 8 FROM livraisons d JOIN produits p ON d.prod_id =p.id; PROD_ID PROD_NAME ---------- ------------------------------ 2 Vase Rouge 3 Tapis Rouge 

Que se passe-t-il ici, demandez-vous? Eh bien, Oracle ne suit pas la norme. Les différents opérateurs d'ensemble sont traités comme égaux et aucun n'a priorité sur l'autre. C'est un problème lorsque vous migrez des applications d'Oracle vers MariaDB, et ce qui est pire, c'est que cette différence est assez difficile à trouver. Aucune erreur n'est produite et les données sont renvoyées et, dans de nombreux cas, les bonnes données sont renvoyées. Mais, dans certains cas, lorsque les données sont comme dans notre exemple ci-dessus, les mauvaises données sont renvoyées, ce qui est un problème.

Effet sur la migration des données

Alors, comment gérons-nous cela si nous migrons une application d'Oracle vers MariaDB ? Il existe plusieurs options :

  • Réécrivez l'application afin qu'elle suppose que les données renvoyées par une requête comme celle-ci sont conformes au standard SQL et à MariaDB.
  • Réécrivez les instructions SQL, en utilisant des crochets, afin que MariaDB renvoie les mêmes données qu'Oracle
  • Ou, et c'est le moyen le plus intelligent, utilisez MariaDB SQL_MODE=Oracle paramètre.

Pour la dernière façon, et la plus intelligente, de fonctionner, nous devons exécuter MariaDB 10.3.7 ou supérieur (ceci a été suggéré par votre serviteur dans https://jira.mariadb.org/browse/MDEV-13695). Vérifions comment cela fonctionne. Comparez le résultat de ce SELECT avec celui d'Oracle au-dessus (qui produit le même résultat) et celui de MariaDB au-dessus (qui ne le fait pas) :

 MariaDB> set SQL_MODE=Oracle ; Requête OK, 0 lignes affectées (0.001 sec) MariaDB> SELECT i.prod_id, p.prod_name -> FROM inventaire i JOIN produits p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> FROM articles_commande oi JOIN produits p ON oi.prod_id =p.id -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FROM livraisons d JOIN produits p ON d.prod_id =p.id ; +---------+------------+ | id_prod | nom_prod | +---------+------------+ | 2 | Vase Rouge | | 3 | Tapis Rouge | +---------+------------+ 2 rangées en série (0.002 sec)

Comme vous pouvez le voir, lorsque SQL_MODE est défini sur Oracle , MariaDB se comporte vraiment comme Oracle. Ce n'est pas la seule chose que SQL_MODE=Oracle le fait, évidemment, mais c'est l'un des domaines les moins connus.

Conclusion

Les opérateurs d'ensemble INTERSECT et EXCEPT ne sont pas beaucoup utilisés, bien qu'ils apparaissent ici et là, et qu'il y ait quelques utilisations pour eux. Les exemples de ce blog sont là plus pour illustrer le fonctionnement de ces opérateurs que pour en montrer de très bonnes utilisations. Il existe probablement de meilleurs exemples. Cependant, lorsque vous migrez d'Oracle vers MariaDB, les opérateurs d'ensemble SQL sont vraiment utiles car de nombreuses applications Oracle les utilisent, et comme on peut le voir, MariaDB peut être amenée à fonctionner comme Oracle, qui n'est pas standard mais sert toujours un but. Mais, par défaut, bien sûr, MariaDB fonctionne comme il se doit et suit le standard SQL.

Bonne lecture SQL
/Karlsson