SQL cvičení - Výběr aktualních komentářů
1. června 2007
nezařazeno
Mějme modelový projekt, předpokládejme, že máme články a k těm je možno přidávat komentáře. Součástí aplikace je zároveň funkce, která vrací poslední tři komentované články – nikoliv poslední 3 komentáře, ale poslední články.
Jak na to SQL příkazem, abychom nemuseli třídit data externě?
V tabulce article máme 3 články:
id | name |
1 | Nový článek |
2 | Jiný článek |
3 | Další článek |
V tabulce comment máme 4 komentáře z různých dní:
id | id_article | date | author |
1 | 3 | 2007-03-02 | xom |
2 | 1 | 2007-03-03 | myff |
3 | 2 | 2007-03-04 | xom |
4 | 2 | 2007-03-05 | lada |
Nelezl jsem dvě řešení, přesně nevím, které z nich je optimálnější, možná mi pomůžete v komentářích nebo vás napadne další možnost řešení.
První řešení je následující:
Použijeme funkci MAX
, která nám vybere nejnovější datum a zároveň výsledek zgroupujeme podle názvu článku a srovnáme podle data.
SELECT
MAX(comment.date) AS date,
article.name
FROM
article LEFT JOIN comment ON article.id = comment.id_article
GROUP BY
article.name
ORDER BY
date DESC
LIMIT
3
2007-03-05 | Jiný článek |
2007-03-03 | Nový článek |
2007-03-02 | Další článek |
No jo, ale co když potřebujeme vybrat více položek z komentáře, pokud bychom chtěli primární klíč – id, tak není problém, jelikož se předpokládá, že bude mít nejvyšší číslo klíče pole, které má zároveň nejnovější – nejvyšší datum.
Funkci MAX(comment.id)
musíme uvést, jelikož chceme filtrovat výsledky dle tabulky article
, pokud bychom selectovali čistý comment.id
, tak bychom ho museli zahrnout do definice GROUP BY
. Tím by se nám výsledku znehodnotili na něco podobného:
2007-03-05 | Jiný článek | 4 |
2007-03-04 | Jiný článek | 3 |
2007-03-03 | Nový článek | 2 |
S použítím MAX(comment.id)
a nezahrnutím do GROUP BY
je výsledek optimální:
2007-03-05 | Jiný článek | 4 |
2007-03-03 | Nový článek | 2 |
2007-03-02 | Další článek | 1 |
Problém by nastal, pokud bychom chtěli vybrat jméno komentujícího comment.author
, při použití funkce MAX
by databáze přiřazovala k výsledku jméno, které by nemuselo patřit k vracenému datu komentáře.
Dostali jsme se nejspíš do slepé uličky v modifikaci této metody.
V SQL existuje deklarace DISTINCT
, která odstraňuje duplicitní hodnoty. V příkladu:
SELECT DISTINCT
id_article
FROM
comment
Tento select by nám vrátil
Jenže při použití na náš příklad by nespatřoval žádný výsledek jako duplicitní, protože vybíráme hodnoty, které se nikdy nepřekrývají úplně.
Postgre SQL má funkci DISTINCT ON
, která umožňuje filtrování duplicit přes definované sloupce.
Jasnou volbou je, že nechceme mít duplicitní hodnotu id_article
, která je vzdáleným klíčem tabulky article
.
SELECT
DISTINCT ON (comment.id_article)
comment.date,
article.name,
comment.author
FROM
article JOIN comment ON article.id = comment.id_article
ORDER BY
comment.id_article,
comment.date DESC
LIMIT
3
Nevýhodou tohoto řešení je nutnost uvést parametr funkce DISTINCT ON
na prvním místě v deklaraci ORDER BY
, což znamená, že máme ve výsledku 3 poslední komentáře 3 různých článků, ale neseřazených dle data.
2007-03-03 | Nový článek | myff |
2007-03-05 | Jiný článek | lada |
2007-03-02 | Další článek | xom |
Seřazení dle data jsem provedl vnořeným selectem..
SELECT
*
FROM (
SELECT
DISTINCT ON (comment.id_article)
comment.date,
article.name,
comment.author
FROM
article JOIN comment ON article.id = comment.id_article
ORDER BY
comment.id_article,
comment.date DESC
LIMIT 3
) AS tabulka
ORDER BY
date DESC
LIMIT
3
A výsledek je:
2007-03-05 | Jiný článek | lada |
2007-03-03 | Nový článek | myff |
2007-03-02 | Další článek | xom |
Vybrali jsme z databáze 3 nejnovější příspěvky ke 3 různým článkům v pořadí od nejnověji přidaného.
Znáte nějaký lepší způsob? Komentáře jsou k dispozici..
Stanislav Duben 1.6.07 10:48
Tohle je obojí zbytečně složité. Mnohem jednodušší cesta z hlediska zatížení DB serveru je:
SELECT distinct TOP 3 article.name FROM article LEFT JOIN comment ON article.id= comment.id_article ORDER BY date DESC
resp.
SELECT distinct article.name FROM article LEFT JOIN comment ON article.id= comment.id_article ORDER BY date DESC LIMIT 3