获取期间之间的日期

时间:2012-02-04 20:32:27

标签: tsql

我有一个包含以下数据的表

periodID periostart periodend
1        01-01-2012 10-01-2012
2        11-01-2012 01-04-2012

我想查询返回这样的内容。 periodID日期

1        01-01-2012
1        02-01-2012
1        03-01-2012
etc.
1        09-01-2012
2        11-01-2012
2        12-01-2012
etc.
2        31-03-2012

由于我有大约100,000行包含句点,我想研究一种不会影响性能的解决方案(游标,循环)。是否有可能在不使用游标或循环的情况下获得我想要的结果?

感谢参与者。

到目前为止,我得到了这个解决方案

create table #p (id int, periodstart smalldatetime, periodend smalldatetime );
insert into #p values 
(1, '2012-01-01', '2015-01-10')
insert into #p values
(2, '2012-04-10', '2015-11-20');


SELECT TOP 366 --aprox one year
        IDENTITY(INT,0,1) AS N
   INTO #Tally
   FROM Master.dbo.SysColumns sc1,
        Master.dbo.SysColumns sc2



SELECT DATEADD(day, T.N, periodstart) AS [Date]
FROM #p
cross join #tally T
WHERE (T.N >= 0 AND T.N < DATEDIFF(day, periodstart, periodend))
ORDER BY [Date]

3 个答案:

答案 0 :(得分:4)

这是:

create table #p (id int, periostart date, periodend date );
insert into #p values 
(1, '20120101', '20120110'),
(2, '20120110', '20120120');


with cte as (
 select
   id, periostart as aDay
 from
   #p
 union all
 select
   cte.id, dateadd( day, 1, cte.aDay) as aDay
 from
   #p
 inner join
   cte on #p.id = cte.id 
 where
   cte.aDay < #p.periodend
)
select * from cte

结果:

id aDay          
-- ------------- 
1  2012-01-01 00:00:00
2  2012-01-10 00:00:00
...
2  2012-01-17 00:00:00
2  2012-01-18 00:00:00
2  2012-01-19 00:00:00
2  2012-01-20 00:00:00
1  2012-01-02 00:00:00
1  2012-01-03 00:00:00
...
1  2012-01-08 00:00:00
1  2012-01-09 00:00:00
1  2012-01-10 00:00:00

解释:我使用CTE递归将新日期添加到当前日期限制为结束时段的1天。难以在递归连接中获取最后生成的日期,我使用OVER子句解决此问题。我认为这是一个很好的查询。

已审核OP评论

我发布了这种方法,因为OP要求一个没有循环且没有游标的解决方案。我不知道在没有循环或游标的情况下编写sql语句的任何其他方法。

另外,我认为生成日期的正确方法是使用内部循环或使用Itzik样式交叉连接的句点表上的光标。

答案 1 :(得分:1)

我不完全确定,但我认为你想使用BETWEEN运算符。像

这样的东西
SELECT PeriodID, @Date FROM Periods WHERE @Date BETWEEN periodstart AND periodend

或者如果您通过加入包含日期的表来获取日期,请在加入中使用BETWEEN

SELECT periodTable.PeriodID, dateTable.Date 
FROM periodTable 
    INNER JOIN dateTable ON dateTable.Date BETWEEN periodTable.periodstart AND periodTable.periodEnd

在这种情况下,您将拥有一张表,其中包含从最早到最晚可能的每个日期,并加入如​​上所示的表格。像这样的表可以这样创建:

CREATE TABLE dateTable(ID int identity(1,1), Date datetime NOT NULL)
Declare @d datetime

set @d=CONVERT(datetime, '1/1/1990')--start date

While @d<=CONVERT(datetime, '1/1/2020')--enddate
Begin
Insert into dateTable values (@d)
set @d=DATEADD(dd, 1, @d)
End

答案 2 :(得分:1)

我的第一个 Itzik风格交叉加入

declare @longestPeriod int
set @longestPeriod = 1000  --you should calculate it with a single query

create table #p (id int, periostart date, periodend date );
insert into #p values 
(1, '20120101', '20120110'),
(2, '20120110', '20120120');

WITH 
  E1(N) AS (
            SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
            SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
            SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
           ),                          -- 1*10^1 or 10 rows
  E2(N) AS (SELECT 1 FROM E1 a, E1 b), -- 1*10^2 or 100 rows
  E4(N) AS (SELECT 1 FROM E2 a, E2 b), -- 1*10^4 or 10,000 rows
  E8(N) AS (SELECT 1 FROM E4 a, E4 b),  -- 1*10^8 or 100,000,000 rows
  NN as (SELECT top(@longestPeriod) ROW_NUMBER() 
                OVER (ORDER BY (SELECT NULL)) as N  FROM E8 )
 select 
   id,dateadd( dd, NN.N , periostart) as aDay
 from
   #p
 cross join
   NN
 where NN.N between 0 and datediff( dd, periostart, periodend )

每天我都会学到一些东西。

<强> Itzik-Style Cross-Join

  

这个坏孩子的真正惊奇之处在于产生了零   读取数据。绝对没有,纳达,没有。

请约翰,发布性能测试!!!