2012-04-24 58 views
47

Tôi có truy vấn SQL sau:SQL Server cho mỗi vòng lặp

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

này một cách tự nhiên trở về '1/1/2010'.

Những gì tôi muốn làm là có một danh sách các ngày, nói:

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

Sau đó, tôi muốn CHO MỖI qua những con số và chạy các truy vấn SQL.

Giống như (giả):

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 

Vì vậy, đây sẽ trở lại: -

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

Tôi muốn điều này trả về dữ liệu dưới dạng một kết quả, không phải nhiều kết quả, vì vậy tôi có thể cần sử dụng một số loại công đoàn ở cuối truy vấn, vì vậy mỗi lần lặp của các tổ hợp vòng tiếp theo .

chỉnh sửa

Tôi có một câu hỏi lớn mà chấp nhận một tham số 'cho đến nay, tôi cần phải chạy nó 24 lần, mỗi lần với một cụ thể cho đến nay mà tôi cần để có thể cung cấp (những ngày tháng sẽ là động) Tôi muốn tránh lặp lại truy vấn của tôi 24 lần với các liên minh tham gia cùng họ như thể tôi cần phải quay lại và thêm các cột bổ sung nó sẽ rất tốn thời gian.

+9

Bạn có thể giải thích lý do tại sao bạn cần thực hiện việc này? 95% thời gian khi bạn cần cấu trúc lặp trong tSQL, có thể bạn đang làm sai. – JohnFx

+2

Tại sao không tạo bảng nơi bạn có thể điền ngày bạn muốn chạy nó. Có gần như chắc chắn là một cách tốt hơn để làm điều này hơn là xem xét thông qua các giá trị liên tục hardcoded. – JohnFx

+0

Các ngày trong ví dụ của bạn được tuần tự theo tháng. Đó có phải là một quy tắc, hay bạn cần để có thể chạy cho một tập hợp ngày tháng tùy ý? Có lý do nào khiến bạn không thể chỉnh sửa truy vấn lớn để có phạm vi ngày hoặc tập hợp các ngày thay vì một ngày không? Nếu bạn hoàn toàn cần phải bước qua các lần lặp lại (đối với lời khuyên tốt ở trên), thì bạn có thể muốn xem xét sử dụng một con trỏ. – JAQFrost

Trả lời

51

SQL chủ yếu là một ngôn ngữ thiết lập định hướng - đó là nói chung là một ý tưởng tồi để sử dụng một vòng lặp trong đó.

Trong trường hợp này, một kết quả tương tự có thể đạt được bằng cách sử dụng CTE đệ quy:

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

Bước tối đa của 'i' được giới hạn ở 100, bằng giới hạn đệ quy tối đa. Hãy thử '... từ CTE trong đó i <= 101' Hoặc giới hạn đệ quy chưa được thêm vào bởi' OPTION (MAXRECURSION 500) ' – guneysus

26

Dưới đây là một lựa chọn với một biến bảng:

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 

Bạn có thể làm tương tự với một bảng 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 

Bạn nên cho chúng tôi biết mục tiêu chính của bạn là gì, như đã nói bởi @JohnFx, điều này có thể có thể được thực hiện một cách khác (hiệu quả hơn).

+0

Tôi cần phải lặp ngày không phải là số nguyên, xem câu hỏi đã chỉnh sửa. Một vòng lặp WHILE có hoạt động với các ngày không? – JsonStatham

+0

@SelectDistinct - Có, nó sẽ hoạt động tốt. Tôi đã thay đổi câu trả lời của mình để trả về ngày thay vì ints – Lamak

+1

Câu trả lời của Mark Bannister ít mã và hiệu quả hơn. Không có lý do gì để sử dụng vòng lặp while cho một thứ như thế này. – Sorpigal

13

Bạn có thể sử dụng một bảng biến, như thế này:

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 tính năng này hoạt động tốt. –

6

này loại phụ thuộc vào những gì bạn muốn làm với kết quả. Nếu bạn chỉ sau các con số, một tùy chọn dựa trên tập hợp sẽ là một numbers table - có ích cho tất cả mọi thứ.

Đối với MSSQL 2005 +, bạn có thể sử dụng một CTE đệ quy để tạo ra một nội tuyến số bảng:

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

Thú vị là bạn coi đây là" tùy chọn dựa trên tập hợp "(từ' RECURSION' là một chút của một cho đi!) – onedaywhen

+0

@onedaywhen - không có mâu thuẫn ở đó. CTE đệ quy * là * dựa trên bộ. Thành viên neo (SELECT 1) được thiết lập S [0], sau đó UNION ALL'ed với n bộ khác (SELECT 1 + N FROM S [n - 1]) cho đến khi một tập rỗng được bắt gặp. Kết quả là UNION của bộ S [0] thành S [n]. Điều đó đang được nói - cá nhân tôi thích một bảng vật lý vì nó hiệu quả hơn. –

+0

[Joe Celko của những suy nghĩ về vấn đề này] (http://www.simple-talk.com/sql/t-sql-programming/procedural,-semi-procedural-and-declarative-programing-part-ii/): " nó làm cho các lập trình viên bán thủ tục cảm thấy tốt khi sử dụng CTE đệ quy ... Nhưng đệ quy thực sự là một kỹ thuật thủ tục. Nó cũng đắt vì nó thực sự là một con trỏ bên dưới vỏ bọc "- Tôi không nói anh ta là đúng nhưng thể hiện vị trí không rõ ràng như bạn sẽ có. Không chỉ trích bình luận của bạn. Như tôi đã nói, tôi thấy nó thú vị mà không có quan điểm mạnh mẽ về chủ đề này :) – 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

Tắt nhiên một câu hỏi cũ. Nhưng tôi có một giải pháp đơn giản mà không cần Looping, CTE, biến Table, v.v.

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 
Các vấn đề liên quan