[ aleksandaraleksandar @ 20.01.2008. 16:46 ] @
dve tabele: tabela 1: id, date tabela 2: id, username kako ispisati samo poslednji podatak iz tabele 1 za sve korisnike iz tabele 2? svaki join mi daje isti rezultat ![]() |
[ aleksandaraleksandar @ 20.01.2008. 16:46 ] @
[ goranvuc @ 20.01.2008. 19:54 ] @
Code: SELECT MAX(Date) AS Poslednji, username FROM Tabela1 INNER JOIN Tabela2 ON Tabela1.ID = Tabela2.ID GROUP BY username [ aleksandaraleksandar @ 20.01.2008. 20:47 ] @
ovaj kod:
Code: SELECT u_id,date FROM tabela1 where u_id in (19,160) ORDER BY date desc LIMIT 10 ispisuje: Code: u_id | date ------+------------ 160 | 2008-01-14 19 | 2008-01-14 19 | 2008-01-14 19 | 2008-01-14 160 | 2008-01-14 160 | 2008-01-14 160 | 2008-01-14 19 | 2008-01-14 19 | 2008-01-14 19 | 2008-01-14 i gotov je u sekundi. ovo sto si napisao traje previse dugo cak i kada dodam uslov Code: ... WHERE id in (19,160) imas li jos neki predlog? [ goranvuc @ 20.01.2008. 21:19 ] @
A sta si ti ono pitao?
[ aleksandaraleksandar @ 20.01.2008. 21:33 ] @
kako da spajanjem dve tabele dobijem samo jedan zapis (po id-u), a ne kombinaciju koja se dobija klasicnim JOIN pristupom.
da probam ovako: u jednoj tabeli je id kupca i datum kupovine. u drugoj tabeli je id kupca i ime kupca. kako dobiti id kupca, ime kupca i datum poslednje kupovine? [ CallMeSaMaster @ 20.01.2008. 22:47 ] @
Ako sam dobro shavtio, mislis na ovo?
Code: SELECT top 1 u_id,date FROM tabela1 where u_id in (19,160) ORDER BY date desc [ goranvuc @ 21.01.2008. 06:27 ] @
Citat: aleksandaraleksandar:kako ispisati samo poslednji podatak iz tabele 1 za sve korisnike iz tabele 2? za sve korisnike [ M E N E @ 25.01.2010. 12:37 ] @
Koliko sam shvatio, niko nije odgovorio na ovo, a sada i meni treba :-(
Imam jednu tabelu sa ID-om, a drugu tabelu sa njegovim atributima, vise atributa za jedan ID... T1: ID, Naziv, ... T2: ID, Atribut VremeUnosa Treba mi da izlistam sve iz T1 sa poslednjim unesenim atributom ? [ aleksandaraleksandar @ 25.01.2010. 13:01 ] @
od T2 napraviš upit, pa ga ubaciš kao alias tebele:
Code: select * from t1, (select max(vremeunosa), id, atribut from t2 group by id, atribut) t2 where t1.id=t2.id [ mmix @ 25.01.2010. 13:32 ] @
Nije dobro, zapravo dobio si isto sto i inner join.
Znam sta vas zulja ![]() ![]() Resenje je da prvo napravite agregaciju datuma samo po ID, bez ostalih polja i onda to joinujete sa T2 po tom virtuelnom kompozitnom kljucu. Code: select T2.* from T2 inner join (select ID, MAX(DatumUnosa) as MaxDT from T2 group by ID ) T2L on T2L.ID = T2.ID and T2L.MaxDT = T2.DatumUnosa ovo ce vratiti tabelu sa ID i poslednji uneseni atribut i vreme unosa. To sad mozete da okacite kao view radi preglednosti (pa se on joinuje na T1) a moze i da se uradi inner join na T1 direktno: Code: select T1.*, T2N.* from T1 inner join (select T2.* from T2 inner join (select ID, MAX(DatumUnosa) as MaxDT from T2 group by ID) T2L on T2L.ID = T2.ID and T2L.MaxDT = T2.DatumUnosa ) T2N on T2N.ID = T1.ID Mozda postoji i malo optimalnije resenje, ali ni ovo nije lose, ako postoji desc index na datumu i ID je PK sve se resava sa dva clustered scan-a. Dupli rezultati mogu da se pojave samo ako ima dva atributa sa istim datetime, ali onda i mora da se definise koji je vazniji od ta dva da bi se rafinisalo resenje. [ aleksandaraleksandar @ 25.01.2010. 13:49 ] @
ja sam razumeo da hoće i za svaki atribut.
no dobro. [ M E N E @ 25.01.2010. 14:00 ] @
radi
za'valjujem! :) [ HladankaoLed @ 26.01.2010. 10:52 ] @
Code: SELECT t1.*, a.* FROM t1 OUTER APPLY (SELECT TOP (1) * FROM t2 WHERE t2.id=t1.id ORDER BY t2.vreme DESC) a gde je T1 glavna tabela, a T2 tabela atributa izraz unutar OUTER APPLY evaluira se za svaki red glavne tabele (predaje mu se argument ID) i ponasa se slicno subselectu, s tim sto je neuporedivo mocniji. Subselect vraca iskljucivo skalarnu vrednost, dok je ovde moguce vratiti virtuelnu tabelu (vise kolona i vise redova). [ mmix @ 26.01.2010. 11:38 ] @
Odlicno resenje, bez agregacije. Cak je i brze 30% od onog sto sam okacio, pod uslovom da postoji desc indeks na vremeunosa, u suprotnom je 4 put sporiji (zbog sorta).
[ HladankaoLed @ 26.01.2010. 12:13 ] @
Jeste, resenje je elegantno i performantno dok tabela ne postane prevelika. Onda troskovi, inace elegantnog Nested Lopps operatora postaju neprijatni ili cak nepodnosljivi. U tom slucaju vise se isplati uraditi JOIN, pa pokupiti samo ono sto nam treba. Optimizer ce u vezi JOIN-a izabrati optimalni pristup za velike table, sto u slucaju APPLY operatora nazalost nece - njemu je tu velicina tabele irelevantna. Ovo bi moglo da bude resenja za large table:
Code: WITH cte as ( SELECT t1.id, t1.naziv, t2.atribut, t2.vreme, ROW_NUMBER() OVER(PARTITION BY t2.id ORDER BY t2.vreme DESC) as rn FROM t1 LEFT OUTER JOIN t2 ON t1.id = t2.id ) SELECT * FROM cte WHERE rn=1 Na velikoj tabli treba mu mnogo manje logical read-ova, nego resenju sa APPLY. [ mmix @ 26.01.2010. 13:28 ] @
Hm, ne vidim to. U ovom slucaju imas obavezan sort tek nakon outer join-a, sto znaci da query engine uvek mora da formira pun outer join dataset sa max t1+t2 redova da bi primenio sort i projektovao ROW_NUMBER().
Sa druge strane outer apply resenje koje si dao ce (uz postojanje desc indeksa na vremenu) uvek raditi sa t2 redova bez ijednog sorta i bez ijednog table scan-a, sve ce cupati iz indeksa. U principu oba se svedu na left outer operaciju samo u row_number() slucaju uvek vise redova dolazi iz T2 seta a where uslov ne moze da se primeni pre outer-joina jer je na projektovanom polju i samim tim ne vidim prostor za query optimizera. Linq2Sql recimo dosta koristi tu taktiku sa ROW_NUMBER() u Take(int) operacijama jer rn omogicava sve komparativne operacije (<, >, itd), ali to mi ne deluje nikako optimalno vec vise apstraktno i univerzalno. [ HladankaoLed @ 02.02.2010. 08:08 ] @
Izvinjavam se na zakasnelom odgovoru...
ROW_NUMBER() resenje je performantnije kada je glavna tabela veoma velika. Uz odgovarajuce indekse optimizer se za ovo resenje generisati plan sa lepim Merge Join operatorom, pracenim ne tako lepim Sort operatorom. U ovom slucaju, kao sto si ti napomenuo, Sort je najveci konzument vremena i resursa. U zavisnosti od velicine tabele atributa moze da bude od koristi da se Sort operator prebaci sa leve strane Merge Join-a (da se najpre prvo sortira druga tabel, pa tek onda joinuje). CROSS APPLY pristup je fiksan - za svaki red iz glavne tabele potrebne su 3 ili 4 strane iz tabele atributa. resenje odlicno skalira drugu tabelu, ranjivo je, medjutim, na velicinu glavne tabele. Ako u glavnoj tabli imamo 1M redova onda ovo resenje potrebuje 3-4M logical readova. Dakle, u ovoj trci (ROW_NUMBER resenje vs. CROSS APPLY sa 1M redova u glavnoj tabli iliti Sort vs. Nested Loops) pobednik je ROW_NUMBER() do izvesnog broja redova u drugoj tabli (recimo 1.5 M). Sto je broj redova u drugoj tabli veci, vece su sanse da drugo resenje pobedi. Imao sam u praksi takav primer i resenjem sa ROW_NUMBER() uspeli smo da poboljsamo performanse za nekih 15-20% (SSMS ce poredeci ova dva plana sugerisati da poboljsanje treba da bude preko 50%, realno je, ipak, 15-tak %). Izvinjavam se svim zbog sturog odgovora, @mmix nadam se da sam barem delimicno odgovorio na voje pitanje. Takodje se nadam da ce okolnosti dozvoliti da u narednih nekoliko dana napisem detaljan clanak o ovom problemu, sa kompletnim test skript rutinama. Pozdrav, M. Copyright (C) 2001-2025 by www.elitesecurity.org. All rights reserved.
|