[ 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

[ 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 Lako bi resili agregacijom samo da nije tog atribut polja koje se razlikuje pa totalno unisti group by
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.