[ negyxo @ 04.11.2004. 13:08 ] @
Prvo me interesuje nesto u vezi join-iranja

Radim u MSDE-u i interesuje me kako je bolje da se pise query prilikom joiniranja.
Evo primera:

U jednoj tabeli se nalaze radnici i njihovi podaci

Radnik
---------------------------------------------------
sifraRadnika
imeRadnika
sifraMestaStanovanja
adresa
---------------------------------------------------

dok je druga tabela cuva podatke o mestima

Mesto
---------------------------------------------------
sifraMesta
imeMesta
---------------------------------------------------

Sad ukoliko bi hteo da join-iram mesta sa radnicima pisao bi

SELECT * FROM Radnik INNER JOIN Mesto ON Mesto.SifraMesta = Radnik.sifraMestaStanovanja

i ovo bi radilo lepo ali sta ako hocu da ucitam radnike koji su na primer iz nekog mesta recimo beograd (neka je onda sifra 11000)
onda bih pisao sledeci upit

SELECT * FROM Radnik INNER JOIN Mesto ON Mesto.SifraMesta = Radnik.sifraMestaStanovanja
WHERE sifraMestaStanovanja = 11000

ili mozda ovako

SELECT * FROM
Radnik INNER JOIN
(SELECT * FROM Mesto WHERE sifraMesta = 11000) AS M
ON M.SifraMesta = Radnik.sifraMestaStanovanja


sta je bolje od ova dva upita pisati i kada? Znam da je pitanje relativno ali mene interesuje prvenstveno praksa drugih.

Drugo sto me interesuje je jedno logicko resenje.
Uvek su mi bili problem podaci koji se vezuju za vreme tj. ni danas nisam siguran kako ih resavam a problem je u sledecem

neka je opet u pitanju ona tabela Radnik
e sad problem je sta da radim sa siframa radnika koji moraju biti jedinstveni ali jedinstveni na nivou godine tj. uvek sifraRadnika ostaje ista ali podaci se tokom godine menjaju za radnika pa se na pocetku svake godine moraju upisati svezi podaci za radnike. Primer


u 2001 godini podaci su sledeci

sifraRadnika | imeRadnika | Adresa
----------------------------------
1 | Pera | nnn 1
2 | Marko | nnn 2

u 2003 godini doslo je do promene adresa pa sad izgleda ovako

sifraRadnika | imeRadnika | Adresa
----------------------------------
1 | Pera | mmm 8
2 | Marko | mmm 9

znaci sve sto se promenilo su adrese radnika a sifra im je ostala ista. Sad problem lezi u tome kad se radi prikaz onda bi se ovi podaci morali vezati za godinu pa se tako iscitavati. Ovo onda povlaci i drugciju strukturu tabele i gomilanje podataka iz godine u godinu i dodatnu kompleksnost prilikom pisanja query-ija. I za ovaj problem me insteresuju resenja drugih.

Toliko za ovaj put

Pozdrav

[ sasas @ 04.11.2004. 13:40 ] @
Citat:


SELECT * FROM Radnik INNER JOIN Mesto ON Mesto.SifraMesta = Radnik.sifraMestaStanovanja
WHERE sifraMestaStanovanja = 11000

ili mozda ovako

SELECT * FROM
Radnik INNER JOIN
(SELECT * FROM Mesto WHERE sifraMesta = 11000) AS M
ON M.SifraMesta = Radnik.sifraMestaStanovanja


Ja bih isao sa onom prvom jer mi se cini jednostavnije za odrzavanje. Jesi li probao da pogledas rezultate u profileru? Ova rucna optimizacija querija mi se cini uvek nepotrebna, posto ce ionako server da uradi posao, vecinom bolje nego mi.


Citat:

sifraRadnika | imeRadnika | Adresa
----------------------------------
1 | Pera | nnn 1
2 | Marko | nnn 2

u 2003 godini doslo je do promene adresa pa sad izgleda ovako

sifraRadnika | imeRadnika | Adresa
----------------------------------
1 | Pera | mmm 8
2 | Marko | mmm 9


Losa je praksa sve ubacivati u istu tabelu. Podatke koji su promenjivi u vremenu bi ja prebacio u posebnu tabelu master->detail. Ako su u istoj tabeli, dobijas bespotrebnu redunanciju. Izbegavati.

ss.
[ jablan @ 04.11.2004. 13:54 ] @
Citat:
negyxo: sta ako hocu da ucitam radnike koji su na primer iz nekog mesta recimo beograd (neka je onda sifra 11000)
onda bih pisao sledeci upit

SELECT * FROM Radnik INNER JOIN Mesto ON Mesto.SifraMesta = Radnik.sifraMestaStanovanja
WHERE sifraMestaStanovanja = 11000

Ne, nego
Code:

SELECT * FROM Radnik
WHERE sifraMestaStanovanja = 11000

Jer nema smisla džoinovati sa tabelom iz koje se podaci ne koriste.
Citat:

e sad problem je sta da radim sa siframa radnika koji moraju biti jedinstveni ali jedinstveni na nivou godine tj. uvek sifraRadnika ostaje ista ali podaci se tokom godine menjaju za radnika pa se na pocetku svake godine moraju upisati svezi podaci za radnike.

Ako ne moraš da čuvaš istorijat adrese, jednostavno update-uješ taj slog. Ako moraš, napraviš novu tabelu adrese, u tabeli adrese imaćeš id, radnikid, adresu i datum do koje je bila validna. ako je taj datum null, znači da je još uvek validna.
[ negyxo @ 04.11.2004. 20:29 ] @
Znam ja jablane da nema smisla join-ovati tabelu iz koje se podaci ne koriste ali u onome slucaju podaci bi trebalo da se koriste tj. imeMesta. Verovatno nisam mozda bio jasan po tom pitanju.
Ako si ti sasas u pravu (a nadam se da jesi) onda je mozda bolje da se ostavim serveru optimizaciju koji on verovatno radi nego ja rucno da optimizujem, doduse valjalo bi i testirati.

E sad sto se tice onih godina ja sam do sada radio tako sto sam kreirao jednu istu tabelu s tim sto je jedina razlika u jednom polju vise a to je godina.
Pa bi onda tabela radnik imala jos jednu kopiju u obliku

RadnikVreme
------------------------------
godina -> ovo je jedino sto je razlicito u odnosu na Radnik tabelu
sifraRadnika
imeRadnika
sifraMestaStanovanja
adresa
...
------------------------------


Ovaj pristup i nije bio los ali onda posle moram duplirati upite kod prikaza tj. prilikom pisanja izvestaja. Jednom bi isao upit za tekucu godinu a drugi upit za neku proslu godinu. Onda jos jedan problem je sto bi onda isti metod trebao da namestim za narednih 20 tabela. To bi onda znacilo da za svaku tabelu moram imati strukturu
master->detail kako je sasas naveo. Jer su i ostale tabele zavisne od vremena. Zato ne znam za sta da se odlucim koji bi metod bio kompleksniji odnosno jednostavniji za odrzavanje.
Ali ako je ovo bolja praksa od one gde se sve cuva u jednoj tabeli onda mi nema mnogo izbora. Uglavnom hvala na vasim komentarima.

Pozdrav
[ Simke @ 05.11.2004. 05:28 ] @
Najefikasnije bi ti bilo:

Code:

SELECT *
FROM Radnik
     INNER JOIN Mesto ON Radnik.sifraMestaStanovanja = Mesto.SifraMesta
          AND Mesto.SifraMesta = 11000


Jer ce u ovom slucaju filter biti uradjen pre join-a - znaci join povezuje tabelu Radnik sa vec filtriranim podatcima.
[ Zidar @ 05.11.2004. 13:58 ] @
Wow, Simke svaka cast. Da li ovo znaci da kad se stavi
Code:

SELECT *
FROM Radnik
     INNER JOIN Mesto ON Radnik.sifraMestaStanovanja = Mesto.SifraMesta
          WHERE Mesto.SifraMesta = 11000

onda se filterovanje desi POSLE JOINa, a kad umesto WHERE prosirim ON iskaz onda se filterovanje desava PRE JOINa? Sa WHERE znaci prvo povuce SVE u join (na primer 50,000 rekorda) pa onda isfiltrira 5,000 koji mi trebaju, a sa prosirenim ON se nikad ne pokupi svih 50,000 nego odmah samo 5,000? Fenomenalno.


Za negyxo: dodavanje polja za godinu je jedino sto mozes da uradis u datoj situaciji. Medjutim, menjanje sifri radnika iz godine u godinu je potpuno nepotreban i glupa operacija i treba videti da li to narucilac sistema moze da promeni. Nije problem u tvom dizajnu, problem je u biznis procesu i to tvoj dizajn ne moze da ispravi.

:-)
[ sasas @ 05.11.2004. 14:17 ] @
Citat:

onda se filterovanje desi POSLE JOINa, a kad umesto WHERE prosirim ON iskaz onda se filterovanje desava PRE JOINa? Sa WHERE znaci prvo povuce SVE u join (na primer 50,000 rekorda) pa onda isfiltrira 5,000 koji mi trebaju, a sa prosirenim ON se nikad ne pokupi svih 50,000 nego odmah samo 5,000? Fenomenalno.


Nije fenomenalno, mene je samo malo dovelo u zabunu ;)

Testirao sam oba nacina kod sebe i Estimated execution plan je isti, prvo se selektuju zapisi, i samo nad selektovanim zapisima se radi JOIN.
Dakle, mssql server ipak optimizuje ovu vrstu upita, cime mozganja tipa 'sta je bolje/brze/vece' postaju obsolete i na znacaju dobija samo citljivost sql kooda.

@simke: da li za tvoju tvrdnju (da postoji razlika u brzini u zavisnosti kako ukucas sql) imas neki slucaj koji ja previdjam?

ss.
[ negyxo @ 05.11.2004. 19:47 ] @
Zidar nisam ni mislio da se menjaju sifre radnika iz godine u godinu jer tek onda bi doslo do konflikta, tj. ne bi znao koji je koji radnik u kojoj godini.

A sto se tice onog join-ranja mislim da je bolje kako sasas kaze (ili sam ja lenj) a to je da se prepusti serveru neka radi taj posao. Postavio sam to kao pitanje zato sto je join-iranje jedna od cescih operacija pa sam zato mislio da bi bilo dobro nauciti kako optimizovati. Inace i ja sam posao tom logikom kao sto si ti zidar objasnio pa sam zato i priupitao.

Za sad toliko, Pozdrav








[ Simke @ 05.11.2004. 21:29 ] @
Teoretski bi server prvo trebao da uradi join pa tek onda filter na primeru koji sam ja pokazao, a sad kakav execution plan postane u praksi...
Nisam tacno siguran sta se sve uzima u obzir kada se pravi execution plan, probaj kada imas neki malo kompleksniji query sa vise joina.
[ sasas @ 05.11.2004. 22:07 ] @
Citat:
Simke: probaj kada imas neki malo kompleksniji query sa vise joina.


7 joina :) radi dobro automatika. tabele su neke sa klasterovanim indexima, neke sa 'obicnim'. mssql je jako dobar sto se tice tih stvari, a vidis ne znam kako stoje stvari sa drugim dbmsovima, pre svega ovim besplatnim i manjim.

moje iskustvo je da treba pisati skolski kood, koji svaki sql programer razume, a zaista mi se retko desavalo da promenom sql-a znacajno ubrzam stvari (iskljucujem slucajeve sa kursorima i hintovima, ali to prevazilazi ovaj thread)

ss.
[ Simke @ 06.11.2004. 04:38 ] @
Onda ocigledno napravi dobar execution plan:)
Doduse ja uglavnom koristim MS SQL 7, verzija 2000 je verovatno "pametnija".
Bas cu pogledati u ponedeljak kad odem na posao da vidim ima li razlike u planu na sedmici...