Vlákno názorů k článku Buďte moderní (v SQL) od Michal M - K funkcii rank() - správnejšia by myslím bola...

  • Článek je starý, nové názory již nelze přidávat.
  • 21. 6. 2016 17:00

    Michal M (neregistrovaný)

    K funkcii rank() - správnejšia by myslím bola funkcia row_number().
    V testovacích dátach možno bude každá obec mať unikátny počet obyvateľov. Vtedy rank() bude dávať rovnaké poradia ako row_number(), ale pri iných testovacích dátach by rank() mohol vybrať viac obcí, než tri. Možno to niekomu znie ako zbytočné "rýpanie". Ale tým menej zbehlým v SQL window funkciách to takto môže (intuitívne) pripadať, že rank() im poradie určuje unikátne, a môžu byť nechcene uvedený do omylu.

  • 21. 6. 2016 17:04

    Michal M (neregistrovaný)

    Na konci samozrejme oprava hrubky (nejde mi to editovať): ... ovedení do omylu (keď už som použil množné číslo).

  • 22. 6. 2016 7:42

    Pavel Stěhule

    Pro otázku 3 největší obce z okresu je RANK korektní. Nicméně tuto správnost v řešeních s LIMITEM nedokáži dosáhnout.

  • 22. 6. 2016 9:16

    NooN (neregistrovaný)

    Nemate pravdu, v pripade ze su v okrese napr. 4 obce s rovnakym poctom ktory je zaroven najvacsii v okrese, tak vam vyberie 4 zaznamy (namiesto 3), pretoze rank pre vsetky 4 obce je 1.

  • 22. 6. 2016 17:06

    Pavel Stěhule

    To také považuji za správné (sémanticky) - jak byste preferoval jednu obec před druhou. Otázka druhá je vlastní zobrazení, kde by to muselo být ošetřeno dynamicky.

  • 23. 6. 2016 8:45

    NooN (neregistrovaný)

    A ide o to ze vysledny pocet moze byt dolezity, ze v ziadnom pripade ich nemoze byt viac ako 3.
    Mozu byt dalsie kriteria, napr. skorsi datum dosiahnutia daného poctu obyvatelov a v tom pripade rank nepomaha. Takze ako som pisal, je to uhol pohladu a brat rank ako jedine spravne riesenie je nekorektne.

  • 23. 6. 2016 12:32

    Michal M (neregistrovaný)

    Najprv k zadaniu úlohy:
    3 najväčšie obce - je veľmi jasné - budú to 3 obce, nie 4, nie 5, ale práve 3.
    Ak tam podľa počtu obyvateľov bude viac rovnakých, tu zadanie neupresňuje, ako medzi nimi vybrať. V príklade, ktorý som včera priložil v inom komentári som navrhol 2 postupy - môžeme to nechať vybrať "náhodne" (t.j. priorita medzi rovnako veľkými obcami nie je zaručená, resp. explicitne určená), čo je stále DOBRÉ riešenie podľa uvedeného zadania. Tiež som tam uviedol LEPŠIE riešenie - že tú prioritu som si určil explicitne, podľa id, a id som plnil abecedne.
    Stále však platí, že rank, tak ako je v článku použitý, je ZLÉ riešenie.

    Takže pre čitateľov - buďte moderní, ale zároveň riešte správne a v lepšom prípade aj explicitne i také zadania, ktoré sú neúplné. Alebo inými slovami, nedostatky zadania nie sú ospravedlnením nesprávnosti alebo nedostatkov riešenia.

    Ďalej - rank, tak ako je použitý v článku - rieši iné zadanie - aké sú obce s troma najväčšími počtami obyvateľov pre každý okres. To, že sa pri unikátnych počtoch obyvateľov (čo asi platilo pri zvolenej testovacej množine v článku) zhodujú výsledky týchto dvoch zadaní, stále nemení nič na tom, že sú to rozdielne zadania.

    No a nakoniec sa musím priznať aj k tomu, že som včera netestoval ostatné riešenia. V článku ma zaujal iba rank, keďže window funkcie používam často a veľmi rád, t.j. som moderný :) . Hlavne ma ale zaujala jeho nesprávnosť, preto som sa venoval iba tomuto riešeniu.

    Ako som už uviedol, jedno zadanie je uvedené, a iné zadanie obhajujete pri ranku. Tak som si prešiel (pre mnou zvolenú testovaciu množinu 15 obcí rozdelených do 3 okresov, včera priložený kód) aj ostatné riešenia.
    Nielen rank je zlý, je ich tam viac. Niekedy limitom obmedzujete počty obyvateľov (t.j. 3 najväčšie unikátne počty, potenciálne oveľa viac obcí), inokedy zase limitujete skutočne obce (t.j. 3 obce, potenciálne menej unikátnych počtov obyvateľov).

    A najhoršie je na tom rekurzívny dotaz, kde sa obce s neunikátnym počtom obyvateľov medzi sebou roznásobujú, takže v mojej vzorke 15 obcí je výsledkom množina 98 obcí. Síce je to asi jasné, ale predsa - porovnávať výkony dotazov, ktoré ani nespočítajú správny výsledok, nemá význam.

  • 23. 6. 2016 18:45

    Pavel Stěhule

    Tady se neshodneme. Představte si, že pět sportovců na prvním místě má stejný čas. Pravděpodobně by neocenili, kdybyste první cenu dal pouze prvnímu podle abecedy. V reálném životě tyto úlohy mají své corner cases, které řešíte různě, podle situace.

  • 24. 6. 2016 8:08

    Pavel (neregistrovaný)

    Nejen ve sportu, ale i jinde (třeba ve volbách) je v případě jinak nerozhodnutelné situace použit los. Vždyť třeba pokud by bylo zadání, že máte pro další postup vybrat dva nejlepší týmy ze skupiny, tak musíte vybrat dva i kdyby čert na koze jezdil (tedy i kdyby například všechny zápasy ve skupině skončily 0:0), protože pokud vyberete jiný počet, není možné sestavit finálové zápasy.

    Pokud dostanu zadání vybrat tři největší obce, tak buďto to vyřeším tak, že vyberu vždy tři přesně specifikovaným způsobem, nebo to hodím zadavateli na hlavu, ať to upřesní. Ale nikdy nemůžu navrhnout řešení, které v ¨"corner cases" dělá nesmysly.

  • 27. 6. 2016 17:26

    Michal M (neregistrovaný)

    V poriadku, pochopenie resp. slovnú formuláciu zadania vnímame inak.
    Čo s tým, že rôzne sql dotazy v článku, ktoré riešia rovnaké zadanie (či už zadanie chápeme jedným alebo druhým spôsobom) produkujú navzájom líšiace sa výsledky ?

  • 29. 6. 2016 7:34

    Pavel Stěhule

    Pokud máte nejednoznačnost v zadání, tak samozřejmě, výsledky mohou být v těch hraničních případech jiné. Ne, že by to nešlo - asi ve všech případech by bylo možné hraniční případy definovat, a definovat i to, co považujeme za řešení - a bylo by možné ty dotazy dotlačit do stavu, kdy budou produkovat stejné výsledky. U CTE by to asi dalo více práce. Pokud bych používal klauzuli LIMIT, tak bych musel respektovat chování, a to správné řešení nadefinovat tak, aby bylo adekvátní použití LIMITu, nebo bych nemohl LIMIT použít.

    Vůbec mi ale nešlo v tech příkladech, abych dostal vždy stejné výsledky. V těch řešeních je signifikantní průnik, ale nejsou ekvivalentní - liší se chováním, možnostmi, implementací. Jako vývojář potřebuji znát, co největší množství variací, abych se mohl rozhodnout pro jedno konkrétní, buďto na základě výkonu nebo na základě chování v hraničních případech.

    Když programuji, tak vždy pracuji s modelem reality - vždy dochází k určitému zkreslení, zjednodušení. U databází (a u výkonnostně náročných úloh) může být nezbytné být pragmatikem - a realitu ohnout o něco více, aby aplikace byla použitelná (udržovatelná). Např. při optimalizacích mám na výběr mezi deterministickou a stochastickou (GA) optimalizací. Všechno ovšem záleží na kontextu.

  • 22. 6. 2016 15:07

    Michal M (neregistrovaný)

    Skúsil som pripraviť príklad na demonštráciu toho, ako som to myslel.
    Príklad je zjednodušený, ale ilustruje pointu:

    create schema test_obce;
    
    -- drop table test_obce.okresy;
    create table test_obce.okresy
    (
      id varchar(6) primary key not null,
      nazev varchar(40) not null
    );
    
    -- drop table test_obce.obce;
    create table test_obce.obce
    (
      id integer not null primary key,
      okres_id varchar(6) not null,
      nazev varchar(40) not null,
      pocet_obyvatel integer not null
    );
    
    insert into test_obce.okresy values
      ('CZ0201', 'Benešov'),
      ('CZ0202', 'Beroun' ),
      ('CZ0203', 'Kladno' )
    ;
    
    insert into test_obce.obce values
      ( 1, 'CZ0201', 'Benešov',     10000),
      ( 2, 'CZ0201', 'Bernartice',  10000),
      ( 3, 'CZ0201', 'Bílkovice',   10000),
      ( 4, 'CZ0201', 'Blažejovice', 20000),
      ( 5, 'CZ0201', 'Borovnice',   30000),
      --
      ( 6, 'CZ0202', 'Bavoryně',    10000),
      ( 7, 'CZ0202', 'Beroun',      20000),
      ( 8, 'CZ0202', 'Běštín',      20000),
      ( 9, 'CZ0202', 'Broumy',      30000),
      (10, 'CZ0202', 'Březová',     30000),
      --
      (11, 'CZ0203', 'Běleč',       30000),
      (12, 'CZ0203', 'Běloky',      30000),
      (13, 'CZ0203', 'Beřovice',    30000),
      (14, 'CZ0203', 'Bílichov',    30000),
      (15, 'CZ0203', 'Blevice',     30000)
    ;
    
    -- porovnanie window funkcii:
    select *,
      -- rank moze opakovat rovnake poradie pre lubovolny pocet riadkov:
      rank()       over (partition by okres_id order by pocet_obyvatel desc)     as original_rank_zly,
      -- tento row_number je dobry v tom zmysle, ze zarucene kazdy vysledny riadok bude mat unikatne poradie,
      -- ale medzi riadkami s rovnakym poctom, t.j. nie su odlisitelne podla orderu - pocet_obyvatel,
      -- bude ich poradie "nahodne" - t.j. ich poradie nie je zarucene:
      row_number() over (partition by okres_id order by pocet_obyvatel desc)     as row_number_dobry,
      -- tento row_number je lepsi v tom zmysle, ze order je zadefinovany az na uroven unikatnosti v tabulke,
      -- takze medzi riadkami s rovnakym poctom je explicitne urcene poradie / priorita:
      row_number() over (partition by okres_id order by pocet_obyvatel desc, id) as row_number_lepsi
      from test_obce.obce
      order by okres_id, original_rank_zly
    ;
    
    -- demonstracia, preco rank je zly pre data,
    -- ktorych pocty obyvatel nie su unikatne v ramci porovnavanej mnoziny:
    select *
      from
      (
        select *,
          rank() over (partition by okres_id order by pocet_obyvatel desc)
          from test_obce.obce
      ) s
      where rank <= 3
    ;
    -- tu si vsimnite, ze aj pocetnost je rozne zla
    -- (raz 5 zaznamov pre okres, inokedy 4 zaznamy pre okres),
    -- prave preto ze zavisi od testovacich dat
    
    -- demonstracia, preco row_number je dobry zarucene, bez ohladu na unikatnost dat:
    select *
      from
      (
        select *,
          row_number() over (partition by okres_id order by pocet_obyvatel desc)
          from test_obce.obce
      ) s
      where row_number <= 3
    ;
    -- spravny pocet zaznamov bez ohladu na obsah testovacich dat,
    
    -- a ak medzi tymi, ktore maju rovnaky pocet obyvatelov,
    -- chceme mat presne definovany order,
    -- t.j. nie "nahodny", tak vo vnutri row_number zadefinujeme
    -- zarucene unikatny order az na uroven zaznamu:
    select *
      from
      (
        select *,
          row_number() over (partition by okres_id order by pocet_obyvatel desc, id)
          from test_obce.obce
      ) s
      where row_number <= 3
    ;
    
    -- drop schema test_obce;