Przejdź do treści
FullstackDev

Kursor

Pętlę WHILE, możemy też wykorzystać w połączeniu z kursorem. W SQL Serverze kursor to mechanizm, który pozwala na iterowanie po zbiorze wierszy zwróconych przez zapytanie. Jest to przydatne, gdy mamy potrzebę przetwarzać dane wiersz po wierszu, na przykład wykonując operacje lub obliczenia na każdym wierszu osobno.

Aby zdefiniować kursor, posłużymy się następującym poleceniem:

DECLARE cursor_name CURSOR FOR
SELECT column1, column2, ...
FROM table_name
WHERE condition;

Gdzie cursor_name to nazwa kursora, column1, column2, ... to kolumny, które chcemy zwrócić, table_name to nazwa tabeli, a condition to warunek, który chcemy zastosować do zwróconych wierszy.

Następnie otwieramy kursor i pobieramy pierwszy wiersz za pomocą polecenia FETCH:

OPEN cursor_name;
FETCH NEXT FROM cursor_name INTO @variable1, @variable2, ...;

Gdzie @variable1, @variable2, ... to zmienne, do których chcemy przypisać wartości zwrócone przez kursor - tak więc musimy najpierw zadeklarować te zmienne, zanim użyjemy ich w poleceniu FETCH, odpowiednio do typów danych zwracanych przez kursor.

Sama operacja FETCH, która wybiera kolejny wiersz, zwraca wartość 0, jeśli wiersz został pomyślnie pobrany, -1, jeśli nie ma więcej wierszy do pobrania, a -2, jeśli wystąpił błąd. Ta informacja jest przechowywana w zmiennej systemowej @@FETCH_STATUS i posłuży nam do sprawdzenia, czy mamy więcej wierszy do przetworzenia.

W zależności od wartości @@FETCH_STATUS możemy przetwarzać wiersze w pętli WHILE:

WHILE @@FETCH_STATUS = 0
BEGIN
    -- Operacje na wierszu
    FETCH NEXT FROM cursor_name INTO @variable1, @variable2, ...;
END

Po zakończeniu przetwarzania wierszy, zamykamy i usuwamy kursor, aby zwolnić zasoby:

CLOSE cursor_name;
DEALLOCATE cursor_name;

Całościowo, utworzonie, użycie, a następnie zamknięcie kursora wygląda tak:

DECLARE @variable1 datatype1, @variable2 datatype2, ...

DECLARE cursor_name CURSOR FOR
SELECT column1, column2, ...
FROM table_name
WHERE condition;


OPEN cursor_name;
FETCH NEXT FROM cursor_name INTO @variable1, @variable2, ...;

WHILE @@FETCH_STATUS = 0
BEGIN
    -- Operacje na wierszu
    FETCH NEXT FROM cursor_name INTO @variable1, @variable2, ...;
END

CLOSE cursor_name;
DEALLOCATE cursor_name;

Przykładowe użycie kursora

Oto przykładowe użycie kursora na podstawie schematu bazy danych LibraryDatabase. Załóżmy, że chcemy napisać procedurę składowaną, która wyświetla informacje o autorach i ilości ich książek:

CREATE PROCEDURE ShowAuthorBookCounts
AS
BEGIN
    DECLARE @AuthorId INT
    DECLARE @FirstName NVARCHAR(50)
    DECLARE @LastName NVARCHAR(50)
    DECLARE @BookCount INT

    -- Deklaracja kursora
    DECLARE authorCursor CURSOR FOR
    SELECT AuthorId, FirstName, LastName
    FROM Authors
    -- Otwarcie kursora
    OPEN authorCursor

    -- Pobranie pierwszego wiersza
    FETCH NEXT FROM authorCursor INTO @AuthorId, @FirstName, @LastName

    -- Pętla przetwarzająca wiersze
    WHILE @@FETCH_STATUS = 0
    BEGIN

        -- Obliczenie liczby książek dla danego autora
        SELECT @BookCount = COUNT(*)
        FROM Books
        WHERE AuthorId = @AuthorId

        -- Wyświetlenie informacji
        PRINT 'Author: ' + @FirstName + ' ' + @LastName + ', Book Count: ' + CAST(@BookCount AS NVARCHAR(10))
        
        -- Pobranie kolejnego wiersza
        FETCH NEXT FROM authorCursor INTO @AuthorId, @FirstName, @LastName
    END

    -- Zamknięcie kursora
    CLOSE authorCursor
    DEALLOCATE authorCursor
END
EXEC ShowAuthorBookCounts

Ta procedura składowana otwiera kursor, pobiera informacje o autorach, a następnie dla każdego autora oblicza liczbę książek, które napisał i wyświetla te informacje. Na koniec zamyka i usuwa kursor.

Zwróć uwagę, że używając kursorów w SQL, należy zachować ostrożność, ponieważ mogą one wpływać na wydajność zapytań, szczególnie przy dużych zbiorach danych. Wiele operacji wykonywanych z użyciem kursorów może być zwykle zastąpionych przez zapytania zbiorcze, co może być bardziej wydajne.