Tsql 获取周期之间的日期
我有一个包含以下数据的表Tsql 获取周期之间的日期,tsql,Tsql,我有一个包含以下数据的表 periodID periostart periodend 1 01-01-2012 10-01-2012 2 11-01-2012 01-04-2012 我希望查询返回这样的内容。 周期日期 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
periodID periostart periodend
1 01-01-2012 10-01-2012
2 11-01-2012 01-04-2012
我希望查询返回这样的内容。
周期日期
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
因为我有大约100000行包含句点,所以我想研究一个不会影响性能游标、循环的解决方案。不使用游标或循环是否可以得到我想要的结果
谢谢你的参与
到目前为止,我得到了这个解决方案
我不完全确定我是否理解,但我认为您需要使用运算符。差不多
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
这是:
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
说明:我使用递归获得新日期,在当前日期的基础上增加1天,限制为结束期间。很难在递归连接中获得最后生成的日期,我用一个子句解决了这个问题。我认为这是一个很好的问题
编辑到期评论
我之所以发布这种方法,是因为OP需要一个没有循环和游标的解决方案。我不知道还有什么方法可以编写一个没有循环或游标的sql语句
此外,我认为生成日期的正确方法是使用内循环或伊兹克样式交叉连接将光标置于时段表上。我的第一个伊兹克样式交叉连接:
每天我都学到一些东西
:
这个坏男孩真正令人惊奇的是,它的产量为零
阅读。绝对没有,娜达,没有
请,约翰,性能测试后 周期跨度较大的问题。声明终止了。在语句完成之前,已用尽最大递归100??将此选项附加到查询:从CTE选项MAXRECURSION 150中选择*;按最大天数更改150。这里有一个示例:感谢您的参与,但我发现这篇文章解释了为什么CTE不是计数的最佳解决方案。是的,本文与我链接到您的页面相同。我已使用新的解决方案更新了我的问题,目前我正在测试这两个solutionsProfiler问题的性能,我将在修复问题后立即发布结果。这基本上取自我提供的文章,但既然你表现出了帮助我的努力,我将把这标记为解决方案。
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
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 )