SQL pro pokročilé - 1.díl
Při uvedení článku o SQL Injection jsem zjistil, že velmi mnoho čtenářů by se o SQL rádo dozvědelo více. Proto jsem se rozhodl napsat pár článků o pokročilých technikách SQL, které se v učebnicích příliš nepublikují, a ukázat pár vzorových příkladů.
Dnešním tématem budiž dočasné tabulky a triky s nimi.
Dočasná tabulka, neboli TEMPORARY TABLE, se až na jednu podstatnou vlastnost neliší od normální tabulky. Tou vlastností je, jak už název napovídá, dočasnost. Tabulka je po ukončení spojení mezi PHP a MySQL databází nenávratně smazána. V praxi to znamená, že tabulka existuje po dobu, kdy máme platný identifikátor na MySQL spojení. Tento identifikátor vrací funkce Mysql_connect.
Uvedu příklad z praxe:
Mám dvě tabulky rozdílné struktury obsahující data o předpovědi počasí pro lyžařská střediska. Potřebuji vytvořit seznam TOP 10 středisek s nejvyšší sněhovou pokrývkou.
Vypadalo by to asi takto:
( SELECT p.`vyska_snehu`,s.`stredisko` FROM `predpoved1` AS p LEFT JOIN `stredisko` AS s ON s.`id_stredisko` = p.`id_stredisko` WHERE (TO_DAYS(NOW())=TO_DAYS(`datum`)) GROUP BY p.`id_stredisko` ) UNION ( SELECT p.`vyska_snehu`,s.`stredisko` FROM `predpoved2` AS p LEFT JOIN `stredisko` AS s ON s.`id_stredisko` = p.`id_stredisko` WHERE (TO_DAYS(NOW())=TO_DAYS(`datum`)) GROUP BY p.`id_stredisko` ) ORDER BY `vyska_snehu` DESC LIMIT 10
Jak vidíte, načteme data z tabulky předpovědi a seskupíme řádky pomocí id_střediska. Tím získáme jeden záznam pro každé středisko - vždy ten nejnovější. Tento select složíme s druhým selectem, který musí mít stejnou strukturu výstupu.
Tento kód je vcelku dobrý, bohužel má ale jednu chybu: Přestane správně fungovat ve chvíli, kdy se v obou tabulkách předpovědí objeví totéž středisko. A pokud možno ještě budeme dostávat trochu odlišná data do každé předpovědi. Pak se nám může stát, že v seznamu TOP 10 středisek bude některé středisko dvakrát...
Jak to řešit? Pomocí dočasných tabulek - TEMPORARY TABLE.
Začneme vytvořením:
CREATE TEMPORARY TABLE `top` (vyska_snehu INT, stredisko VARCHAR(100))
Jak vidíte, je to téměř totožné jako vytváření běžných tabulek.
Následuje mírné přepsání selectů:
INSERT INTO `top` ( SELECT p.`vyska_snehu`,s.`stredisko` FROM `predpoved1` AS p LEFT JOIN `stredisko` AS s ON s.`id_stredisko` = p.`id_stredisko` WHERE (TO_DAYS(NOW())=TO_DAYS(`datum`)) GROUP BY p.`id_stredisko` ) UNION ( SELECT p.`vyska_snehu`,s.`stredisko` FROM `predpoved2` AS p LEFT JOIN `stredisko` AS s ON s.`id_stredisko` = p.`id_stredisko` WHERE (TO_DAYS(NOW())=TO_DAYS(`datum`)) GROUP BY p.`id_stredisko` ) ORDER BY `vyska_snehu` DESC
Opět žádná novinka, pouze vkládáme výsledek selectů do naší dočasné tabulky. Jediný rozdíl je odstranění LIMIT na konci dotazu.
A nakonec přijde select z dočasné tabulky:
SELECT * FROM `top` GROUP BY stredisko ORDER BY 1 DESC LIMIT 10
Tím je problém vyřešen. Data vložíme do dočasné tabulky a tam je poté opět seskupíme podle středisek. Žádné duplicity už tedy nevzniknou...