Skip to main content

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á:

  1. názvy stĺpcov sa preberajú z prvého dopytu, takže aliasy (AS) dávame len do prvého dopytu;
  2. zoraďovanie (ORDER BY) patrí až na koniec, za posledný dopyt;
  3. 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 COALESCE vž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 JOIN vyberie 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ť.