Pętla w funkcji T-SQL

0

Cześć,
czy byłby mi ktoś w stanie pomóc napisać funkcję która po podaniu dwóch argumentów wyświetli mi pierwotne wartości z pewnego przedziału. Najprościej będzie wytłumaczyć to na przykładzie, otóż:
Podająć wartości z kolumny ID2,LP2 (1)
screenshot-20220529114841.png
Chciałbym uzyskać wynik wartości kolumn ID1,LP1(1). Wartości w kolumnach ID1 i ID2 są zawsze unikatowe, tylko lp może wystąpić kilka razy.
Wiem że trzeba do tego użyć pętli jednak nie do końca mi to wychodzi. Byłym wdzięczy za pomoc

0

A dlaczego akurat chcesz uzyskać 1 rekord?

0
Panczo napisał(a):

A dlaczego akurat chcesz uzyskać 1 rekord?

Ponieważ specyfika tych danych wymaga pokazania pierwotnego LP,ID. Przykładowo ID to ID adresu a LP to numer paczki w spisie (jak widać zmienny). Chcę wiedzieć jaki jest pierwotny adres mojej paczki oraz numer na pierwszym spisie LP. Gdy ID1 i LP1 nie ma sie już do czego odwołać niżej to jest mój wynik.

0

@Tylywizor:

Za ChRL nie rozumiem opisu słownego (ze ścisłością jak programista rozwiązujący problem). Za nic nie rozumiem, co oznaczają czerwone prostokąty, jaka to jest zależność
a) nie dajesz kodu, który próbujesz (tytuł mówi o funkcji)
b) najstarsi Indianie nie wiedzą (ale przypuszczają) że jest jakiś spory problem w projekcie bazy
c) pozyskanie jednego rekordu w MS-SQL możesz zrobić z TOP 1, ewentualnie sortując DESC - choc za cholerę nie rozumiem, o co ci chodzi
d) używasz nagle jakiś nazw z domeny (adres, spis) nigdy tego nie definiując

0
ZrobieDobrze napisał(a):

@Tylywizor:

Za ChRL nie rozumiem opisu słownego (ze ścisłością jak programista rozwiązujący problem). Za nic nie rozumiem, co oznaczają czerwone prostokąty, jaka to jest zależność
a) nie dajesz kodu, który próbujesz (tytuł mówi o funkcji)
b) najstarsi Indianie nie wiedzą (ale przypuszczają) że jest jakiś spory problem w projekcie bazy
c) pozyskanie jednego rekordu w MS-SQL możesz zrobić z TOP 1, ewentualnie sortując DESC - choc za cholerę nie rozumiem, o co ci chodzi
d) używasz nagle jakiś nazw z domeny (adres, spis) nigdy tego nie definiując

Dobrze, no może nie wyjaśniłem tego w dobry sposób(jest to mój pierwszy post więc proszę o wyrozumiałość). Zależność jest taka że Adres1 i LP1 to adresy pierwotny paczki i jej lp. Jeżeli paczka przemieszcza się do innego adresu mamy wpis w Adres2 i LP2. Czyli w pierwszym wierszu z Adresu1 paczka o numerze 1 (LP1), przechodzi na Adres2 i dostaje nr. 2(LP2). Jeżeli z Adresu2 paczka idzie dalej to pojawia się nowy wiersz i tak dalej. Adresy zawszę są unikatowe czyli mamy powiązanie (adres,lp) 100,1 -> 200,2 -> 300,3 -> 500,5 -> 800,8. Podając ostatnie wartości czyli 800,8, chciałbym wiedzieć skąd moja paczka wystartowała.
screenshot-20220529130522.png
Prawdopodobnie tak jak kolega mówi baza jest źle skonstruowana, jednakże działam na tym co mam. Co do kodu, niestety jak już wspomniałem nie jestem wstanie go zrobić dlatego napisałem z prośba o pomoc. Co do funkcji to właśnie ona przyszła mi do głowy, ponieważ chciałbym podawac dwa parametry. Jeżeli nie da sie tego zrobić to trudno. Jest to oczywiście przykład dla zobrazowania problemu, poniżej wklejam mojego inserta.

Create table dbo.paczki (ADRES1 INT, LP1 INT, ADRES2 INT, LP2 INT)
INSERT INTO DBO.paczki (ADRES1 , LP1 , ADRES2 , LP2 )
VALUES (100,1,200,2)
,(200,2,300,3)
,(300,3,500,5)
,(500,5,800,8)

1

Coś mi się wydaje, że nie zwracasz nam wszystkich pól z tabeli, bo mozna założyć że "pierwszym rekordem" jest ten który nie ma swojej pary w adres i lp2.
Ale brakuje rozróżnienia czego dotyczy przesyłka:

select *
from paczki s 
where not  exists(select * from paczki p where p.adres2=s.adres1 and p.lp2=s.lp1)

Powiedzmy że masz kolumnę id z wyróżnikiem paczki wtedy można rozbudować to tak:

select *
from paczki s 
where 
   id in (select id from paczki where adres2=800 and lp2=8)
and not  exists(select * from paczki p where p.adres2=s.adres1 and p.lp2=s.lp1)
0
Panczo napisał(a):

Coś mi się wydaje, że nie zwracasz nam wszystkich pól z tabeli, bo mozna założyć że "pierwszym rekordem" jest ten który nie ma swojej pary w adres i lp2.
Ale brakuje rozróżnienia czego dotyczy przesyłka:

select *
from paczki s 
where not  exists(select * from paczki p where p.adres2=s.adres1 and p.lp2=s.lp1)

Powiedzmy że masz kolumnę id z wyróżnikiem paczki wtedy można rozbudować to tak:

select *
from paczki s 
where 
   id in (select id from paczki where adres2=800 and lp2=8)
and not  exists(select * from paczki p where p.adres2=s.adres1 and p.lp2=s.lp1)

Udało mi się to osiągnąć jednak w dość nietypowy sposób bo funkcją skalarną.

alter FUNCTION dbo.funkcja(@lp decimal (10,1))

returns decimal(10,1)
as
begin
declare @temp decimal(10,1)
while 0<1

begin
set @temp = null
select @temp = convert(decimal(10,1), adres1 + lp1/10.0 ) 
from dbo.paczki where adres2 =CONVERT(int, @lp)
and lp2 =substring(CONVERT(varchar(10),@lp),charindex('.',@lp)+1,100)
if @temp is null break else set @lp = @temp

end

return @lp
end

---------------------
select *,dbo.funkcja(800.8)from dbo.paczki

Wygląda to jak wygląda ale działa, może uda mi sie to przerobić na funkcje tabelaryczną, albo ktoś by mi pomógł :)

2

A musi być koniecznie funkcja? Nie lepiej zrobić sobie widok? Dziś chcesz odpytać bazę o jedną paczkę, a kiedyś może Ci się zamarzyć zrobić jakiś listing - będziesz wtedy budował osobną funkcję tabelaryczną i kleił dane w pętli? Nie tędy droga.

Polecam zrobić coś takiego:

CREATE VIEW dbo.v_paczki
AS
	WITH CTE
	AS (
		SELECT ADRES2, LP2, ADRES1, LP1, 0 AS KROK
		FROM PACZKI
	
		UNION ALL
	
		SELECT CTE.ADRES2, CTE.LP2, P.ADRES1, P.LP1, CTE.KROK+1
		FROM CTE
		JOIN PACZKI P ON P.ADRES2 = CTE.ADRES1
			AND P.LP2 = CTE.LP1
		)
	SELECT CTE.*, CASE WHEN EXISTS(SELECT * FROM PACZKI p0 WHERE p0.ADRES2=CTE.ADRES1 AND p0.LP2=CTE.LP1) THEN 0 ELSE 1 END OSTATNI
	FROM CTE

I teraz odpytujesz

SELECT * FROM V_PACZKI
WHERE 
	ADRES2=800 AND LP2=8
	AND OSTATNI=1 --jeśli wykomentujesz tę linię, zapytanie zwróci całą historię tej jednej paczki
0
Fac napisał(a):

A musi być koniecznie funkcja? Nie lepiej zrobić sobie widok? Dziś chcesz odpytać bazę o jedną paczkę, a kiedyś może Ci się zamarzyć zrobić jakiś listing - będziesz wtedy budował osobną funkcję tabelaryczną i kleił dane w pętli? Nie tędy droga.

Polecam zrobić coś takiego:

CREATE VIEW dbo.v_paczki
AS
	WITH CTE
	AS (
		SELECT ADRES2, LP2, ADRES1, LP1, 0 AS KROK
		FROM PACZKI
	
		UNION ALL
	
		SELECT CTE.ADRES2, CTE.LP2, P.ADRES1, P.LP1, CTE.KROK+1
		FROM CTE
		JOIN PACZKI P ON P.ADRES2 = CTE.ADRES1
			AND P.LP2 = CTE.LP1
		)
	SELECT CTE.*, CASE WHEN EXISTS(SELECT * FROM PACZKI p0 WHERE p0.ADRES2=CTE.ADRES1 AND p0.LP2=CTE.LP1) THEN 0 ELSE 1 END OSTATNI
	FROM CTE

I teraz odpytujesz

SELECT * FROM V_PACZKI
WHERE 
	ADRES2=800 AND LP2=8
	AND OSTATNI=1 --jeśli wykomentujesz tę linię, zapytanie zwróci całą historię tej jednej paczki

Dzieki, takie rozwiązanie jest nawet lepsze :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1