Oracle ROWNUM

Monday, 19.10.2009 – Darko

Kada bi vas pitali da razmislite I navedete koja je to bila najmanja i najtrivijalnija stvar koja je vašim kolegama i vama prilikom pisanja upita stvarala nevolje koje su obrnuto proporcionalne njenoj veličini, šta bi odgovorili?

Pa…?

Evo dok razmislite, ja ću reći šta bih ja odgovorio da ste me isto pitali prije par dana dok sam radio paginaciju u jednoj web aplikaciji: “ rad sa rownum-om!”.

Postoji li nešto što vas je više navelo na pomisao kako  u potpunosti shvatate šta znači i kako se sa tim radi, kao što je slučaj sa rownum-om, kada ga prvi put vidite?
A na kraju uhvatite sebe kako ne možete da shvatite zašto vaš najjednostavniji mogući upit ne radi kada upotrebljavate ovu riječ?

Ma ustvari , rownum i jeste izuzetno jednostavna stvar ali  zna da pravi velike probleme ljudima koji ne uđu u srž prirode ove pseudokolone…

Šta je rownum?

Rownum je upravo to  – pseudo kolona. Kolona koja ne postoji fizički u bazi već je RDBMS dinamički dodaje selektovanim kolonama dobijenih upitom , jedinstveno označavajući svaki od dobijenih redova autoinkrementirajućim brojem.

Kada i kako upotrebljavati rownum?

Rownum je sjajna funkcionalnost Oracle RDBMS-a ( naravno , slične i iste funkcionalnosti imaju i druge baze ) koja ima mnogo primjena.
Dvije vjerovano najveće primjene su prilikom

–    Prikazivanja N top vrsta u nekom upitu
–    Kreiranje paginacije u web aplikacijama

Implementacija paginacije u jednoj web aplikaciji me je upravo i navela da napišem ovaj tekst. Dakle, trebalo je izvesti da se klikom na strelicu navigatora , npr. udesno, selektuje samo N kolona , počevši od K-tog reda nekog upita.

Da vidimo kako bi to izgledalo. Recimo da je N=10 , a K=31.
Ono što nama treba je, svakako:

Select * from moja_tabela where rownum between 31 and 40;

Ovaj upit, naravno, vraća  0 vrsta.
Ali postavlja se pitanje zašto vraća nula vrsta?

Zašto npr. upit

Select * from moja_tabela where rownum < 10

vraca rezultat – 9 prvih vrsta.

a upit

Select * from moja_tabela where rownum > 10

ne vraca nista ( iako u rezultatu postoji vise od 10 vrsta )?

Da bi to znali, vratimo se na priču o prirodi rownum-a.

Rownum se prilikom izvršavanja upita vezuje ( ali se ne setuje odmah ) za vrstu  prije ispitivanja uslova i bilo kakvog sortiranja, i vrlo je važno znati da:

1.    Prva vrsta uvijek ima rownum = 1
2.    Rownum se dalje inkrementira tek nakon što je uspješno setovan u početnoj/prethodnoj vrsti.

Zbog ovoga npr. upit

select * from moja_tabela where rownum = 2

nikada neće vratiti rezultat. Šta se dešava: upit će selektovati prvu vrstu, i provjeriti da li je 1 = 2. Ovo nije tačna tvrdnja i rownum se neće inkrementirati, niti će se prva vrsta selektovati. Prema tome, ni jedna naredna vrsta neće ispunjavati ovaj uslov.

Isto je i za upit

Select * from moja_tabela where rownum > 10

Ali zato, ako kažemo

Select * from moja_tabela where rownum < 2

selektovaće se prva vrsta iz tabele moja_tabela, provjeriće se da li je 1 < 2. Pošto je ova tvrdnja istinita i vrsta će se selektovati a rownum će joj se setovati na 1.
Rownum se zatim inkrementira ( pošto je uspješno setovan ) i tako dalje za sljedeću vrstu…

Zbog ove osobine ćete sresti dosta ljudi koji tvrde da se sa rownum-om ne mogu selektovati bilo koje vrste iz upita, već samo prvih N , upotrebljavajući “where rownum < N “.

Dakle, kako ovo prevazići?

Veoma lako!
Podupitom!

Jednostavno:

Select * from
(
Select moja_tabela.* , rownum as vrsta from moja_tabela
)
where vrsta > 2

Dodijelimo alijas koloni rownum, selektujmo je sa originalnim upitom i onda profiltrirajmo rezultat.

Takođe, kada se radi o npr. grupisanju (sjećate li se, dodjela rownum-a se vrši prije grupisanja pa se može desiti da je rownum totalno izmješan nakon grupisanja ) , vrlo je važno znati postaviti stvari po pravom redoslijedu.
Npr:


select * from
(
Select a.*, rownum as vrsta from
(
Select *  from moja_tabela order by id asc
) a
)
where vrsta > 2

Kao sto sam na početku rekao, zaista se radi o “najmanjoj i najtrivijalnijoj stvari” , ali i kao takva zna da stvori nevolje i otvori pitanja…
Do prije paginacije nisam ni ulazio mnogo u razmišljanje o ovoj temi. Možda jer nikad ranije nisam imao potrebu pisati upite sa rownum-om osim sa “where rownum = 1“ ili “where rownum < N“…
Međutim, kada su problemi počeli da se pojavljuju, ispalo je da rad sa rownum-om baš i nije kakav sam zamišljao da jeste…
E sad, da li je ovo moglo biti urađeno malo jednostavnije za upotrebu – mislim da jeste…
Po meni, provjera uslova „where rownum > N“ se trebao implementirati onako kako korisnik zamišlja da treba da radi: da se svi uslovi isprovjeravaju, sve se izgrupiše pa tek onda selektuju vrste na osnovu ovog uslova… Ipak bi  ( po meni ) rownum trebao biti tu samo da “izbroji” vrste koje smo željeli selektovati.

I, kakav bi bio vaš odgovor na početno pitanje?

  1. 3 Responses to “Oracle ROWNUM”

  2. Meni se ranije znalo desavati, da negdje ostane zagubljen COMMIT ili ROLLBACK, pa sam se iscudjavao neispravnim podacima, ali sam vremenom naucio drzati te izraze pod kontrolom 😉

    By Dejan on Oct 19, 2009

  3. S obzirom da sam već prije čitao o ROWNUM pseudo koloni (Tom Kyte je napisao odličan članak za Oracle Magazine) nisam imao problema sa time, ali vidio sam da mnogo ljudi ima poteškoća baš zbog toga što ne znaju kao stvar funkcionira.
    Najčešća zabluda (koliko sam mogao primijetit) je bila ta da je ROWNUM trajno dodijeljen nekom retku.

    Što se tiče pitanja, ne mogu se sjetiti neke sitnice koja bi mi stvarala takve probleme, ali to pripisujem činjenici da ne kodiram baš često 🙂

    By Marko Sutic on Oct 19, 2009

  4. Dejane, čudi me da ti je takva jedna mala sitnica mogla stvarati tolike probleme 🙂
    E kakvu to tek zabunu može da stvori…

    Marko, ako se ipak sjetiš nečega – podjeli to sa nama.
    Što se tiče zablude koju si pomenuo, čitao sam da se ona često sreće kod ljudi, ali mi je to tek nevjerovatno da neko pomisli…

    By Darko on Oct 19, 2009

Post a Comment