Jak zrobić repeat w kursorze ms sql

0

Witam,
Potrzebuję pilnie pomocy. Mam procedurę z kursorem i nie wiem jak zrobić by się kursory działały w pętli - zwiększały się po każdym razie o jeden wiersz. Pzdr

DECLARE @I INT
DECLARE @lp INT
DECLARE @lp2 INT
DECLARE @KodP INT
DECLARE @KodP2 INT
DECLARE @data Date
DECLARE @Data2 Date
DECLARE @Kurs INT
DECLARE @Kurs2 INT

declare KRentownosc SCROLL cursor for
select lp, KodP,Data, Kurs from TRentownosc ORDER BY lp asc
OPEN KRentownosc;
FETCH NEXT FROM KRentownosc INTO @lp,@KodP,@data,@Kurs;
FETCH PRIOR FROM KRentownosc INTO @lp2,@KodP2,@Data2,@Kurs2;
begin
if @lp2 is null
update TRentownosc SET TRentownosc.Rentownosc=7 where lp=1
else
if @KodP=@KodP2
update TRentownosc SET TRentownosc.Rentownosc= ((@Kurs2/@Kurs)-1) where lp=@lp
else
update TRentownosc SET TRentownosc.Rentownosc=7 where lp=@lp
end
CLOSE KRentownosc
DEALLOCATE KRentownosc
SELECT *,@lp,@lp2 FROM TRentownosc
go

1

Za kursory obcina się ręce, nie możesz tego zrobić Update'm?

OPEN KRentownosc;
FETCH PRIOR FROM KRentownosc INTO @lp2,@KodP2,@Data2,@Kurs2;
FETCH NEXT FROM KRentownosc INTO @lp,@KodP,@Data,@Kurs;
WHILE @@FETCH_STATUS=0
	begin
		if @lp2 is null
		update TRentownosc SET TRentownosc.Rentownosc=7 where lp=1
		else
			BEGIN
				if @KodP=@KodP2 
				update TRentownosc SET TRentownosc.Rentownosc= ((@Kurs2/@Kurs)-1) where lp=@lp
				else
				update TRentownosc SET TRentownosc.Rentownosc=7 where lp=@lp 
			END
		FETCH PRIOR FROM KRentownosc INTO @lp2,@KodP2,@Data2,@Kurs2;
		FETCH NEXT FROM KRentownosc INTO @lp,@KodP,@Data,@Kurs;
	end
CLOSE KRentownosc  
DEALLOCATE KRentownosc
SELECT *,@lp,@lp2 FROM TRentownosc
0

Wersja bez kursora, (zakładam, ze kolumna lp jest inkrementowana, ale ma luki:

WITH ctePonumerowany AS (
SELECT 
    lp
    ,ROW_NUMBER() OVER (ORDER BY lp) r 
FROM
    TRentownosc
)
 
 
UPDATE
    TRentownosc
SET
    Rentownosc = CASE 
                    WHEN p.lp IS NULL OR isnull(TRentownosc.kurs,0)=0 THEN 7
                    ELSE (p.kurs/TRentownosc.kurs)-1
                END
FROM
    TRentownosc 
    inner join (SELECT 
                    b.lp B
                    , p.lp P 
                FROM 
                    ctePonumerowany b
                    left join ctePonumerowany p ON b.r-1 = p.r) lp ON lp.B = TRentownosc.lp
    Left join TRentownosc P ON p.lp = lp.P AND TRentownosc.kodp = p.kodp
0

Bardzo dziękuję za odpowiedź. Chciałem to zrobić kursorami, gdyż chcę się nauczyć poruszać tą metodą po tabeli, ale dzięki również za drugą metodę.
Obie metody jednak nie działają ;-)
Pierwsza nic nie robi, a w drugiej jest błąd - nazwa TRentownosc jest dwuznaczna - sam nie wiem dlaczego.

Załączyłem obraz tabeli TRentownosc.
Celem jest wypisanie pola rentowność na dany dzien wg wzoru r=(Kurs z poprzedniego dnia/Kurs z aktualnego dnia)-1
Oczywiście w ramach każdego KodP. Jak nie ma poprzedniego dnia to wpisujemu 0 (w moim przykładzie było 7 - testowo)

Jeśli mogę liczyć na jakąś pomoc, to będę bardzo wdzięczny, gdyż chcę się tego nauczyć.
Jeśli, ktoś może polecić fajną książkę z dobrymi przykłądami - taką bardziej zaawansowaną to również będę wdzięczny
Pzdr

0

Dwuznaczność w nazwie tabeli polegała na tym, że w klauzuli from do każdej tabeli użyłem aliasu, stąd silnik nie wiedział co tak naprawdę chce aktualizować, poprawiłem w źródle posta.

Kursor u ciebie się zapętlał i był ciągle na pozycji 1, poprawnie to powinno być tak:

DECLARE @KodP2 INT
DECLARE @Data Date
DECLARE @Data2 Date
DECLARE @Kurs INT
DECLARE @Kurs2 INT

declare KRentownosc SCROLL cursor for 
select lp, KodP,Data, Kurs from TRentownosc ORDER BY lp asc
OPEN KRentownosc;
FETCH FIRST FROM KRentownosc INTO @lp,@KodP,@Data,@Kurs;
WHILE @@FETCH_STATUS=0
    BEGIN
		PRINT @LP
		PRINT @LP2
        IF @lp2 IS NULL
			UPDATE TRentownosc SET TRentownosc.Rentownosc=7 WHERE lp=1
        ELSE
            BEGIN
                IF @KodP=@KodP2 
					UPDATE TRentownosc SET TRentownosc.Rentownosc= ((@Kurs2/@Kurs)-1) WHERE lp=@lp
                ELSE
					UPDATE TRentownosc SET TRentownosc.Rentownosc=7 WHERE lp=@lp 
            END
        FETCH RELATIVE 0 FROM KRentownosc INTO @lp2,@KodP2,@Data2,@Kurs2;
        FETCH NEXT FROM KRentownosc INTO @lp,@KodP,@Data,@Kurs;
    END
CLOSE KRentownosc  
DEALLOCATE KRentownosc
SELECT *,@lp,@lp2 FROM TRentownosc

Musisz zwrócić uwagę że Fetch przesuwa kursor, tak naprawdę nie musisz sie cofać tylko pobrać to co masz w bieżącym wierszu i przejść do następnego, ogólnie mógłbyś nawet przekopiować zmienne.

Nie zwróciłem na to uwagi (jak pisałem za kursory ucina się ręce, więc słabo je znam ;)), ale zapis:

 FETCH PRIOR FROM KRentownosc INTO @lp2,@KodP2,@Data2,@Kurs2;
    FETCH NEXT FROM KRentownosc INTO @lp,@KodP,@Data,@Kurs;

Powoduje, że przechodzę do rekordu 0, później do 1 i tak w kółko stąd pętla nigdy nie przerobiła innych rekordów.

To co mi się nasuwa to fakt, że kolejność lp wcale nie determinuje poprawności wykonania zapytania, ponieważ wystarczy, że dopiszesz kolejny rekord dla kodP = 10 z datą 2004-01-05 i nie policzysz rentowności, kolejność powinieneś wymuszać po kodp i data, wtedy będzie prawidłowo. Zresztą tu aż "prosi się" o klucz na polach kodp i data, wtedy nawet prościej zrobić zapytanie:

UPDATE
    TRentownosc
SET
    Rentownosc = CASE 
                    WHEN p.lp IS NULL OR isnull(TRentownosc.kurs,0)=0 THEN 7
                    ELSE (p.kurs/TRentownosc.kurs)-1
                END
FROM
   TRentownosc 
   Left join TRentownosc P ON P.Data = DateAdd(d,-1,TRentownosc.Data) AND TRentownosc.kodp = p.kodp

Warto też zwrócić uwagę na typy, bo dla typu int dzielenie 2/4 = 0

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