Agregirani bitovi

Objavljeno: 19-06-2014 | Autor: Nenad Živković | Kategorija: Funkcije, T-SQL, Trikovi

Ознаке: , , ,

0

Ukoliko dobijete ideju da proverite da li u nekoj BIT koloni postoji makar jedna vrednost true ili makar jedna vrednost false, možda će vam prvo pasti na pamet ideja da iskoristite agregatne funkcije MAX() ili MIN() i lako proverite da li su sve vrednosti iste, s obzirom da postoje samo dve moguće vrednosti.

Međutim, ako pokušate da izvršite komandu:

SELECT MAX(UBoji) FROM dbo.CrtaneEpizode;

ubrzo ćete udariti u zid pošto se javlja sledeća greška:

Msg 8117, Level 16, State 1, Line 1
Operand data type bit is invalid for max operator.


Tip podataka bit ne može da se koristi u agregatnim operacijama, bar ne po dizajnu, ali postoji nekoliko rešenja kako se ovo može lako prevazići. Za početak, da odmah pokažemo i naizgled najnezgrapnije rešenje, ali ipak i najefikasnije:

SELECT CASE WHEN EXISTS (SELECT * FROM dbo.CrtaneEpizode WHERE UBoji = 0) 
      THEN 0 ELSE 1 END AS SveEpizodeUBoji;

EXISTS operator prekida izvršavanje odmah kada pronađe traženu vrednost i vraća jednostavno true ili false na postavljeni zahtev postojanja makar jedne vrednosti.

Drugi način bi bio konvertovanje BIT vrednosti u INT, čime omogućavate upotrebu željene MAX funkcije:

SELECT  MIN(CAST(UBoji AS INT)) AS SveEpizodeUBoji
FROM dbo.CrtaneEpizode;

I za kraj treći i najjednostavniji način je da prosto dodate broj 0 svojoj BIT koloni i time je implicitno pretvarate u INT:

SELECT  MIN(UBoji+0) AS SveEpizodeUBoji
FROM dbo.CrtaneEpizode;

S obzirom da druga dva upita zahtevaju index scan nad celom tabelom, a prvi se prekida na prvi nađeni rezultat, očigledno da će se pokazati kao performantniji.

MAX(bit)+0

Međutim, ukoliko je pored traženja vrednosti potrebno i grupisanje, što je sasvim realna situacija, onda se prvi upit pretvara u još nezgrapniju konstrukciju sa korelacionim podupitom. Druga dva ostaju i dalje prilično jednostavna i po performansama postaju uporedivi sa prvim, a čak bi trebalo da budu i za nijansu bolji.

SELECT CrtacID
 , CASE WHEN EXISTS (SELECT * FROM dbo.CrtaneEpizode c2 WHERE c2.UBoji = 0 AND c2.CrtacID = c1.CrtacID) 
      THEN 0 ELSE 1 END AS SveEpizodeUBoji
FROM dbo.CrtaneEpizode c1
GROUP BY CrtacID;

SELECT CrtacID, MIN(CAST(UBoji AS INT)) AS SveEpizodeUBoji
FROM dbo.CrtaneEpizode
GROUP BY CrtacID;

SELECT CrtacID, MIN(UBoji+0) AS SveEpizodeUBoji
FROM dbo.CrtaneEpizode 
GROUP BY CrtacID;

Ukoliko VI imate još neku ideju, napišite u komentarima.