Navigacija
Lista poslednjih: 16, 32, 64, 128 poruka.

TOP u rekurzivnom delu CTE

[es] :: MS SQL :: TOP u rekurzivnom delu CTE

[ Pregleda: 2766 | Odgovora: 8 ] > FB > Twit

Postavi temu Odgovori

Autor

Pretraga teme: Traži
Markiranje Štampanje RSS

vujkev
Beograd

Član broj: 8072
Poruke: 1347
212.200.241.*



+104 Profil

icon TOP u rekurzivnom delu CTE13.10.2010. u 23:47 - pre 164 meseci
potrebno mi je da u rekurzivnom delu CTE dobijem samo jedan/naredni slog koji zavisi od prethodnog. Na žalost TOP i order by ne smem da stavim u ovom delu funkcije pa me zanima da li mogu nekako da uzmem samo prvi slog na koji naiđem sa nekim sortom

u ovom primeru (malo je glup) hoću da dobijem samo redove gde je ID = 1, 3, 6 ... tj. gde je ID za minimum dva broja veći od predhodnog sloga koji sam dobio.
Code:

create table #test(id int identity(1,1), datum datetime)
insert into #test select getdate()
insert into #test select getdate()
insert into #test select getdate()
insert into #test select getdate()
insert into #test select getdate()
insert into #test select getdate()
insert into #test select getdate()

delete from #test where id = 5

;with e as (
select *, 1 as l from #test where id=1
union all
    select  top 1 t.*, e.l+1  -- top 1 ne sme da stoji ovde
    from e 
    inner join #test t on t.id>=e.id+2
)
select * from e



Naučio sam...
Da je važnije biti ljubazan nego biti u pravu
 
Odgovor na temu

Zidar
Canada

Član broj: 15387
Poruke: 3085
*.100.46-69.q9.net.



+79 Profil

icon Re: TOP u rekurzivnom delu CTE14.10.2010. u 14:10 - pre 164 meseci
Ne razumem sta pokusavas da uradis. Mozda ti i ne treba rekurzija... Postavi stvaran primer i stvarno pitanje.
 
Odgovor na temu

vujkev
Beograd

Član broj: 8072
Poruke: 1347
*.telekom.rs.



+104 Profil

icon Re: TOP u rekurzivnom delu CTE14.10.2010. u 14:24 - pre 164 meseci
problem je sledeći

imam tabelu sa lokacijama vozila (lat i lon) i datum kad je bio na toj poziciji. Treba da uzmem sve lokacije, počevši od zadnje, gde je rastojanje između te pozicije i sledeće pozicije npr 300m. Konkretan primer za sad ne mogu da dam, ali potrudiću se da u toku dana napravim neki kod.

nadam se da je sad jasnije
Naučio sam...
Da je važnije biti ljubazan nego biti u pravu
 
Odgovor na temu

Zidar
Canada

Član broj: 15387
Poruke: 3085
*.100.46-69.q9.net.



+79 Profil

icon Re: TOP u rekurzivnom delu CTE14.10.2010. u 17:20 - pre 164 meseci
OK, sad je bolje.

Mislim da ti ne treba iterativni CTE uopste. Ti poredis svaku lokaciju sa onom pre nje, tu ne trebaju iteracije, obican SELECT je dovoljan, i u tom istom prolazu izracunas udaljenost na osnovu koordinata. Onda samo izaberes one redove gde je udaljenost veca od X metara.

Da napise query, moze ti biti od pomoci CTE, ali bez iteracija. Ovako nekako (pseudo kod):
Code:

WITH Redosled AS
(
SELECT 
Vozilo, Vreme, Latitude,  Longitude
, RedosledLokacije = Row_Number() OVER(PARTITION BY Vozilo ORDER BY Vreme)
FROM LokacijeVozilaUVremenu
)
, Udaljenosti AS
(
SELECT 
A.Vozilo,   A.Latitude,  A.Longitude,  A.Redosled
, B.Vozilo, A.Latitude,  A.Longitude, B.Redosled
, Distance = (SQRT(A.Latitude-B.Latitude)^2 + (A.Longitude-B.Longitude)^2))
FROM Redosled AS A
JOIN Redosled AS B ON A.Vozilo = B.Vozilo AND B.RedosledLokacije = A.RedosledLokacije  + 1
)
SELECT *
FROM Udaljenosti
WHERE Udaljenost >= 300


Prvo si dodelio redne brojeve lokacijama, u izrazu 'Redosled' . Funkcija Row_Number() je kljucan za resenje.

Zatim u delu 'Udaljenosti ' izracunas daljine izmedju svake dve uzastiopne lokacije za svako vozilo. Ovde mozda treba da se igras sa uslovom za JOIN, da ne izgubis poslednji par.

Kad si lepo izracunao daljine, na kraju prikazes samo one parove koji imaju daljinu vecu od trazenih 300 metara.

I sve to uz pomoc CTE i bez iteracija.

Moglo je i ebz CTE, ali bi se redosled kverija poptpuno preokrenuo i identicno resenje en bi bilo citljivo.

 
Odgovor na temu

vujkev
Beograd

Član broj: 8072
Poruke: 1347
*.telekom.rs.



+104 Profil

icon Re: TOP u rekurzivnom delu CTE14.10.2010. u 17:50 - pre 164 meseci
Ovo je dobro rešenje, ali moje objašnjenje problema nije baš sjajno :(

Posmatrajmo tačke 1-10. Svaka tačka/broj predstavlja jedan unos u tabelu sa svojom pozicijom i vremenom.

1-----2------3------4---------5-----------6----7---8------9-------10

Meni je potrebno da uzmem tačku 1 i sledeću tačku koja je minimum 15 crtica udaljena od tačke 1. U ovom slučaju to bi značili da upit treba da mi vrati tačke 1, 4, 6 i 10. Ako dobro razumem tvoj kod, rastojanje u njemu se računa samo između susednih tačaka, što u ovom slučaju znači da bih dobio samo tačku 1 ili ni jednu tačku pošto između tačaka nigde nemam 15 i više crtica.


Naučio sam...
Da je važnije biti ljubazan nego biti u pravu
 
Odgovor na temu

Zidar
Canada

Član broj: 15387
Poruke: 3085
*.100.46-69.q9.net.



+79 Profil

icon Re: TOP u rekurzivnom delu CTE14.10.2010. u 18:29 - pre 164 meseci
Nema veze, idemo dalje.

Ako sam dobro razumeo, hoces ovo:
1) Vozilo je proslo kroz tacke 1,2,3..10, bas u tom redosledu
2) IZmedju svake dve tacke moze se odrediti daljina, ili je poznata
3) Krenem od tacke 1 i trazim sledecu tacku gde je kumulativna daljina od tacke 1 veca od 15 ili vise crtica. => Tacka 4
4) Sad polazim od tacke 4 i trazim prvu koja je ukupno daleko 15 ili vise crtica => tacka 6
5) Sad idem od tacke 6 na dalje, i sabiram razdaljine izmedju susednih tacaka sve dok ne dobije 15 ili vise tacaka => tacka 10.

Ako sam dobro shvatio zadatak, to je vec sasvim druga prica. Veoma interesantan problem, prava mozgalica. Mozda treba rekurzivi CTE, mozda i ne, a mozda i ne moze kroz CTE, nego kroz nekakvu petlju u funkciji koja vraca tabelu.... Moram da razmislim, pa cu ti javiti, verovatno sutra. Mnogo je tesko za odmah, ovde treba mladji mozak nego moj

Ispravi me ako nisam shvatio zadatak kako valja.
 
Odgovor na temu

vujkev
Beograd

Član broj: 8072
Poruke: 1347
*.telekom.rs.



+104 Profil

icon Re: TOP u rekurzivnom delu CTE14.10.2010. u 18:33 - pre 164 meseci
Uh, dobro je, lepo sam objasnio. Baš mi to treba :)
Naučio sam...
Da je važnije biti ljubazan nego biti u pravu
 
Odgovor na temu

vujkev
Beograd

Član broj: 8072
Poruke: 1347
212.200.241.*



+104 Profil

icon Re: TOP u rekurzivnom delu CTE15.10.2010. u 00:25 - pre 164 meseci
Za sad sam uspeo da uradim ovo uz pomoć CTE i jedne funkcije.
Pošto u rekurzivnom delu CTE-a ne mogu da se ograničim na samo jedan slog, onda tu pozivam funkciju koja mi vraća samo jedan red koji odgovara zahtevu. Za sad kod izgleda ovako, ali nije još dovršen jer možda postoji neko bolje rešenje. Ukoliko postoji spreman sam da promenim sve :)
LocationsGetNextLocation je funkcija koja vraća samo jedan red iz tabele tblLog (znam da tabele ne treba da imaju "tbl" u nazivu, ali ovo je nasleđena baza i nemam baš neke mogućnosti da menjam nazive :) )

Code:

declare @Date as datetime
declare @EndDate as datetime
set @Date = '2008-07-11'
set @EndDate = DATEADD(d, 1, @Date)
select @Date, @EndDate 
;with e as(
    select top 1 Lat, Long, IDPeriferica, Data , 1 as lvl
        from tblLog 
        where IDPeriferica = 4 
        and Data between @Date and @EndDate 
        order by Data desc
    union all
    select t.* , e.lvl+1
        from e
        cross apply
        [LocationsGetNextLocation] (e.IDPeriferica, @Date, e.Data, 10, e.Lat, e.Long) as t
)
select * from e

Naučio sam...
Da je važnije biti ljubazan nego biti u pravu
 
Odgovor na temu

Zidar
Canada

Član broj: 15387
Poruke: 3085
*.100.46-69.q9.net.



+79 Profil

icon Re: TOP u rekurzivnom delu CTE15.10.2010. u 19:48 - pre 164 meseci
Svaka cast na resenju. Doduse, tesko je razmeti bez aktuelnih podataka, ali se nazire sustina:

1. za bilo koju zadtu tacku, dobije se tacka udaljena od nje zadatih X metara pomocu funkcije LocationsGetNextLocation
2. CTE pod imenom 'e' (WITH e) prodje kroz sve tacke koje su povezan u nizu, i za ceo niz izvrsi funkciju LocationsGetNextLocation
, uz pomoc CROSS APPLY

Posto LocationsGetNextLocation vraca tacno jedan red, mozda nije moralo CROSS APPLY, mozda je mogao obican JOIN (mozda, nisma siguran, ne vidim stvarnu tabelu).

Ne verujem da ce ovo razumeti puno ljudi, pa da bi bar nesto izaslo opste orisno, evo kako se pomocu rekurzivnog CTE racuna kumulativ. Probacu zatim da dam primer koji odedjuje za svaku tacku neku sledecu tacku koja je na daljini D>= const.

Prvo da napravimo tabelu sa nekoliko parova tacaka:
Code:

IF OBject_ID('tempdb..#Distances') IS NOT NULL DROP TABLE #Distances
GO
CREATE TABLE #Distances (T1 varchar(1), T2 varchar(1), Distance int)
;
INSERT INTO #Distances VALUES ('A','B',120);
INSERT INTO #Distances VALUES  ('B','C',23);
INSERT INTO #Distances VALUES  ('C','D',56);
INSERT INTO #Distances VALUES  ('D','E',14);
INSERT INTO #Distances VALUES  ('E','F',78);
INSERT INTO #Distances VALUES  ('F','G',13);
INSERT INTO #Distances VALUES  ('G','H',65);
INSERT INTO #Distances VALUES  ('H','I',12);
INSERT INTO #Distances VALUES  ('I','J',89);
INSERT INTO #Distances VALUES  ('J','K',85);
INSERT INTO #Distances VALUES  ('K','M',74);
INSERT INTO #Distances VALUES  ('M','N',52);
INSERT INTO #Distances VALUES  ('N','O',16);
INSERT INTO #Distances VALUES  ('O','P',37);
INSERT INTO #Distances VALUES  ('P','Q',29);
;

Onda u istoj skripti izvrsite ovo:
Code:

WITH RowOrder AS
(-- potrebno je dodeliti Seq - redni broj redovima u tabeli Distance
 -- posto ORDER BY ne ide, koristiimo Row_number funkciju
SELECT 
Seq = row_number() OVER (ORDER BY T1)
, T1, T2, Distance
FROM #Distances
)
, CumulativeCTE AS
( -- racunanje kumulativne daljine medju tackama
  -- klasican priemr rekurzije u CTE
SELECT Seq, T1, T2, Distance, Cumul = Distance 
FROM RowOrder
WHERE Seq=1
UNION ALL
SELECT R1.Seq, R1.T1, R1.T2, R1.Distance
, Cumul = C.Cumul + R1.Distance
FROM CumulativeCTE AS C   
JOIN RowOrder AS R1 ON R1.Seq = C.Seq + 1
)
SELECT Seq,T1,T2,Distance, Cumul 
FROM CumulativeCTE
ORDER BY 1,2

Sad dolazi zapetljani deo - kako izracunati za svakod zadatih tacaka koja je njena najlbliza tacka takva da je daljina barem 150 jedinica.
Na kod za racunanje kumulativa dodacemo jos nekoliko koraka. Tako radde CTE (common table expressions) - resavamo problem korak po korak i koraci se zapisuju u redosledu resavanja. Svaki korak poziva neki od prethodnih koraka.

Rezultat ovog kverija je niz parova tacaka, takvih da je tacka T2 udaljena od T1 barem 150 i to je prva takva tacka (sve ostale su takodje udaljene vise od 150 jedinica ali ih ne prikazujemo). Rezultat je prikazan na dnu prozora sa kodom. Moze se rucno proveriti lako da je rezultat tacan.
Code:

;
SET NOCOUNT OFF
;
WITH RowOrder AS
(-- potrebno je dodeliti Seq - redni broj redovima u tabeli Distance
 -- posto ORDER BY ne ide, koristiimo Row_number funkciju
SELECT 
Seq = row_number() OVER (ORDER BY T1)
, T1, T2, Distance
FROM #Distances
)
, CumulativeCTE AS
( -- racunanje kumulativne daljine medju tackama
  -- klasican priemr rekurzije u CTE
SELECT Seq, T1, T2, Distance, Cumul = Distance 
FROM RowOrder
WHERE Seq=1
UNION ALL
SELECT R1.Seq, R1.T1, R1.T2, R1.Distance
, Cumul = C.Cumul + R1.Distance
FROM CumulativeCTE AS C   
JOIN RowOrder AS R1 ON R1.Seq = C.Seq + 1
)
--SELECT * FROM CumulativeCTE ORDER BY 1,2
, DistanceBetweenPoints AS
(-- D = daljina svake tacke od svih tacaka koje dodju posle nje:
-- ovo vraca 105 redova, daljina izmedju svaka dve tacke
SELECT A.Seq, A.T1 AS PointA, B.T1 As PointB, 
 A.Cumul AS A_Cumul, B.Cumul AS B_Cumul
, DistanceT1_T2 = B.Cumul - A.Cumul
FROM CumulativeCTE A
JOIN CumulativeCTE AS B 
ON A.Seq < B.Seq
)
, ValidDistantPairs AS
( -- Parovi tacaka koji su udaljeni D >= 150
  -- Za svaku PointA treba izabrati onaj par koji imam minimalno D
  -- Ovaj deo moze da se preskoci, tu je samo za prikaz, ko hoce da ide korak po korak
  -- Ovo vraca 72 reda, izbacili smo sve daljine koje su manje od 150 jedinica
SELECT
Seq, PointA, PointB, DistanceT1_T2
FROM DistanceBetweenPoints 
WHERE DistanceT1_T2 >= 150    -- neka se traze tacke koje su na daljini vecoj od 150
)
--SELECT * FROM DistantPairs ORDER BY 1,2
, MinDist AS
(
SELECT Seq, MIN(DistanceT1_T2) AS MinDist
FROM ValidDistantPairs
GROUP BY Seq
)
--SELECT * FROM MinDist
-- Konacno, od svih tacaka koje su od zdatae tacke udaljene vise nego 150 jedinica
-- izabiramo najblizu
SELECT 
V.Seq, PointA AS T1, PointB AS T2, DistanceT1_T2 
FROm ValidDistantPairs AS V
JOIN MinDist AS M ON V.Seq = M.Seq AND V.DistanceT1_T2 = M.MinDist
;

/* Rezultat
                 Seq T1   T2   DistanceT1_T2
-------------------- ---- ---- -------------
                   1 A    E              171
                   2 B    F              161
                   3 C    G              170
                   4 D    G              156
                   5 E    I              179
                   6 F    I              166
                   7 G    J              186
                   8 H    J              174
                   9 I    K              159
                  10 J    O              179

(10 row(s) affected)
*/


:-)

Hvala Vujkevu na odlicnom pitanju. Ko bi rekao za sta se sve moze SQL upotrebiti.....I bez kursora.

:-)
 
Odgovor na temu

[es] :: MS SQL :: TOP u rekurzivnom delu CTE

[ Pregleda: 2766 | Odgovora: 8 ] > FB > Twit

Postavi temu Odgovori

Navigacija
Lista poslednjih: 16, 32, 64, 128 poruka.