MariaDB
 sql >> Baza danych >  >> RDS >> MariaDB

Operatory zbiorów SQL MariaDB

Operatory zestawów to operatory SQL, które zajmują się łączeniem na różne sposoby różnych zestawów wyników. Załóżmy, że masz dwa różne SELECT Jeśli chcesz połączyć w jeden zestaw wyników, w grę wchodzą operatory zestawu. MariaDB wspiera UNION i UNION ALL operatory zbiorów przez długi czas i są to zdecydowanie najbardziej popularne operatory zbiorów SQL.

Ale tutaj wyprzedzamy siebie, pozwólcie, że najpierw wyjaśnię, jakie mamy operatory zbiorów SQL i jak one działają. Jeśli chcesz to wypróbować, możesz użyć istniejącego wdrożenia MariaDB Server lub wypróbować to w bazie danych MariaDB SkySQL w chmurze.

UNION i UNION ALL

UNION i UNION ALL operatory zestawów dodają wynik dwóch lub więcej zestawów wyników. Zacznijmy od UNION ALL i UNION będzie wtedy odmianą UNION ALL .

Przyjrzyjmy się, jak to wygląda w SQL. Załóżmy, że prowadzimy sklep internetowy i dla sprzedawanych przez nas produktów mamy zapas. Teraz chcemy zobaczyć wszystkie produkty, które są na zamówienie lub są w magazynie, zapytanie o to wyglądałoby mniej więcej tak:

 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;

W teorii mnogości jest to UNION zestawów produktów, które zostały zamówione oraz zestawów produktów, które znajdują się w magazynie. W teorii jest to w porządku, ale jest problem z wynikiem tego zapytania. Problem polega na tym, że produkt, który pojawia się zarówno w zamówieniach, jak i w magazynie lub w wielu miejscach w magazynie, pojawi się więcej niż jeden raz w wynikach. Ten problem jest powodem, dla którego UNION ALL nie jest często używany, a zamiast tego UNION DISTINCT (DISTINCT jest wartością domyślną i można ją zignorować). Na przykład:

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;

W przypadku tego zapytania produkt, który jest zamówiony lub znajduje się w magazynie, jest wymieniony tylko raz. Pamiętaj, że kiedy usuwamy tutaj duplikaty, porównywane są wartości, więc dwa wiersze z tymi samymi wartościami w tej samej kolumnie są uważane za równe, nawet jeśli wartości pochodzą z różnych tabel lub kolumn.

Szczerze mówiąc, w powyższym zapytaniu nie ma nic, czego nie można by zrobić za pomocą zwykłego SELECT z produktów stół i kilka złączeń. W pewnym sensie UNION może być łatwiejszy do odczytania. Z drugiej strony, jeśli chcemy mieć listę produktów na zamówieniu lub w asortymencie, a także chcemy wiedzieć, który to był, to zapytanie wyglądałoby mniej więcej tak:

 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;

Oto zapytanie, które nie jest łatwe do wykonania za pomocą prostego SELECT z produktów tabeli, ponieważ dwukrotnie patrzymy na ten sam wiersz z tabeli produktów (raz dla order_items i raz na inwentarz ).

Więcej operatorów zbiorów SQL

Wraz z MariaDB Server 10.3 pojawiły się dwa nowe operatory zestawów SQL, w dużej mierze wprowadzone w celu zwiększenia zgodności z Oracle, ale operatory te są przydatne same w sobie. MariaDB Server 10.4 dodaje następnie możliwość kontrolowania pierwszeństwa operatorów zestawu. Na to też przyjrzymy się. Bez możliwości kontrolowania pierwszeństwa operatorów, operatory zbioru nie zawsze działają tak, jak byś tego chciał lub oczekiwał.

Nowe operatory zestawów SQL to INTERSECT i EXCEPT i są przydatne, szczególnie podczas korzystania z analiz. Również, chociaż JOIN s i inne konstrukcje mogą być często używane zamiast tego, operatory zestawów SQL umożliwiają stosowanie składni SQL, która może być łatwiejsza do odczytania i zrozumienia. A jeśli masz aplikacje Oracle, które migrujesz do MariaDB, przydatność tych operatorów jest oczywista.

Operator mnogości INTERSECT

INTERSECT Operator zwróci wszystkie elementy, które istnieją w dwóch lub więcej zestawach, lub w kategoriach SQL, wszystkie wiersze, które istnieją w dwóch zestawach wynikowych. W takim przypadku tworzony jest przekrój poprzeczny dwóch zestawów elementów. W języku SQL oznacza to, że zwracane są tylko wiersze, które istnieją w obu zestawach, więc jeśli chcę sprawdzić, które produkty mam na zamówienie, a które są w magazynie, zapytanie może wyglądać tak:

 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;

Ponownie, to zapytanie może być skonstruowane przy użyciu JOIN o produktach tabeli, ale powyższe zapytanie jest nieco jaśniejsze na temat tego, co staramy się osiągnąć.

Z WYJĄTKIEM operatora zbioru

W przypadku EXCEPT operatora, chcemy, aby elementy, które znajdują się w jednym z zestawów, ale nie w drugim. Tak więc, ponownie korzystając z powyższego przykładu, jeśli chcemy zobaczyć produkty, które mamy na zamówienie, ale dla których nie mamy zapasów, możemy napisać zapytanie w ten sposób:

 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;

Ponownie, istnieją inne sposoby napisania tego konkretnego zapytania, ale w przypadku innych, bardziej zaawansowanych zapytań, gdy łączymy dane z dwóch różnych tabel, tak nie jest.

Łączenie wielu operatorów zestawów

Możesz połączyć więcej niż 2 operatory zestawów, jeśli jest to przydatne. Na przykład zobaczmy, czy możemy znaleźć produkty, które są na zamówienie i zostały dostarczone lub są w magazynie. Kod SQL dla tego wyglądałby mniej więcej tak:

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;

Aby wyrazić to prostym językiem, najpierw sprawdzam, które produkty są na zamówienie, a które zostały dostarczone, a następnie łączę ten zestaw produktów ze wszystkimi produktami w magazynie. Żaden produkt, którego nie ma w zestawie wyników, nie ma w ekwipunku ale może być na zamówienie lub mógł zostać dostarczony, ale nie jedno i drugie.

Ale teraz wyraźmy to inaczej i zobaczmy, co się stanie. Chcę listę wszystkich produktów, które są w magazynie lub zostały dostarczone i są na zamówienie. SQL byłby wtedy podobny do tego, podobny do powyższego SQL, ale nieco inny:

 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;

Jak więc to zinterpretujesz? Czy wymieniasz produkty, które są w magazynie i są na zamówienie oraz produkty, które są dostarczane? Tak to wygląda, prawda? Po prostu INTERSECT (i EXCEPT jeśli o to chodzi) ma pierwszeństwo ponad UNION . Dwie instrukcje SQL dają ten sam zestaw wyników, przynajmniej w MariaDB i tak standard SQL mówi, że wszystko powinno działać. Ale jest wyjątek, Oracle.

Jak to działa w Oracle

Oracle ma wszystkie cztery operatory zestawów SQL (UNION , UNION ALL , INTERSECT i EXCEPT ) przez długi czas, na długo zanim zostały ujednolicone, więc ich implementacja jest nieco inna. Spróbujmy z powyższymi tabelami i wstawmy do nich trochę danych. Dane są bardzo proste i odzwierciedlają niezbyt udaną firmę, ale działają jako przykład i pokazujemy tutaj tylko odpowiednie kolumny.

Tabela produkty order_items inwentarz dostawy
Kolumna prod_id nazwa_produktu identyfikator zamówienia prod_id prod_id prod_id
Dane 1 Wazon niebieski 1 1 1 2
2 Wazon czerwony 2 1 2 3
3 Czerwony dywan 2 3

Mając dane, spójrzmy ponownie na ostatnią instrukcję SQL powyżej. Istnieje funkcja, która pozwala kontrolować pierwszeństwo i polega na używaniu nawiasów lub nawiasów (wprowadzona w MariaDB 10.4, patrz https://jira.mariadb.org/browse/MDEV-11953) i używanie ich do zilustrowania co się dzieje, stwierdzenie wyglądałoby tak:

 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)

Teraz użyjmy tej samej techniki, aby wymusić działanie trzech składników zapytania w ścisłej kolejności:

 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)

I na koniec bez nawiasów:

 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)

Widzimy, że MariaDB, zgodnie ze standardem, założyła, że ​​INTERSECT ma pierwszeństwo przed UNION . Co prowadzi nas do Oracle. Wypróbujmy powyższy SQL w Oracle używając 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

Co tu się dzieje, pytasz? Cóż, Oracle nie przestrzega standardu. Różne operatory zbioru są traktowane jako równe i żaden nie ma pierwszeństwa przed innymi. Jest to problem, gdy migrujesz aplikacje z Oracle do MariaDB, a co gorsza, trudno jest znaleźć tę różnicę. Nie występuje błąd i dane są zwracane, aw wielu przypadkach zwracane są właściwe dane. Jednak w niektórych przypadkach, gdy dane są takie jak w powyższym przykładzie, zwracane są nieprawidłowe dane, co stanowi problem.

Wpływ na migrację danych

Jak więc sobie z tym poradzić, jeśli migrujemy aplikację z Oracle do MariaDB? Jest kilka opcji:

  • Przepisz aplikację, aby zakładała, że ​​dane zwrócone z takiego zapytania są zgodne ze standardem SQL i MariaDB.
  • Przepisz instrukcje SQL, używając nawiasów, aby MariaDB zwracała te same dane co Oracle
  • Lub, i to jest najmądrzejszy sposób, użyj MariaDB SQL_MODE=Oracle ustawienie.

Aby ostatni i najmądrzejszy sposób działał, musimy uruchomić z MariaDB 10.3.7 lub nowszym (zasugerowano to naprawdę na https://jira.mariadb.org/browse/MDEV-13695). Sprawdźmy, jak to działa. Porównaj wynik tego SELECT z tym Oracle powyżej (który daje ten sam wynik) i tym z MariaDB powyżej (który nie):

 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)

Jak widać, kiedy SQL_MODE jest ustawiona na Oracle , MariaDB naprawdę zachowuje się jak Oracle. To nie jedyna rzecz, którą SQL_MODE=Oracle oczywiście tak, ale jest to jeden z mniej znanych obszarów.

Wniosek

Operatory zbioru INTERSECT i EXCEPT nie są używane tak często, chociaż pojawiają się tu i tam i są dla nich pewne zastosowania. Przykłady na tym blogu są bardziej po to, aby zilustrować, jak działają te operatory, niż pokazać naprawdę dobre dla nich zastosowania. Są chyba lepsze przykłady. Jednak podczas migracji z Oracle do MariaDB operatory zbiorów SQL są naprawdę przydatne, ponieważ wiele aplikacji Oracle z nich korzysta, a jak widać, MariaDB można oszukać, aby działała tak, jak Oracle, która jest niestandardowa, ale nadal służy celowi. Ale domyślnie MariaDB działa tak, jak powinna i jest zgodna ze standardem SQL.

Wesołego SQL'a
/Karlsson


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak działa REVERSE() w MariaDB

  2. Projektowanie baz danych 101:Partycje w MySQL

  3. Jak ADD_MONTHS() działa w MariaDB

  4. Jak skonfigurować AppArmor dla systemów opartych na MySQL (MySQL/MariaDB Replication + Galera)

  5. Uruchamianie zapytań analizy Big Data przy użyciu SQL i Presto