2012-04-24 5 views
47

У меня есть следующий SQL-запрос:SQL Server для каждого цикла

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

Это, естественно, возвращает '1/1/2010'.

То, что я хочу сделать, это список дат, скажем:

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

Тогда я хочу ДЛЯ КАЖДОЙ по номерам и запустить SQL Query.

Что-то вроде (псевдокод):

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 

Так заносить бы: -

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

Я хочу, чтобы это возвращало данные как один набор результатов, а не несколько наборов результатов, поэтому мне может потребоваться использование какого-либо объединения в конце запроса, поэтому каждая итерация петлевого объединения на следующий ,

редактировать

У меня есть большой запрос, который принимает параметр «на сегодняшний день», мне нужно, чтобы запустить его в 24 раза, каждый раз с конкретным на сегодняшний день, который я должен быть в состоянии поставлять (эти даты будут динамическими). ​​Я хочу избежать повторения моего запроса 24 раза, когда все союзники присоединяются к ним, как если бы мне нужно было вернуться и добавить дополнительные столбцы, это было бы очень трудоемким.

+9

Не могли бы вы объяснить, зачем вам это нужно? 95% времени, когда вам нужна структура циклов в tSQL, вы, вероятно, ошибаетесь. – JohnFx

+2

Почему бы не создать таблицу, в которой вы можете заполнить даты, которые вы хотите использовать для этого. Существует почти наверняка лучший способ сделать это, чем просматривать жестко установленные постоянные значения. – JohnFx

+0

Даты в вашем примере являются последовательными по месяцам. Это правило, или вам нужно иметь возможность запускать произвольный набор дат? Есть ли причина, по которой вы не можете редактировать большой запрос, чтобы выбрать диапазон дат или набор дат, а не одну дату? Если вам абсолютно необходимо пройти итерации (против хороших советов, приведенных выше), тогда вам может понадобиться использовать курсор. – JAQFrost

ответ

51

SQL в первую очередь ориентированных на множество языков - это вообще плохая идея использовать цикл в нем.

В этом случае, подобный результат может быть достигнут с помощью рекурсивных ОТВ:

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

Максимальный шаг 'i' ограничен 100, который равен максимальному пределу рекурсии. Попробуйте '... из CTE, где i <= 101' Или добавление ограничения на добавление рекурсии с помощью' OPTION (MAXRECURSION 500) ' – guneysus

26

Вот вариант с табличной переменной:

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 

Вы можете сделать то же самое с темп таблицы:

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 

Вы должны сказать нам, что ваша главная цель, как было сказано by @JohnFx, возможно, это может быть сделано другим (более эффективным) способом.

+0

Мне нужно цитировать даты не целые числа, см. Отредактированный вопрос. Будет ли цикл WHILE работать с датами? – JsonStatham

+0

@SelectDistinct - Да, он должен работать нормально. Я изменил свой ответ на даты возврата вместо ints. – Lamak

+1

Ответ Марка Баннистера меньше кода и более эффективен. Нет причин использовать цикл while для чего-то подобного. – Sorpigal

13

Вы можете использовать таблицу переменных, например:

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 это сработало хорошо. –

6

Этого вида зависит от того, что вы хотите сделать с результатами. Если вы сразу же после номеров, то на основе набора будет numbers table - что пригодится для всех видов вещей.

Для MSSQL 2005+, вы можете использовать рекурсивные CTE для создания номера таблицы инлайн:

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

Интересно, что вы считаете это« опцией на основе набора »(слово« RECURSION »немного отдавать!) – onedaywhen

+0

@onedaywhen - там нет противоречия. Рекурсивный CTE * * установлен на основе. Якорный элемент (SELECT 1) установлен в S [0], а затем UNION ALL'ed с еще n наборами (SELECT 1 + N FROM S [n - 1]) до тех пор, пока не встретится пустой набор. В результате получается UNION множеств S [0] на S [n]. Это, как говорится, я предпочитаю физический стол, поскольку он более эффективен. –

+0

[Мысли Джо Селко по этому вопросу] (http://www.simple-talk.com/sql/t-sql-programming/procedural,-semi-procedural-and-declarative-programing-partiiii): " это делает полупрограммного программиста хорошим, чтобы использовать [рекурсивный] CTE ... Но рекурсия на самом деле является процедурной техникой. Она также дорогая, поскольку она действительно является курсором под обложками »- я не говорю, что он правильно, но демонстрирует, что позиция не такая четкая, как у вас есть. Не критика ваших комментариев тоже. Как я уже сказал, я нахожу это интересным, не имея серьезных взглядов по этому вопросу самостоятельно :) – 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

Конечно же старый вопрос. Но у меня есть простое решение, в котором нет необходимости в Looping, CTE, переменных таблицы и т. Д.

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