Nenamerni korelacioni podupiti

Objavljeno: 02-02-2014 | Autor: Nenad Živković | Kategorija: T-SQL

0

Neki od vas znaju šta su korelacioni podupiti (correlated subqueries) i možda ih i koristite. Neki ne znaju i mogu da žive i bez njih. Problem, i to veliki, i jednim i drugim, može da nastane kada upotrebe takve podupite, a da toga nisu ni svesni.

Za primer uzećemo tabelu CrtaniFilmovi iz Kefalo baze, koja kao primarni ključ ima kolonu CrtacID i kao drugu pomoćnu tabelu kreiraćemo temp tabelu #OdabraniCrtaci koja će imati samo jednu kolonu ID.

CREATE TABLE #OdabraniCrtaci (ID INT);
INSERT INTO #OdabraniCrtaci (ID) VALUES (1),(3);

Dodaćemo vrednosti 1 i 3 u tabelu #OdabraniCrtaci, i sadržaj obe tabele će izgledati ovako:

Nenamerni korelacioni podupiti

Probaćemo jednostavnim upitom da selektujemo sve crtaće koji se nalaze u sporednoj tabeli:

SELECT * FROM dbo.CrtaniFilmovi WHERE CrtacID IN (SELECT CrtacID FROM #OdabraniCrtaci);

Na prvi pogled možda vam odmah deluje da nešto nije u redu sa ovim upitom. Kolona CrtacID ne postoji u sporednoj tabeli i možda očekujete da ovo pukne sa greškom. Ako biste izvršili samo taj podupit odvojeno, to bi se i desilo. Međutim, ako probate da izvršite ceo upit, videćete da će proći i da se dobijaju rezultati koji možda nisu očekivani. Upit će vratiti sve iz prve tabele, a ono što se desilo je da ste upravo upotrebili correlated subquery, a da to verovatno niste planirali. Problem je ako niste ni primetili.

Ono što se desilo je da je SQL Server upotrebio kolonu CrtacID iz prve tabele, pošto je potpuno legitimno da se u podupitu koriste kolone iz spoljnjeg upita. Ovaj upit bi, ukoliko tabela #OdabraniCrtaci nije prazna, bio ekvivalentan bilo kome od sledećih:

SELECT * FROM dbo.CrtaniFilmovi WHERE CrtacID IN (SELECT CrtacID);
SELECT * FROM dbo.CrtaniFilmovi WHERE CrtacID = CrtacID;
SELECT * FROM dbo.CrtaniFilmovi WHERE 1=1;
SELECT * FROM dbo.CrtaniFilmovi;

ili na primer:

SELECT * FROM dbo.CrtaniFilmovi cf WHERE cf.CrtacID IN (SELECT cf.CrtacID FROM #OdabraniCrtaci oc);

Međutim, da su odmah korišćeni alijasi ispred naziva kolona, verovatno biste stavili:

SELECT * FROM dbo.CrtaniFilmovi cf WHERE cf.CrtacID IN (SELECT oc.CrtacID FROM #OdabraniCrtaci oc);

što bi očekivano puklo i uvedili biste grešku da ste u podupitu naveli oc.CrtacID umesto oc.ID

Ovo se može lako potkrasti i kod svakog podupita treba paziti oko naziva kolona i koristiti alijase ili nazive tabela ispred. Zamislite još da umesto SELECT imate DELETE naredbu – realan scenario gde je sporedna tabela stage sa ID-evima spremnim za brisanje. Okinete to preko noći i ujutru vam nedostaje 600 000 redova iz produkcione baze (Istinita priča).