2009-01-25 18 views

Odpowiedz

0

9 na 10 razy można zrobić, co chcesz, bez kursora lub pętli while. Jednakże, jeśli musisz użyć jednego, odkryłem, że podczas gdy pętle wydają się być szybsze.

Także, jeśli nie chcesz, aby usunąć lub zaktualizować tabelę, można użyć coś takiego:

DECLARE @id [type] 
SELECT @id = MIN([id]) FROM [table] 
WHILE @id IS NOT NULL 
BEGIN 
    EXEC [sproc] @id 
    SELECT @id = MIN([id]) FROM [table] WHERE [id] > @id 
END 
+1

Czy to jest całkowicie dokładne? Czy można zamówić przez id bez określania klauzuli group by? –

+0

To nie działa poprawnie. Nie zatrzymuje się po odczytaniu maks. Id. –

+1

-1 Nie działa. Jak powiedział Richard Collette, wybrana składnia jest zepsuta. A jeśli poprawisz składnię, utknie ona w nieskończonej pętli, jak wskazał Tony_Henrich). Dodałem odpowiedź, która ma działającą pętlę, ale była powolna w moim doświadczeniu. –

17

Tak. Jeśli masz kolumnę można użyć w tabeli, aby oznaczyć te przetwarzane, można użyć PODCZAS istnieje:

DECLARE @Id int 
WHILE EXISTS(SELECT * FROM Table1 WHERE Processed='N') 
BEGIN 
SELECT Top 1 @Id = id from Table1 WHERE Procesed='N' 
EXEC dbo.Sproc @Id 
UPDATE Table1 SET Processed = 'Y' WHERE Id = @Id 
END 

Alternatywnie, zrzucić identyfikatory do zmiennej temp tabeli lub tabel i usunąć po zakończeniu:

DECLARE @HoldTable table (Id int PRIMARY KEY) 
DECLARE @Id int 
INSERT INTO @HoldTable SELECT Id FROM Table1 
WHILE EXISTS(SELECT * FROM @HoldTable) 
BEGIN 
SELECT @Id = id from @HoldTable 
EXEC dbo.Sproc @Id 
DELETE FROM @HoldTable where Id = @Id 
END 
+1

Edytował swoją drugą odpowiedź. Wybrałeś @Id zamiast @HoldTable i miałeś klauzulę where 'where processed = 'N'', ale @HoldTable nie ma takiej kolumny. Druga opcja powinna teraz działać. Mam nadzieję, że dobrze zrozumiałem twoją intencję. – kralco626

3

Szczerze mówiąc, gdybym musiał wykonać przechowywanej proc do zestawu danych zamiast jeśli jeden rekord został napisany, napisałem kod oparty na zestawie zamiast używać zapisanego proc. Jest to jedyny skuteczny sposób, aby to zrobić, jeśli zamierzasz działać przeciwko dużemu zestawowi danych. Możesz przejść od godziny, aby wstawić wstawkę, którą przechowywany proc robi do sekund lub nawet milisekund. Nie używaj przetwarzania opartego na rekordach zawsze, gdy musisz przetworzyć zestaw danych, zwłaszcza z niemądrych powodów, takich jak ponowne użycie kodu. Wydajność przyspiesza ponowne użycie kodu.

+0

Jest kilka rzeczy, w których absolutnie * musisz * używać kursorów, ale tak jak w przypadku GOTO, prawdziwa potrzeba jest naprawdę mała. Zgadzam się, że lepiej jest przeprowadzić przetwarzanie oparte na zestawach, gdziekolwiek to ma sens, ale twoje "mocne atuty ponownie wykorzystują kod" jest nieco ekstremistyczne. –

+0

Ponieważ mówimy o differnce między minutami a milisekundami lub godzinami i sekundami (lub minutami), nie sądzę, że jest to ekstremizm. Programiści, którzy nie biorą pod uwagę wydajności bazy danych w porównaniu z ponownym użyciem kodu (szczególnie jeśli chodzi o używanie kursorów), generalnie mają bardzo źle działające bazy danych. – HLGEM

+0

Jesteś tym, który powiedział, że to sekundy w stosunku do godzin. W tej sprawie ze Słomianem, kto mógłby się spierać? Ale jeśli różnica wynosi milisekundy w porównaniu z sekundami lub milisekundami w porównaniu z dwukrotnie większą liczbą milisekund, to zależy. – stannius

1

Jeśli to możliwe, napisałbym drugą wersję przechowywanego procesu, który odczytuje z tabeli tymczasowej.

Jeśli to nie jest możliwe, prawdopodobnie nie masz szczęścia.

12

Jeśli używasz programu SQL Server 2005 lub nowszego, nie przejmuj się kompatybilnością wsteczną i możesz przekonwertować swój kod na funkcję zdefiniowaną przez użytkownika (zamiast przechowywanego procesu), możesz użyć nowego " CROSS APPLY "operator, który używa składni bardzo podobnej do tego, co chcesz. Znalazłem here krótki wstęp (oczywiście, można również przeczytać Bols i MSDN)

Zakładając swój SP zwraca pojedynczą wartość o nazwie out_int Twój Przykładem mogą być zapisane jako:

SELECT T.id, UDF.out_int 
FROM 
    Table1 T 
CROSS APPLY 
    dbo.fn_My_UDF(T.id) AS UDF 

This pobierze każdy "identyfikator" z Tabeli 1 i użyje go do wywołania fn_My_UDF, którego wynik pojawi się w ostatecznym zestawie wyników oprócz oryginalnego parametru.

Odmiana "CROSS APPLY" to "ZEWNĘTRZNE ZASTOSOWANIE". Są one odpowiednikami "INNER JOIN" i "LEFT JOIN", ale pracują nad dołączeniem do tabeli i UDF (i wywoływania drugiego w tym samym czasie).

Jeśli musisz musi (przez wyraźną kolejność szefa spiczastych włosach) używać SPs bezimienny, dobrze - pech! Będziesz musiał trzymać kursory lub spróbować trochę oszukiwać: zmień kod na UDF i utwórz opakowanie SP: D.

+0

CROSS APPLY nie działa dla procedur składowanych, ponieważ nie zwracają zestawów danych, wystarczy podać wyniki. Funkcje zwracają wszystko, dlatego SQL może ich używać tylko podczas korzystania z aplikacji CROSS APPLY. Głosowałem w dół. – Fandango68

+8

@ Fernando68 Wskazałem 3 niezbędne warunki korzystania z aplikacji CROSS APPLY jako rozwiązania na samym początku mojej odpowiedzi, z których trzecia z nich to "można przekonwertować kod na UDF zamiast na SP" –

0

Ta pętla działa, ale dla mojego SP była powolna.Myślę, że właściwą odpowiedzią jest HLGEM, najlepiej jest napisać lepsze zapytanie zbiorcze.

DECLARE @id INT 
SET @id = 0 

DECLARE @max INT 
SELECT TOP 1 @max = TableID 
FROM dbo.Table 
ORDER BY TableID desc 

-- loop until BREAK 
-- this is how you can perform a DO-WHILE loop in TSQL 
WHILE (1 = 1) 
BEGIN 
    SELECT  
     TOP 1 @id = TableID 
     FROM dbo.Table 
     WHERE TableID > @id 

    IF @id IS NOT NULL 
    BEGIN   
     -- call you SP here 
     PRINT @id   
    END 

    -- break out of the loop once the max id has been reached 
    IF @id = @max BREAK 
END 
Powiązane problemy