4.3 Množinové operácie
Zlúčenie dopytov zjednotením
Sú situácie, keď potrebujeme zlúčiť výsledky viacerých dopytov. Nie je tomu tak dávno, keď sme sa naučili vonkajšie spájanie tabuliek (OUTER JOIN) a v prípade MySQL/MariaDB nám chýbalo obojstranné plné spojenie (FULL OUTER JOIN). Môžeme však zlúčiť výsledky ľavého a pravého spojenia - k tomu je potrebné zjednotenie pomocou operátora UNION. Zlúči výsledky (všetky riadky) viacerých SQL dopytov:
{dopyt 1} UNION {dopyt 2}
Funguje úplne rovnako ako operácia zjednotenia množín, ktorú poznáme z matematiky. Ak sa niektorý riadok nachádza v oboch zjednocovaných častiach, v zjednotenom výsledku bude len raz.
Pri eliminácií duplicít sa zohľadňujú len tie stĺpce, ktoré sme vybrali v klauzule SELECT. Nejde teda o úplnú zhodu všetkých stĺpcov v pôvodných tabuľkách. V podstate výsledok zodpovedá využitiu SELECT DISTINCT.
Pri zlučovaní dopytov musíme dodržať nasledujúce pravidlá:
- názvy stĺpcov sa preberajú z prvého dopytu, takže aliasy (
AS) dávame len do prvého dopytu; - zoraďovanie (
ORDER BY) patrí až na koniec, za posledný dopyt; - zo zlučovaných tabuliek musíme vyberať rovnaký počet stĺpcov s rovnakým dátovým typom.
Ukážka nahradenia FULL OUTER JOIN v MariaDB:
-- prehľad všetkých oddelení a všetkých zamestnancov
SELECT
COALESCE(d.department_name, '-') AS oddelenie,
CONCAT(e.last_name, ' ', e.first_name) AS zamestnanec
FROM employees e
LEFT OUTER JOIN departments d USING(department_id)
UNION
SELECT
d.department_name,
COALESCE(CONCAT(e.last_name, ' ', e.first_name), '-')
FROM employees e
RIGHT OUTER JOIN departments d USING(department_id)
ORDER BY
oddelenie, zamestnanec;
Prečo je vo vyššie uvedenom dopyte ošetrenie NULL hodnoty cez
COALESCEvždy len v jednom dopyte?Prvý dopyt realizuje
LEFT OUTER JOIN, teda vo výsledkoch bude každý jeden zamestnanec - aj taký, ktorý nie je zaradený do žiadneho oddelenia, preto je potrebné riešiť neexistujúce oddelenie. Nebude tu však žiadne oddelenie bez zamestnanca.V druhom dopyte je situácia presne opačná -
RIGHT OUTER JOINvyberie všetky oddelenie, teda aj tie, v ktorých nik nepracuje. Preto riešime neexistujúceho zamestnanca.
Zlučovanie rôznych tabuliek
Zjednotenie môžeme samozrejme využiť aj pri zlučovaní údajov z rôznych tabuliek - napríklad pri zlučovaní aktuálnych a historických údajov. Musíme si však dať pozor na počet stĺpcov a ich dátový typ - musia byť rovnaké.
Môžeme teda zlúčiť len tabuľky s rovnakými stĺpcami?
Nie, nejde o to, aké stĺpce majú kompletné tabuľky, ale o to, ktoré stĺpce si vyberáme. Tabuľky môžeme dokonca zlúčiť aj v prípade, že niektorá z nich neobsahuje niektoré vyberané stĺpce - tie však musíme nahradiť napríklad hodnotou NULL (alebo akoukoľvek inou vyhovujúcou a do kontextu zapadajúcou hodnotou).
-- prehľad zamestnancov a ich zamestnaní, vrátane predošlých
SELECT employee_id, job_id, end_date
FROM job_history
UNION
SELECT employee_id, job_id, NULL
FROM employees
ORDER BY employee_id, end_date;
Zjednotenie so zachovaním duplicity
Pokiaľ by sme chceli zachovať duplicitné riadky, môžeme využiť operátor UNION ALL:
{dopyt 1} UNION ALL {dopyt 2}
Práca s prienikom
Možné je tiež získať tie riadky, ktoré sa nachádzajú vo výsledkoch oboch dopytov (ide o prienik), vtedy využijeme operátor prieniku INTERSECT:
{dopyt 1} INTERSECT {dopyt 2}
Odstránenie prieniku
Niekedy príde vhod aj operátor EXCEPT (PostgreSQL a MariaDB) alebo MINUS (Oracle SQL):
{dopyt 1} EXCEPT {dopyt 2}
Vráti výsledky dopytu 1, z ktorých odstráni tie riadky, ktoré sú aj medzi výsledkami dopytu 2.
-- prehľad zamestnancov, ktorí nezmenili svoje zamestnanie
SELECT
CONCAT(last_name, ' ', first_name, ' - ', employee_id) AS zamestnanec
FROM employees
EXCEPT
SELECT
CONCAT(last_name, ' ', first_name, ' - ', employee_id)
FROM job_history
JOIN employees USING(employee_id)
ORDER BY zamestnanec;
Množinové operácie potrebujeme v praxi len zriedka, ale je dobré o nich aspoň vedieť.