Poredjenje Tabela
August 31, 2010 – ZidarPoredjenje Tabela
Â
Cesto se u praksi javlja zadatak poredjenja tabela. Date su dve tabele, istih struktura, istog broja redova. Takve dve table bice jednake ako su im svi odgovarajuci redovi jednaki. Dva reda su jednaka ako su im vrednsoti u odgovarjucim kolonama jednake.
Â
Primer dve jednake tabele:
Â
IF Object_ID(‘tempdb..#Original’) IS NOT NULL DROP TABLE #Original
;
CREATE TABLE #Original (ID int, Ime varchar(12), Prezime varchar(12))
;
IF Object_ID(‘tempdb..#Kopija’) IS NOT NULL DROP TABLE #Kopija
;
CREATE TABLE #Kopija (ID int, Ime varchar(12), Prezime varchar(12))
;
INSERT INTO #Original VALUES (1,‘Dusko’,‘Radovic’)
INSERT INTO #Original VALUES (2,‘Simo’,‘Esic’)
INSERT INTO #Original VALUES (3,‘Zuko’,‘Dzumhur’)
INSERT INTO #Original VALUES (4,‘Momo’,‘Kapor’)
INSERT INTO #Original VALUES (5,‘Branko’,‘Copic’)
INSERT INTO #Original VALUES (6,‘Mesa’,‘Selimovic’)
;
INSERT INTO #Kopija VALUES (1,‘Dusko’,‘Radovic’)
INSERT INTO #Kopija VALUES (2,‘Simo’,‘Esic’)
INSERT INTO #Kopija VALUES (3,‘Zuko’,‘Dzumhur’)
INSERT INTO #Kopija VALUES (4,‘Momo’,‘Kapor’)
INSERT INTO #Kopija VALUES (5,‘Branko’,‘Copic’)
INSERT INTO #Kopija VALUES (6,‘Mesa’,‘Selimovic’)
;
Za tabele u primeru znamo da su jednake jer smo ih tako napravili. Kako dokazati da su tabele jednake? Posto smo mi SQL majstori, SQL, pokusacemo da napisemo SQL kverije koji ce dokazati jednakost datih tabela.
Â
Prvo sto nam prirodno pada na pamet je nesto ovako:
Â
SELECT
*
FROM #Original AS O
JOIN #Kopija AS K ON O.ID = K.ID
WHERE O.Ime <> K.Ime
OR O.prezime <> K.prezime
Â
(0 row(s) affected)
Â
Sve deluje lepo. Da vidimo da li kveri radi i kad imamo razlike. Napravimo razliku u redu sa ID = 1, Dusko Radvic neka postane Dusko Popovic.
Â
Â
UPDATE #Kopija SET Prezime = ‘Popovic’ WHERE ID = 1
;
Kada izvrsimo kveri, dobijemo naravno tacan rezultat:
Â
        ID Ime         Prezime              ID Ime         Prezime
———– ———— ———— ———– ———— ————
         1 Dusko       Radovic               1 Dusko       Popovic
Â
(1 row(s) affected)
Â
Â
Uticaj NULL vrednsoti
Hajde sada da za ID = 2 stavimo da je Prezime = NULL
Â
UPDATE #Kopija SET Prezime = NULL WHERE ID = 2
;
SELECT * FROM #Kopija
;
        ID Ime         Prezime
———– ———— ————
         1 Dusko       Popovic
         2 Simo        NULL
         3 Zuko        Dzumhur
         4 Momo        Kapor
         5 Branko      Copic
         6 Mesa        Selimovic
Â
(6 row(s) affected)
Â
Sada naravno ocekujemo da kveri vrati dva reda.Medjutim, dobijamo i dalje samo jedan red:
Â
        ID Ime         Prezime              ID Ime         Prezime
———– ———— ———— ———– ———— ————
         1 Dusko       Radovic               1 Dusko       Popovic
Â
(1 row(s) affected)
Â
U cemu je problem? Zasto se ne vidi red sa ID =2 gde jeIme = NULL? Nas kveri pokusava da uporedi NULL vrednost sa necim sto nije NULL. NULL nije jednako nicemu, niti je NULL jednako nekom drugom NULL. Zato kveri ne radi dobro kad imamo NULL vrednosti u nekoj od tabela. Uradimo ovo;
Â
UPDATE #Original SET Prezime = ‘Simo’, Ime = NULL WHERE ID = 2
UPDATE #Kopija SET Prezime = NULL, Ime = ‘Esic’ WHERE ID = 2
;
SELECT * FROM #Kopija
;
SELECT * FROM #Original
;
        ID Ime         Prezime
———– ———— ————
         1 Dusko       Popovic
         2 Esic        NULL
         3 Zuko        Dzumhur
         4 Momo        Kapor
         5 Branko      Copic
         6 Mesa        Selimovic
Â
(6 row(s) affected)
Â
        ID Ime         Prezime
———– ———— ————
         1 Dusko       Radovic
         2 NULL        Simo
         3 Zuko        Dzumhur
         4 Momo        Kapor
         5 Branko      Copic
         6 Mesa        Selimovic
Â
Redovi za ID =1 i ID=2 se ocigledno razlikuju. Ipak, kveri vraca samo jedan red koji nije identican u dve date tabele. Da eliminisemo probleme sa NULL vrednostima, mozemo da kveri napisemo ovako:
Â
SELECT
*
FROM #Original AS O
JOIN #Kopija AS K ON O.ID = K.ID
WHERE COALESCE(O.Ime,‘Ovde je NULL’) <> COALESCE(K.Ime,‘Ovde je NULL’)
OR COALESCE(O.prezime, ‘Ovde je NULL’) <> COALESCE(K.prezime, ‘Ovde je NULL’)
;
Dobijamo tacan rezultat:
        ID Ime         Prezime              ID Ime         Prezime
———– ———— ———— ———– ———— ————
         1 Dusko       Radovic               1 Dusko       Popovic
         2 NULL        Simo                  2 Esic        NULL
Â
(2 row(s) affected)
Â
Kveri deluje jednostavno, cak is a COALESCE. Sta ce se desiti kad budemo imali veoma mnogo kolona? WHERE uslov postaje glomazan, a i izlaz nece biti bas lep, bice mnogo kolona. Bilo bi najlepse kad ne bismo morali da pisemo sve kolone….Pokazacemo resenje i za to, nesto kasnije. Sada cemo da ubacimo jos gresaka u tabele, da proverimo kako ce se ponasati kveri.
Â
NULL u JOIN koloni
Napravimo tabele tako da ID nema osobine primarnog kljuca.Neka imamo NULL u koloni ID (po kojoj vrsimo JOIN)
Â
IF Object_ID(‘tempdb..#Original’) IS NOT NULL DROP TABLE #Original
;
CREATE TABLE #Original (ID int , Ime varchar(12), Prezime varchar(12))
;
IF Object_ID(‘tempdb..#Kopija’) IS NOT NULL DROP TABLE #Kopija
;
CREATE TABLE #Kopija (ID int , Ime varchar(12), Prezime varchar(12))
;
INSERT INTO #Original VALUES (1,‘Dusko’,‘Radovic’)
INSERT INTO #Original VALUES (NULL,‘Simo’,‘Esic’)
INSERT INTO #Original VALUES (3,‘Zuko’,‘Dzumhur’)
INSERT INTO #Original VALUES (4,‘Momo’,‘Kapor’)
INSERT INTO #Original VALUES (5,‘Branko’,‘Copic’)
INSERT INTO #Original VALUES (6,‘Mesa’,‘Selimovic’)
Â
;
INSERT INTO #Kopija VALUES (1,‘Dusko’,‘Radovic’)
INSERT INTO #Kopija VALUES (2,‘Simo’,‘Esic’)
INSERT INTO #Kopija VALUES (3,‘Zuko’,‘Dzumhur’)
INSERT INTO #Kopija VALUES (4,‘Momo’,‘Kapor’)
INSERT INTO #Kopija VALUES (5,‘Branko’,‘Copic’)
INSERT INTO #Kopija VALUES (6,‘Mesa’,‘Selimovic’)
;
Sve je isto, osim sto imamo NULL umesto ID = 2 u tabeli #Original. Kveri kojije lepo radio za NULL vrednosti u non-join kolonama bio je ovaj
SELECT
*
FROM #Original AS O
JOIN #Kopija AS K ON O.ID = K.ID
WHERE COALESCE(O.Ime,‘Ovde je NULL’) <> COALESCE(K.Ime,‘Ovde je NULL’)
OR COALESCE(O.prezime, ‘Ovde je NULL’) <> COALESCE(K.prezime, ‘Ovde je NULL’)
;
Ponovo dobijamo netacan rezultat:
Â
        ID Ime         Prezime              ID Ime         Prezime
———– ———— ———— ———– ———— ————
Â
(0 row(s) affected)
Â
Neka umesto NULL imamo neku vrednost, ali razlicitu od druge tabele:
Â
UPDATE #Original SET ID = 99 WHERE Ime = ‘Simo’
Â
Tabele izgledaju sada ovako:
Â
SELECT * FROM #Kopija
;
SELECT * FROM #Original
;
        ID Ime         Prezime
———– ———— ————
         1 Dusko       Radovic
         2 Simo        Esic
         3 Zuko        Dzumhur
         4 Momo        Kapor
         5 Branko      Copic
         6 Mesa        Selimovic
Â
(6 row(s) affected)
Â
        ID Ime         Prezime
———– ———— ————
         1 Dusko       Radovic
        99 Simo        Esic
         3 Zuko        Dzumhur
         4 Momo        Kapor
         5 Branko      Copic
         6 Mesa        Selimovic
Â
Red u kome je ‘Simo’,’Esic’ ocigledno se razlikuje u dve tabele, ID nije isti. Da li ce nas kveri to pokazati? Ponovo, ako izvrsimo
Â
SELECT
*
FROM #Original AS O
JOIN #Kopija AS K ON O.ID = K.ID
WHERE COALESCE(O.Ime,‘Ovde je NULL’) <> COALESCE(K.Ime,‘Ovde je NULL’)
OR COALESCE(O.prezime, ‘Ovde je NULL’) <> COALESCE(K.prezime, ‘Ovde je NULL’)
;
ne dobijamo ni jedan red:
Â
        ID Ime         Prezime              ID Ime         Prezime
———– ———— ———— ———– ———— ————
Â
(0 row(s) affected)
Â
Postoji jos mogucnosti da stvari krenu naopako. Neka JOIN kolona nema NULL vrednosti, neka nema razlika, ali neka imamo duplikat.
Â
UPDATE #Original SET ID = 5 WHERE Ime = ‘Simo’
Â
SELECT * FROM #Kopija
;
SELECT * FROM #Original
;
Sada imamo
        ID Ime         Prezime
———– ———— ————
         1 Dusko       Radovic
         2 Simo        Esic
         3 Zuko        Dzumhur
         4 Momo        Kapor
         5 Branko      Copic
         6 Mesa        Selimovic
Â
(6 row(s) affected)
Â
        ID Ime         Prezime
———– ———— ————
         1 Dusko       Radovic
         5 Simo        Esic
         3 Zuko        Dzumhur
         4 Momo        Kapor
         5 Branko      Copic
         6 Mesa        Selimovic
Â
U donjoj tabeli, i Zuko i Branko imaju ID = 5. Izvrsimo ponovo kveri
Â
SELECT
*
FROM #Original AS O
JOIN #Kopija AS K ON O.ID = K.ID
WHERE COALESCE(O.Ime,‘Ovde je NULL’) <> COALESCE(K.Ime,‘Ovde je NULL’)
OR COALESCE(O.prezime, ‘Ovde je NULL’) <> COALESCE(K.prezime, ‘Ovde je NULL’)
;
Ovog puta dobili smo rezulat:
Â
        ID Ime         Prezime              ID Ime         Prezime
———– ———— ———— ———– ———— ————
         5 Simo        Esic                  5 Branko      Copic
Â
(1 row(s) affected)
Â
Nije bas odmah jasno sta u stvari rezultat pokazuje, ali bar vidimo da negde imamo problem, pa cemo vec istrazivati i verovatno nesto zakljuciti. Znaci, nas kveri ne propusta duplikate, barem tako za sada izgleda.
Â
Vidimo da naivno napisan kveri moze da vrati tacan ili pogresan rezultat, ili delimicno tacan, zavisno od toga kakvi su podaci. Dobar kveri ne sme da se ponasa tako. Opisane situacije mogu se javiti lako u praksi. Ako su tabele velike i ne mogu se golim okom proveriti, mozemo biti u grdnoj nevolji ako napisemo naivan kveri.
Â
Zasto je predlozeni kvari naivan i ne radi? Zato sto smo precutno i nesvsno ucinili pogresnu prtpostavku na pocetku. Ponasali smo se kao da je kolona ID primarni kljuc. Da jeste, ne bismo imali problem sa duplikatom i NULL vrednostima. Medjutim, vrednosti ne moraju da su iste za PK u obe kolone. Pogotovo ako je PK neki autonumber ili identity, neki broj koji sistem sam generise. Ako dokazemo na pocetku da ID u obe tabele ima svojstva primarnog kljuce (nema NULL i nema duplikata), mogli bi da razbijemo nas naivan kveri na tri. Prvo bismo pokazali redove koji se nalaze u jednoj tabeli a nema ih u drugoj, a onda bismo pokazali redove koji se nalaze u obe tabele, ali su razliciti.
Â
Podesimo prvo podatke
Â
UPDATE #Original SET ID = 5 WHERE Ime = ‘Simo’
Â
UPDATE #Original SET ID = 99 WHERE Ime = ‘Simo’
;
UPDATE #Kopija SET ID = 55 WHERE Ime = ‘Branko’
;
UPDATE #Kopija SET Ime = ‘Zzzzuko’ WHERE ID = 3
;
Tabele sada izgledaju ovako:
Â
SELECT * FROM #Kopija
;
SELECT * FROM #Original
;
        ID Ime         Prezime
———– ———— ————
         1 Dusko       Radovic
         2 Simo        Esic
         3 Zzzzuko     Dzumhur
         4 Momo        Kapor
        55 Branko      Copic
         6 Mesa        Selimovic
Â
(6 row(s) affected)
Â
        ID Ime         Prezime
———– ———— ————
         1 Dusko       Radovic
        99 Simo        Esic
         3 Zuko        Dzumhur
         4 Momo        Kapor
         5 Branko      Copic
         6 Mesa        Selimovic
Â
(6 row(s) affected)
Â
Razlike su ove:
-   u #Kopija ‘Branko’,’Copic’ ima ID = 55
-   u #Original ‘Simo’,’Esic’ ima ID = 99.
-   U #Original umesto ‘Zuko’ pise ‘ZZZuko’ za red sa ID = 3
Â
Sve ostalo je isto u obe tabele. Duplikata u ID nema, nema ni NULL vrednosti u ID.
Â
Pokazimo prvo deo gde su razlike u ID:
Â
— Sta ima u #Original a nema u #Kopija
SELECT
O.*
FROM #Original AS O
LEFT JOIN #Kopija AS K ON O.ID = K.ID
WHERE K.ID IS NULL
;
        ID Ime         Prezime
———– ———— ————
        99 Simo        Esic
         5 Branko      Copic
Â
(2 row(s) affected)
Â
– Sta ima u #Kopija a nema u #Original
SELECT
K.*
FROM #Original AS O
RIGHT JOIN #Kopija AS K ON O.ID = K.ID
WHERE O.ID IS NULL
;
Â
        ID Ime         Prezime
———– ———— ————
         2 Simo        Esic
        55 Branko      Copic
Â
(2 row(s) affected)
Â
Nista lakse, LEFT JOIN, pa RIGHT JOIN, milina jedna
Â
Razlike za one redove gde ID postoji u obe tabele:
Â
SELECT
*
FROM #Original AS O
JOIN #Kopija AS K ON O.ID = K.ID
WHERE COALESCE(O.Ime,‘Ovde je NULL’) <> COALESCE(K.Ime,‘Ovde je NULL’)
OR COALESCE(O.prezime, ‘Ovde je NULL’) <> COALESCE(K.prezime, ‘Ovde je NULL’)
;
Â
        ID Ime         Prezime              ID Ime         Prezime
———– ———— ———— ———– ———— ————
         3 Zuko        Dzumhur               3 Zzzzuko     Dzumhur
Â
(1 row(s) affected)
Â
Lepo, ali nije najzgodnije za prikazivanje. Imamo tri kverija, od kojih treci ima duplo vise kolona nego prva dva. Ko ce se tu snaci, sta ne valja i gde?
Â
I to sve pod uslovom da je ID ili jedinstven i NOT NULL. U realnom zivotu to ne mozemo da garantujemo uvek. Za potrebe primera smo napravili takve tabele, ID je kao PK, ali to nece biti uvek slucaj.
Â
Znaci, nasa strategija sa naivno napisanim kverijem se polako raspada. Treba nam nesto bolje. Sad znamo uslove to ‘nesto bolje’:
-Â Â Â da resenje nije osetljovo na NULL vrednosti u bilo kojoj
-Â Â Â da nije osteljivo na promene u bilo kojoj koloni (JOIN ili ne JOIN, svejedno)
-Â Â Â da resenje nije osetljivo na duplikate
Da resenje „nije osetljivo na neki slucaj“ treba citati „da resenje ne promasuje posmatrane slucajeve“
Â
U sledecem nastavku poci cemo drugim putem, pa dokle stignemo.
Â
J
1 Trackback(s)