【问题标题】:Loop through each row in a table for a given range循环遍历给定范围的表中的每一行
【发布时间】:2021-03-02 18:41:29
【问题描述】:

我有一个包含日期的表格

declare @x date ='01-jan-2021'
declare @y date ='31-jan-2021'
create table hdate( a date)
 
while @x <= @y
begin
    insert into hdate(a) values( @x)
    set @x = dateadd(day,1,@x)
end

如何循环遍历每条记录?

例如

declare @start_no int =1
declare @end_no int
select @end_no = count(*) from hdate where a between '05-jan-2021 and '10-jan-2020'
while @start_no <= @end_no
begin
    select a from hdate
    set @start_no = @start_no+1

    --how do I get next record?
end

预期结果是

a
05-jan-2021
06-jan-2021
07-jan-2021
08-jan-2021
09-jan-2021
10-jan-2021

我想使用 while 循环。请帮忙

【问题讨论】:

  • 为什么要使用while循环? SQL 针对基于集合的操作进行了优化。
  • 需要在这些日期获得余额,Jim 先生已经向我展示了如何做到这一点,谢谢先生
  • 但为什么是循环而不是 CTE?

标签: sql sql-server tsql


【解决方案1】:

Jim 展示了如何使用循环获得所需的结果,但指出循环不是可行的方法。在 SQL Server 中,正确的方法是使用数字表或函数,例如 fnTally

declare @x date ='01-jan-2021'
declare @y date ='31-jan-2021'

SELECT SomeDate = DATEADD(DAY,f.N,@x) FROM dbo.fnTally(0,DATEDIFF(DAY,@x,@y)) AS f;

返回:

SomeDate
----------
2021-01-01
2021-01-02
2021-01-03
...
2021-01-29
2021-01-30
2021-01-31

速度更快,效率更高,代码更少。

【讨论】:

  • 艾伦。我不确定我们是否回答了他的问题,但非常感谢 fnTally 的参考。寻找这样的掘金是我不时回来回答问题的原因。
【解决方案2】:

不知道你为什么要使用循环,但我相信你有你的理由......

-- 
-- option 1
--
declare @x date ='01-jan-2021'
declare @y date ='31-jan-2021'
declare @hdate table ( a date)

while @x<=@y
begin
insert into @hdate(a) values( @x)
set @x=dateadd(day,1,@x)
end


declare @i date 
declare @j date
select top 1 @i =  a from @hdate order by a
while @i is not null
    begin
    select @j = @i, @i = null


    select top 1 @i = a from @hdate where a > @j order by a
    select @j,@i

end

go

-- 
-- option 2
--
declare @x date ='01-jan-2021'
declare @y date ='31-jan-2021'
declare @hdate table ( a date, rowNum int identity)

while @x<=@y
begin
insert into @hdate(a) values( @x)
set @x=dateadd(day,1,@x)
end

select * from @hdate


declare @i int = 1
declare @j int 
select @j =  max(rowNum) from @hdate
while @i <= @j
    begin
    select * from @hdate where rowNum = @i
    select @i = @i + 1

end

go
-- 
-- option 3 -- no loop
--
declare @x date ='01-jan-2021'
declare @y date ='31-jan-2021'
declare @hdate table ( a date)

while @x<=@y
begin
insert into @hdate(a) values( @x)
set @x=dateadd(day,1,@x)
end

select * from @hdate


declare @i date = '01-jan-2021'

select h1.a as prev, x.a as next 
from @hdate h1
outer apply ( select top 1 h2.a from @hdate h2 where h2.a > h1.a) x
where h1.a = @i
order by h1.a

go

【讨论】:

  • 感谢 Jim 实际上我有一个带有日期的表格,但不是我放在这里的增量因此我想知道当我将计数器加 1 时如何获得下一条记录
  • 不确定我是否理解您的评论。选项 1 和选项 2 都不会关心日期是否有间隔。它们是相对排序。如果您正在寻找简单的 prev/next,out apply 很有用(请参阅选项 3)。
  • 是的,我按照您的指示进行操作,先生
【解决方案3】:

您可以使用recursive CTE 创建顺序值,这会在递归的每个步骤中添加新项目。因此,无需为此类任务编写命令式代码。

declare @x date = convert(date, '2021-01-01', 23);
declare @y date = convert(date, '2021-01-31', 23);

with a (dt) as (
  select @x
  where @x <= @y
  
  union all

  select dateadd(d, 1, dt)
  from a
  where dt < @y
)
insert into hdate(a)
select dt
from a

db小提琴here

【讨论】:

    【解决方案4】:

    鉴于您的日期不连续,您可以使用:

    declare @start_date datetime = '05-jan-2021'
    declare @end_date datetime = '10-jan-2021'
    
    declare @cdate datetime = @start_date
    
    while (@cdate is not null or datediff(dd, @cdate, @end_date) >= 0)
    begin
    
        select @cdate = min(a) from #hdate 
          where a between @cdate and @end_date
    
        if (@cdate is not null) 
        begin
            select a from #hdate
            where a = @cdate
        end
    
        select @cdate = dateadd(dd, 1, @cdate)
    
    end
    

    【讨论】:

      猜你喜欢
      • 2010-11-30
      • 2016-01-01
      • 2015-04-14
      • 2018-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-03
      相关资源
      最近更新 更多