2012-04-24 18 views
47

Tengo la siguiente consulta SQL:SQL Server para cada lazo

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

Esto devuelve de forma natural '1/1/2010'.

Lo que quiero hacer es tener una lista de fechas, dicen:

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

entonces quiero PARA CADA través de los números y ejecutar la consulta SQL.

Algo así como (pseudocódigo):

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 

lo que esta sería volver: -

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

Quiero que esto devuelva los datos como un conjunto de resultados, no múltiples conjuntos de resultados, por lo que puede necesitar algún tipo de unión al final de la consulta, por lo que cada iteración del lazo se une a el siguiente .

edición

Tengo una consulta de gran tamaño que acepta un parámetro 'hasta la fecha', tengo que ejecutarlo 24 veces, cada vez con un determinado hasta ahora, que tengo que ser capaz de suministrar (estos las fechas van a ser dinámicas) Quiero evitar repetir mi consulta 24 veces con alls union uniéndome a ellas como si tuviera que volver y agregar columnas adicionales que me llevaría mucho tiempo.

+9

¿Puede explicar por qué necesita hacer esto? El 95% del tiempo cuando necesita una estructura de bucle en tSQL probablemente lo está haciendo mal. – JohnFx

+2

¿Por qué no crear una tabla donde puede poblar las fechas con las que desea ejecutarla? Es casi seguro que hay una forma mejor de hacerlo que mirando a través de valores constantes codificados. – JohnFx

+0

Las fechas en su ejemplo son secuenciales por mes. ¿Es eso una regla, o necesitas ser capaz de correr por un conjunto arbitrario de fechas? ¿Hay alguna razón por la que no pueda editar la consulta grande para tomar un rango de fechas o un conjunto de fechas en lugar de una sola fecha? Si necesitas pasar por las iteraciones (en contra de los buenos consejos dados anteriormente), entonces quizás quieras considerar usar un cursor. – JAQFrost

Respuesta

51

SQL es principalmente un lenguaje orientado conjunto - es generalmente una mala idea utilizar un bucle en el mismo.

En este caso, un resultado similar se podría lograr mediante un CTE recursiva:

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

El paso máximo de 'i' está limitado a 100, que es igual al máximo límite de recursión. Pruebe '... de CTE donde i <= 101' O incremente el límite de recursividad agregando' OPTION (MAXRECURSION 500) ' – guneysus

26

Aquí es una opción con una variable de tabla:

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 

Puede hacer lo mismo con una tabla temporal:

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 

usted debe decirnos cuál es su objetivo principal, como se dijo por @JohnFx, esto probablemente podría hacerse de otra manera (más eficiente).

+0

Necesito las fechas del ciclo no enteros, vea la pregunta editada. ¿Funcionaría un bucle WHILE con las fechas? – JsonStatham

+0

@SelectDistinct - Sí, debería funcionar bien. Cambié mi respuesta a las fechas de devoluciones en lugar de ints – Lamak

+1

La respuesta de Mark Bannister es menos código y más eficiente. No hay ninguna razón para usar un ciclo while para algo como esto. – Sorpigal

13

se puede utilizar una tabla de variables, como esto:

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 esto funcionó bien. –

6

Este tipo de depende de lo que quiere hacer con los resultados. Si solo buscas los números, una opción basada en conjuntos sería numbers table, que es útil para todo tipo de cosas.

Para MSSQL 2005+, puede utilizar un CTE recursiva para generar una tabla de números en línea:

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

. Es interesante que considere que se trata de una" opción basada en conjuntos "(la palabra' RECURSION' es un poco de un regalo!) – onedaywhen

+0

@onedaywhen - no hay contradicción allí. Un CTE * recursivo está * basado en conjunto. El miembro de anclaje (SELECT 1) se establece en S [0], que luego es UNION ALL'ed con n más conjuntos (SELECT 1 + N FROM S [n - 1]) hasta que se encuentre un conjunto vacío. El resultado es la UNIÓN de conjuntos S [0] a S [n]. Dicho eso, personalmente prefiero una mesa física ya que es más eficiente. –

+0

[Las ideas de Joe Celko al respecto] (http://www.simple-talk.com/sql/t-sql-programming/procedural,-semi-procedural-and-declarative-programing-part-ii/): " hace que el programador semi-procedimental se sienta bien al usar un CTE recursivo ... Pero la recursión es en realidad una técnica de procedimiento. También es costosa, ya que es realmente un cursor bajo las sábanas "- No digo que sea correcto, pero demuestra que la posición no es tan clara como la que tendría. Tampoco una crítica de tus comentarios. Como ya he dicho, me parece interesante sin tener buenas opiniones sobre el tema yo mismo :) – 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 supuesto una vieja cuestión. Pero tengo una solución simple donde no hay necesidad de bucle, CTE, variables de tabla, etc.

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 
Cuestiones relacionadas