Slovník | Vyhledávání | Mapa webu
 
Základy informatiky pro biologyDatabázové systémy v biomedicíně Analytické a statistické funkce SQL Funkce pro určení pořadí řádků ve výsledku - Ranking function

Logo Matematická biologie

Funkce pro určení pořadí řádků ve výsledku - Ranking function

Velmi často potřebujeme v praxi stanovit pořadí záznamů ve výsledku. Pro setřídění výsledků nám standard SQL nabízí klíčové slovo ORDER BY umísťované na konec SELECT dotazu. Očíslovat výsledek můžeme v prostředí ORACLE pomocí pseudosloupce ROWNUM. 

     SELECT ROWNUM poradi, jmeno, prijmeni FROM student

Pseudosloupec ROWNUM přiřazuje číslo výsledným řádkům. Bohužel přiřazení probíhá ještě před finálním setříděním podle výrazu za ORDER BY, a proto následujícím způsobem pořadí studentů dle abecedy nezískáme:

   SELECT ROWNUM poradi, jmeno, prijmeni FROM student 
            ORDER BY prijmeni, jmeno

Pokud chceme číslovat až setříděný seznam, musíme dotaz zanořit:

   SELECT ROWNUM poradi, jmeno, prijmeni
            FROM (
                        SELECT jmeno, prijmeni FROM student 
                        ORDER BY prijmeni, jmeno
            )

Na další omezení narazíme, pokud potřebujeme vybrat záznamy podle pořadí, například třetího studenta dle abecedy. V takovém případě musíme podmínku vložit až do třetí vrstvy nadřazeného dotazu. Nelze napsat:

     SELECT ROWNUM poradi, jmeno, prijmeni FROM student 
            WHERE rownum  = 3
            ORDER BY prijmeni, jmeno 

ani

     SELECT * FROM (
                        SELECT ROWNUM poradi, jmeno, prijmeni FROM student 
                        ORDER BY prijmeni, jmeno 
            ) WHERE poradi = 3

 

Důvodem je, že databáze pseudosloupec ROWNUM  nastavuje, až když řádek splní podmínku za WHERE,  první řádek je vždy ROWNUM = 1, a protože tento řádek nesplňuje podmínku ROWNUM = 3, na výstup se nedostane. Databáze  tak projde všechny záznamy v tabulce, ale žádný podmínku nesplní. Druhý problém je, že ROWNUM se nastavuje ještě před setříděním přes ORDER BY. Databáze vybere tři řádky z tabulky, které teprve následně setřídí.  Výsledkem druhého dotazu je třetí řádek, který databáze našla, ale nikoliv nutně třetí dle  příjmení studenta. Náhoda často způsobí, že při letmém testování se může zdát, že dotaz funguje, teprve při hlubší kontrole se ukáže, že jde o chybu. 

Správně je:

     SELECT * FROM (
                        SELECT ROWNUM poradi, jmeno, prijmeni FROM (
                                    SELECT jmeno, prijmeni FROM student 
                                    ORDER BY prijmeni, jmeno 
                        )
            )

     WHERE poradi = 3

Tedy nejprve setřídit, pak očíslovat a teprve ve třetí vrstvě filtrovat. Jak je vidět tato konstrukce je dost komplikovaná a navíc neřeší variantu, kdy máme studenty se stejným příjmením a tyto bychom chtěli označit stejným pořadovým číslem. Proto je pro tento typ úloh výhodnější použít některou z tzv. ranking function. ORACLE i POSTGRESQL nabízí tři funkce:

  • RANK()
  • DENSE_RANK()
  • ROW_NUMBER() 

Funkce se liší způsobem, jakým řádky číslují v případě, kdy se objeví ve výsledku stejné, dle pravidel třídění rovnocenné hodnoty. Funkce RANK() a DENSE_RANK() číslují stejné hodnoty stejným pořadovým číslem. Funkce ROW_NUMBER přiděluje každému řádku unikátní číslo, u shodných hodnot rozhoduje o pořadí náhoda. Rozdíl mezi RANK a DENSE_RANK spočívá v tom, jaké pořadové číslo následuje po sérii shodných řádků. Funkce DENSE_RANK pokračuje nepřerušenou číselnou řadou, funkce RANK přeskočí odpovídající počet čísel. Vše osvětlí následující tabulka.

Tab. 1: Srovnání výsledků funkcí RANK, DENSE_RANK a ROW_NUMBER

Příjmení

RANK()

DENSE_RANK()

ROW_NUMBER()

Mladý

1

1

1

Novák

2

2

2

Novák

2

2

3

Novák

2

2

4

Starý

5

3

5

Syntaxe všech tří funkcí je následující:

     RANK () OVER (ORDER BY sloupec)

Za názvem funkce následují prázdné závorky, dále klíčové slovo OVER, za kterým následuje definice třídění, podle kterého chceme určovat pořadí řádků. Definice za ORDER BY v ranking funkci nemá žádnou vazbu k ORDER BY klauzuli na konci celého SQL dotazu. Můžeme určovat pořadí zcela nezávisle na finálním setřídění výsledku. 

V případě, kdy potřebujeme stanovit pořadí v jednotlivých kategoriích výsledku, lze  výraz OVER dále rozšířit o klauzuli PARTITION BY. Například pokud chceme číslovat pořadí vyšetření jednotlivých pacientů podle data vyšetření. 

     SELECT patient_id, datum_vysetreni,
            RANK() OVER (PARTITION BY patient_id ORDER BY datum_vysetreni) poradi
            FROM vysetreni

Tab. 2: Číslování výsledku s klauzulí PARTITION BY

Patient_id

datum_vysetreni

poradi

PAT_1

12. 5. 2012

1

PAT_2

14. 5. 2012

1

PAT_2

23. 9. 2012

2

PAT_2

4. 2. 2013

3

PAT_3

15. 3. 2012

1

Ranking funkce lze v dotazu umístit mezi klíčová slova SELECT a FROM nebo jako výraz pro třídění za závěrečné ORDER BY. Naopak nelze je umístit do podmínky za WHERE či HAVING. Pokud chceme pomocí nich definovat podmínku, musíme použít vnořený SQL dotaz.

 
vytvořil Institut biostatistiky a analýz Lékařské fakulty Masarykovy univerzity