Operátory množin jsou operátory SQL, které se zabývají kombinováním různých sad výsledků různými způsoby. Řekněme, že máte dva různé SELECT
Pokud chcete spojit do jediné výsledné sady, vstupují do hry operátory sady. MariaDB podporuje UNION
a UNION ALL
množinové operátory již dlouhou dobu, a to jsou zdaleka nejběžnější množinové operátory SQL.
Ale to předbíháme, dovolte mi nejprve vysvětlit operátory SQL sady, které máme, a jak fungují. Pokud to chcete vyzkoušet, můžete použít své stávající nasazení serveru MariaDB nebo to vyzkoušet v cloudové databázi MariaDB SkySQL.
UNION a UNION ALL
UNION
a UNION ALL
množinové operátory sčítají výsledek dvou nebo více množin výsledků. Začněme UNION ALL
a UNION
bude pak variantou UNION ALL
.
Pojďme se podívat, jak to vypadá v SQL. Předpokládejme, že provozujeme internetový obchod a že pro produkty, které prodáváme, máme zásoby. Nyní chceme vidět všechny produkty, které jsou na objednávku nebo jsou na skladě, dotaz na toto by vypadal asi takto:
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 inventar i JOIN products p ON i.prod_id =p.id;
Toto je v teorii množin UNION
sad produktů, které byly objednány, a sad produktů, které jsou na skladě. Což je teoreticky v pořádku, ale s výsledkem tohoto dotazu je problém. Problém je v tom, že produkt, který se objeví jak v objednávkách, tak v inventáři nebo na více místech v inventáři, se ve výstupu objeví více než jednou. Tento problém je důvodem UNION ALL
se příliš nepoužívá a místo toho UNION DISTINCT
(DISTINCT
je výchozí a lze jej ignorovat). Například:
SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN produkty p ON oi.prod_id =p.id UNION SELECT i.prod_id, p.prod_name Z inventáře i JOIN produkty p ON i.prod_id =p.id;S tímto dotazem je produkt, který je buď na objednávku nebo existuje v inventáři, uveden pouze jednou. Všimněte si, že když zde odstraňujeme duplikáty, porovnávají se hodnoty, takže dva řádky se stejnými hodnotami ve stejném sloupci jsou považovány za stejné, i když hodnoty pocházejí z různých tabulek nebo sloupců.
Abych byl upřímný, ve výše uvedeném dotazu není nic, co by se nedalo udělat běžným
SELECT
z produktů stůl a pár spojů. V některých ohledechUNION
může být snazší číst. Na druhou stranu, pokud chceme mít seznam produktů na objednávku nebo na skladě a také chceme vědět, který z nich to byl, pak by dotaz vypadal asi takto:SELECT 'Na objednávku', oi.prod_id, p.prod_name FROM order_items oi JOIN produkty p ON oi.prod_id =p.id UNION SELECT 'Inventory', i.prod_id, p.prod_name FROM inventar i JOIN products p ON i.prod_id =p.id;Zde je dotaz, který není snadné provést jednoduchým
SELECT
z produktů tabulky, protože se díváme na stejný řádek z tabulky produktů dvakrát (jednou pro order_items a jednou pro inventář ).Více operátorů sady SQL
S MariaDB Server 10.3 přišly dva nové operátory sady SQL, které byly z velké části představeny pro zlepšení kompatibility s Oracle, ale tyto operátory jsou užitečné samy o sobě. MariaDB Server 10.4 pak přidává možnost řídit prioritu nastavených operátorů. Na to se také podíváme. Bez možnosti ovládat prioritu operátorů nefungují nastavené operátory vždy tak, jak byste chtěli nebo očekávali.
Nové operátory množiny SQL jsou
INTERSECT
aKROMĚ
a jsou užitečné, zejména při používání analytiky. Také, ačkoliJOIN
Místo toho lze často použít s a další konstrukce, operátory množin SQL umožňují syntaxi SQL, která může být snadněji čitelná a pochopitelná. A pokud máte aplikace Oracle, které migrujete na MariaDB, užitečnost těchto operátorů je zřejmá.
Operátor množiny INTERSECT
INTERECT
operátor vrátí všechny položky, které existují ve dvou nebo více sadách, nebo v podmínkách SQL všechny řádky, které existují ve dvou sadách výsledků. V tomto případě se vytvoří průřez dvěma sadami položek. V podmínkách SQL to znamená, že jsou vráceny pouze řádky, které existují v obou sadách, takže pokud chci zkontrolovat, které produkty mám objednané a které jsou také v inventáři, dotaz může vypadat takto: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 Z inventáře i JOIN products p ON i.prod_id =p.id;Tento dotaz lze opět vytvořit pomocí
JOIN
na produktech tabulka, ale výše uvedený dotaz je o něco jasnější ohledně toho, čeho se snažíme dosáhnout.Operátor množiny EXCEPT
V případě
EXCEPT
operátora, chceme položky, které jsou v jedné ze sad, ale ne ve druhé. Pokud tedy znovu použijeme výše uvedený příklad, pokud chceme vidět produkty, které máme na objednávku, ale pro které nemáme zásoby, můžeme napsat dotaz takto:SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN produkty p ON oi.prod_id =p.id EXCEPT SELECT i.prod_id, p.prod_name Z inventáře i JOIN produkty p ON i.prod_id =p.id;Opět existují jiné způsoby psaní tohoto konkrétního dotazu, ale pro jiné, pokročilejší dotazy, kdy kombinujeme data ze dvou různých tabulek, tomu tak není.
Kombinace více operátorů sady
Pokud je to užitečné, můžete kombinovat více než 2 operátory sady. Podívejme se například, zda můžeme najít produkty, které jsou na objednávku a byly dodány nebo jsou skladem. SQL pro toto by vypadalo nějak takto:
SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN produkty p ON oi.prod_id =p.id INTERSECT SELECT d.prod_id, p.prod_name FROM dodávky d JOIN produkty p ON d.prod_id =p.id UNION SELECT i.prod_id, p.prod_name Z inventáře i PŘIPOJIT produkty p ON i.prod_id =p.id;Abych to vyjádřil srozumitelně, jde o to, že nejprve zkontroluji, které produkty jsou na objednávku a které byly doručeny, a poté zkombinuji tuto sadu produktů se všemi produkty v inventáři. Žádný produkt, který není v sadě výsledků, není v inventáři ale může být na objednávku nebo může být doručeno, ale ne obojí.
Ale teď to vyjádřeme jinak a uvidíme, co se stane. Chci seznam všech produktů, které jsou skladem nebo byly dodány a jsou na objednávku. SQL by pak bylo něco takového, podobné výše uvedenému SQL, ale mírně odlišné:
SELECT i.prod_id, p.prod_name FROM inventar 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 OD dodávek d JOIN produkty p ON d.prod_id =p.id;Jak si to potom vykládáte? Uvádíte produkty, které jsou skladem a které jsou na objednávku, a produkty, které jsou dodávány? Takhle to vypadá, že? Jde jen o to
INTERSEC> (a
EXCEPT
na to přijde) má přednost přesUNION
. Dva příkazy SQL produkují stejnou sadu výsledků, alespoň v MariaDB, a takto by měly věci fungovat podle standardu SQL. Ale existuje výjimka, Oracle.Jak to funguje v Oracle
Oracle má všechny čtyři operátory sady SQL (
UNION
,SPOJETE VŠECHNY
,INTERECT
aKROMĚ
) dlouhou dobu, dávno předtím, než byly standardizovány, takže jejich implementace je trochu jiná. Zkusme s výše uvedenými tabulkami a vložíme do nich nějaká data. Data jsou velmi jednoduchá a odrážejí nepříliš úspěšnou společnost, ale fungují jako příklad a my zde zobrazujeme pouze příslušné sloupce.
Tabulka | produkty | položky_objednávky | inventář | dodávky | ||
Sloupec | prod_id | název_produktu | id_objednávky | prod_id | prod_id | prod_id |
Data | 1 | Modrá váza | 1 | 1 | 1 | 2 |
2 | Váza červená | 2 | 1 | 2 | 3 | |
3 | Červený koberec | 2 | 3 |
Když jsou data na místě, podívejme se znovu na poslední příkaz SQL výše. Existuje funkce, která vám umožňuje řídit prioritu, a to je použití závorek nebo závorek (zavedeno v MariaDB 10.4, viz https://jira.mariadb.org/browse/MDEV-11953), a jejich použití k ilustraci co se děje, bude výpis vypadat takto:
MariaDB> SELECT i.prod_id, p.prod_name -> Z inventáře i JOIN produkty 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 delivery d JOIN products p ON d.prod_id =p.id); +---------+------------+ | prod_id | název_produktu | +---------+------------+ | 1 | Váza modrá | | 2 | Váza červená | | 3 | Koberec červený | +---------+------------+ 3 řádky v sadě (0,001 s)
Nyní použijme stejnou techniku k vynucení toho, aby tři součásti dotazu fungovaly v přísném pořadí:
MariaDB> (SELECT i.prod_id, p.prod_name -> Z inventáře i JOIN produkty p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> FROM order_items oi JOIN produkty p ON oi.prod_id =p.id) -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FROM delivery d JOIN products p ON d.prod_id =p.id; +---------+------------+ | prod_id | název_produktu | +---------+------------+ | 2 | Váza červená | | 3 | Koberec červený | +---------+------------+ 2 řádky v sadě (0,001 s)
A nakonec bez závorek:
MariaDB [test]> SELECT i.prod_id, p.prod_name -> Z inventáře i JOIN produkty p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> FROM order_items oi JOIN produkty p ON oi.prod_id =p.id -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FROM delivery d JOIN products p ON d.prod_id =p.id; +---------+------------+ | prod_id | název_produktu | +---------+------------+ | 1 | Váza modrá | | 2 | Váza červená | | 3 | Koberec červený | +---------+------------+ 3 řádky v sadě (0,001 s)
Vidíme, že MariaDB podle standardu předpokládala, že INTERSECT
má přednost před UNION
. Což nás přivádí k Oracle. Zkusme výše uvedený SQL v Oracle pomocí sqlplus:
SQL> SELECT i.prod_id, p.prod_name 2 Z inventáře i JOIN produkty 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 delivery d JOIN products p ON d.prod_id =p.id; PROD_ID PROD_NAME ---------- ------------------------------ 2 váza červená 3 červený koberecCo se tady děje, ptáš se? Oracle nedodržuje standard. Různé množinové operátory jsou považovány za rovnocenné a žádný nemá přednost před druhým. To je problém, když migrujete aplikace z Oracle do MariaDB, a co je horší, je, že tento rozdíl je trochu těžké najít. Nedojde k žádné chybě a data se vrátí a v mnoha případech se vrátí správná data. Ale v některých případech, kdy jsou data jako v našem příkladu výše, jsou vrácena nesprávná data, což je problém.
Vliv na migraci dat
Jak se s tím tedy vypořádáme, pokud migrujeme aplikaci z Oracle do MariaDB? Existuje několik možností:
- Přepište aplikaci tak, aby předpokládala, že data vrácená z dotazu, jako je tento, jsou v souladu se standardem SQL a MariaDB.
- Přepište příkazy SQL pomocí hranatých závorek tak, aby MariaDB vracela stejná data jako Oracle
- Nebo, a to je nejchytřejší způsob, použijte MariaDB
SQL_MODE=Oracle
nastavení.
Abychom mohli fungovat jako poslední a nejchytřejší způsob, musíme běžet s MariaDB 10.3.7 nebo vyšší (toto bylo skutečně navrženo vaším v https://jira.mariadb.org/browse/MDEV-13695). Podívejme se, jak to funguje. Porovnejte výsledek tohoto SELECT
s výše uvedeným Oracle (který poskytuje stejný výsledek) a tím z MariaDB nad tímto (který nikoli):
MariaDB> nastavit SQL_MODE=Oracle; Dotaz je v pořádku, ovlivněno 0 řádků (0,001 s) MariaDB> SELECT i.prod_id, p.prod_name -> Z inventáře i JOIN produkty 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 delivery d JOIN products p ON d.prod_id =p.id; +---------+------------+ | prod_id | název_produktu | +---------+------------+ | 2 | Váza červená | | 3 | Koberec červený | +---------+------------+ 2 řádky v sadě (0,002 s)
Jak můžete vidět, když SQL_MODE
je nastaven na Oracle
MariaDB se opravdu chová jako Oracle. Toto není jediná věc, kterou SQL_MODE=Oracle
samozřejmě ano, ale je to jedna z méně známých oblastí.
Závěr
Operátory sady INTERECT
a KROMĚ
se tolik nepoužívají, i když se tu a tam objeví a nějaké využití pro ně existuje. Příklady na tomto blogu jsou spíše pro ilustraci toho, jak tyto operátory fungují, než pro ukázku jejich opravdu dobrého využití. Asi jsou lepší příklady. Když však migrujete z Oracle na MariaDB, operátory sady SQL jsou opravdu užitečné, protože je používá mnoho aplikací Oracle, a jak je vidět, MariaDB může být oklamána, aby fungovala stejně jako Oracle, který není standardní, ale stále slouží svému účelu. Ale ve výchozím nastavení samozřejmě MariaDB funguje tak, jak má, a řídí se standardem SQL.
Šťastný SQL’ing
/Karlsson