2012-04-24 17 views
47

Mam następujące zapytanie SQL:SQL Server dla każdego Loop

DECLARE @MyVar datetime = '1/1/2010'  
SELECT @MyVar 

To naturalnie zwraca '1/1/2010'.

Co chcę zrobić, to mieć listę terminów, powiedzmy:

1/1/2010 
2/1/2010 
3/1/2010 
4/1/2010 
5/1/2010 

Następnie chcę dla każdej z liczb i uruchomić kwerendę SQL.

coś jak (Pseudokod):

List = 1/1/2010,2/1/2010,3/1/2010,4/1/2010,5/1/2010 

For each x in List 
do 
    DECLARE @MyVar datetime = x 

    SELECT @MyVar 

Więc będzie to powrót: -

1/1/2010 2/1/2010 3/1/2010 4/1/2010 5/1/2010

Chcę, aby to zwróciło dane jako jeden zestaw wyników, a nie wiele zestawów wyników, więc może potrzebować użyć pewnego rodzaju związku na końcu zapytania, więc każda iteracja pętli na Następny .

edit

Mam duże kwerendę, która przyjmuje parametr „na bieżąco”, muszę go uruchomić 24 razy, za każdym razem z określonym na dzień, który muszę być w stanie dostarczyć (te daty będą dynamiczne) Chcę uniknąć powtarzania mojego zapytania 24 razy z łączącymi się łączami, tak jakbym musiał wrócić i dodać dodatkowe kolumny, byłoby to bardzo czasochłonne.

+9

Czy możesz wyjaśnić, dlaczego musisz to zrobić? 95% czasu, w którym potrzebujesz struktury pętli w tSQL, prawdopodobnie robisz to źle. – JohnFx

+2

Dlaczego nie utworzyć tabeli, w której można wypełnić datami, które mają być uruchomione przeciwko temu. Jest prawie na pewno lepszy sposób na to, niż przeglądanie stałych wartości zakodowanych na stałe. – JohnFx

+0

Daty podane w przykładzie są kolejnymi miesiącami. Czy to jest reguła, czy też musisz być w stanie uruchomić dla dowolnego zestawu dat? Czy istnieje powód, dla którego nie można edytować dużego zapytania, aby wybrać zakres dat lub zestaw dat zamiast jednej daty? Jeśli koniecznie musisz przejść przez iteracje (wbrew dobrej rady podanej powyżej), możesz rozważyć użycie kursora. – JAQFrost

Odpowiedz

51

SQL jest przede wszystkim zorientowana na język zestaw - to ogólnie zły pomysł, aby użyć pętli w nim.

W tym przypadku podobny rezultat można osiągnąć za pomocą rekurencyjnych CTE:

with cte as 
(select 1 i union all 
select i+1 i from cte where i < 5) 
select dateadd(d, i-1, '2010-01-01') from cte 
+4

Maksymalny krok "i" jest ograniczony do 100, który jest równy maksymalnemu limitowi rekursji. Wypróbuj '... z CTE gdzie i <= 101' Lub wprowadź ograniczenie rekursji dodawania przez' OPCJA (MAXRECURSION 500) ' – guneysus

26

Tutaj jest opcja ze zmienną tabeli:

DECLARE @MyVar TABLE(Val DATETIME) 
DECLARE @I INT, @StartDate DATETIME 
SET @I = 1 
SET @StartDate = '20100101' 

WHILE @I <= 5 
BEGIN 
    INSERT INTO @MyVar(Val) 
    VALUES(@StartDate) 

    SET @StartDate = DATEADD(DAY,1,@StartDate) 
    SET @I = @I + 1 
END 
SELECT * 
FROM @MyVar 

Można zrobić to samo z tabeli temp:

CREATE TABLE #MyVar(Val DATETIME) 
DECLARE @I INT, @StartDate DATETIME 
SET @I = 1 
SET @StartDate = '20100101' 

WHILE @I <= 5 
BEGIN 
    INSERT INTO #MyVar(Val) 
    VALUES(@StartDate) 

    SET @StartDate = DATEADD(DAY,1,@StartDate) 
    SET @I = @I + 1 
END 
SELECT * 
FROM #MyVar 

Należy powiedzieć nam, co jest głównym celem, jak zostało powiedziane przez @JohnFx, można to prawdopodobnie zrobić inny (bardziej efektywny) sposób.

+0

Potrzebuję pętli dat nie całkowitymi, zobacz edytowane pytanie. Czy pętla WHILE będzie działać z datami? – JsonStatham

+0

@SelectDistinct - Tak, powinno działać dobrze. Zmieniłem swoją odpowiedź na daty zwrotów zamiast na int. – Lamak

+1

Odpowiedź Marka Bannistera jest mniejsza i bardziej wydajna. Nie ma powodu, aby używać pętli while na coś takiego. – Sorpigal

13

Można użyć zmiennej tabeli w następujący sposób:

declare @num int 

set @num = 1 

declare @results table (val int) 

while (@num < 6) 
begin 
    insert into @results (val) values (@num) 
    set @num = @num + 1 
end 

select val from @results 
+0

+1 to działało dobrze. –

6

Ten rodzaj zależy od tego, co chcesz zrobićz wyników. Jeśli masz tuż za liczbami, opcją opartą na zestawie będzie numbers table - co przyda się do różnych rzeczy.

dla MSSQL 2005+, można użyć rekurencyjnej CTE wygenerować inline numery tabeli:

;WITH Numbers (N) AS (
    SELECT 1 UNION ALL 
    SELECT 1 + N FROM Numbers WHERE N < 500 
) 
SELECT N FROM Numbers 
OPTION (MAXRECURSION 500) 
+0

Ciekawe, że uważasz to za" opcją opartą na zestawie "(słowo' RECURSION' jest nieco z rozdania!) – onedaywhen

+0

@staywhen - tam nie ma sprzeczności. Rekurencyjne CTE * jest oparte na zestawie. Element kotwiący (SELECT 1) jest ustawiony S [0], który jest następnie UNION ALL'ed z n więcej zestawów (SELECT 1 + N FROM S [n - 1]) do momentu napotkania pustego zestawu. Wynikiem jest UNION z zestawów S [0] do S [n]. Biorąc to pod uwagę - ja osobiście wolę stół fizyczny, ponieważ jest bardziej wydajny. –

+0

[Myśli Joe Celko w tej sprawie] (http://www.simple-talk.com/sql/t-sql-programming/procedural,-semi-procedural-and-declarative-programing-part-ii/): " to sprawia, że ​​programista semi-proceduralny czuje się dobrze, gdy używa [rekursywnego] CTE ... Ale rekurencja jest w rzeczywistości techniką proceduralną, jest również droga, ponieważ jest to naprawdę kursor pod okładką "- nie mówię, że jest jest poprawny, ale pokazuje, że pozycja nie jest tak wyraźna, jak byś to zrobił. Nie krytykuję również twoich komentarzy. Jak już powiedziałem, uważam, że jest to interesujące, ale nie mam mocnych poglądów na ten temat. :) – onedaywhen

5
declare @counter as int 
set @counter = 0 
declare @date as varchar(50) 
set @date = cast([email protected] as varchar)+'/01/2013' 
while(@counter < 12) 
begin 
select cast([email protected] as varchar)+'/01/2013' as date 
set @counter = @counter + 1 
end 
1
[CREATE PROCEDURE [rat].[GetYear] 

AS 
BEGIN 

-- variable for storing start date 
Declare @StartYear as int 
-- Variable for the End date 
Declare @EndYear as int 

-- Setting the value in strat Date 
select @StartYear = Value from rat.Configuration where Name = 'REPORT_START_YEAR'; 

-- Setting the End date 
select @EndYear = Value from rat.Configuration where Name = 'REPORT_END_YEAR'; 


-- Creating Tem table 
    with [Years] as 
    (
     --Selecting the Year 
     select @StartYear [Year] 
     --doing Union 
     union all 
     -- doing the loop in Years table 
     select Year+1 Year from [Years] where Year < @EndYear 
    ) 
    --Selecting the Year table 
selec] 
1

Off Oczywiście stary pytanie. Ale mam proste rozwiązanie, w którym nie ma potrzeby zapętlania, CTE, zmiennych tabel itp.

DECLARE @MyVar datetime = '1/1/2010'  
SELECT @MyVar 

SELECT DATEADD (DD,NUMBER,@MyVar) 
FROM master.dbo.spt_values 
WHERE TYPE='P' AND NUMBER BETWEEN 0 AND 4 
ORDER BY NUMBER 
Powiązane problemy