SQL Server FOR EACH 循环
Posted
技术标签:
【中文标题】SQL Server FOR EACH 循环【英文标题】:SQL Server FOR EACH Loop 【发布时间】:2012-04-24 14:49:53 【问题描述】:我有以下 SQL 查询:
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 查询。
类似(伪代码):
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
所以这会返回:-
2010 年 1 月 1 日 2010 年 2 月 1 日 2010 年 3 月 1 日 2010 年 4 月 1 日 2010 年 5 月 1 日
我希望它以一个结果集而不是多个结果集的形式返回数据,因此我可能需要在查询结束时使用某种联合,因此循环的每次迭代都会联合到下一个。
编辑
我有一个接受“to date”参数的大型查询,我需要运行它 24 次,每次都带有我需要能够提供的特定日期(这些日期将是动态的)我想要避免重复我的查询 24 次,并加入他们,就好像我需要回来添加额外的列一样,这将非常耗时。
【问题讨论】:
您能解释一下为什么需要这样做吗? 95% 的情况下,当您需要 tSQL 中的循环结构时,您可能做错了。 为什么不创建一个表格,您可以在其中填充您想要运行它的日期。几乎可以肯定有比查看硬编码常量值更好的方法。 您示例中的日期按月顺序排列。这是一个规则,还是您需要能够运行任意一组日期?您是否有理由无法编辑大型查询以获取日期范围或日期集而不是单个日期?如果您绝对需要逐步完成迭代(与上面给出的好建议相反),那么您可能需要考虑使用光标。 【参考方案1】:SQL 主要是一种面向集合的语言 - 在其中使用循环通常是个坏主意。
在这种情况下,使用递归 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
【讨论】:
i
的最大步长限制为 100,等于最大递归限制。尝试... from CTE where i <= 101
或通过OPTION (MAXRECURSION 500)
增加递归限制
只有我一个人不理解这个公认的答案吗?
@Tk1993:哪一点不清楚? (递归)CTE 生成一组从 1 到 5 的数字,而查询的 select dateadd
部分将这些数字中的每一个(减 1)作为天数添加到 2010 年 1 月 1 日。还是我不喜欢使用循环在 SQL 中?
@ChrisW:OP 位于英国,日期格式为 dmy,而不是 mdy(如美国)。【参考方案2】:
这是一个带有表变量的选项:
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
您应该告诉我们您的主要目标是什么,正如 @JohnFx 所说,这可能可以通过另一种(更有效的)方式来完成。
【讨论】:
我需要循环日期而不是整数,请参阅已编辑的问题。 WHILE 循环是否适用于日期? @SelectDistinct - 是的,它应该可以正常工作。我将答案更改为返回日期而不是整数 Mark Bannister 的回答是代码更少,效率更高。没有理由为这样的事情使用 while 循环。【参考方案3】:你可以使用一个变量表,像这样:
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
【讨论】:
【参考方案4】:这取决于你想对结果做什么。如果您只关注数字,则基于集合的选项将是 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)
【讨论】:
有趣的是,您认为这是一个“基于集合的选项”(RECURSION
这个词有点夸张!)
@onedaywhen - 那里没有矛盾。递归 CTE 是基于集合的。锚成员 (SELECT 1) 设置为 S[0],然后将其与 n 个集合 (SELECT 1 + N FROM S[n - 1]) 进行 UNION ALL'ed,直到遇到空集。结果是集合 S[0] 到 S[n] 的 UNION。话虽如此 - 我个人更喜欢物理表,因为它更有效。
Joe Celko's thoughts on the matter: "使用[递归] CTE 让半过程程序员感觉很好......但递归实际上是一种过程技术。它也很昂贵,因为它真的是一个游标在幕后”——我并不是说他是正确的,而是表明这个立场并不像你想象的那样明确。也不是对您的 cmets 的批评。正如我所说,我觉得这很有趣,但我自己对这个主题没有强烈的看法:)
@onedaywhen - 和here,Celko 区分了“过程代码...过程或游标”和递归 CTE,后者“[仍然] 隐藏在过程中”。我不怀疑递归 CTE 是按程序执行的……但 JOIN 也是如此。至少我们可以同意数字表是更好的选择。【参考方案5】:
declare @counter as int
set @counter = 0
declare @date as varchar(50)
set @date = cast(1+@counter as varchar)+'/01/2013'
while(@counter < 12)
begin
select cast(1+@counter as varchar)+'/01/2013' as date
set @counter = @counter + 1
end
【讨论】:
【参考方案6】:当然是一个老问题。但我有一个简单的解决方案,不需要循环、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
注意: spt_values
是 Mircrosoft 的未记录表。它有每种类型的数字。它不建议使用,因为它可以在没有事先信息的任何新版本的 sql server 中删除,因为它没有记录。但是我们可以在上面的某些场景中使用它作为快速解决方法。
【讨论】:
spt_values 是 Mircrosoft 的未记录表。它有每种类型的数字。它不建议使用,因为它可以在没有事先信息的任何新版本的 sql server 中删除,因为它没有记录。但是我们可以在上面的某些场景中使用它作为快速解决方法。 @MattE【参考方案7】:[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]
【讨论】:
以上是关于SQL Server FOR EACH 循环的主要内容,如果未能解决你的问题,请参考以下文章