如果没有循环或游标,您如何获取日期间隔列表并将其转换为1和0的字符串,以便:
例如,如果间隔是:
然后你编写的SQL应该输出11011.这是你可以使用的安装脚本:
declare @TimeSpan table
(
start datetime
,finish datetime
)
-- this is a good data set, with overlapping and non-overlapping time spans
insert into @TimeSpan values ('02/02/2010', '02/02/2010')
insert into @TimeSpan values ('02/03/2010', '02/03/2010')
insert into @TimeSpan values ('02/04/2010', '02/05/2010')
insert into @TimeSpan values ('02/05/2010', '02/06/2010')
insert into @TimeSpan values ('02/07/2010', '02/09/2010')
insert into @TimeSpan values ('02/08/2010', '02/08/2010')
insert into @TimeSpan values ('02/08/2010', '02/10/2010')
insert into @TimeSpan values ('02/14/2010', '02/16/2010')
-- for this set of data, the output string would be 111111111000111
答案 0 :(得分:7)
DECLARE @Result VARCHAR(MAX), @start DATETIME
SELECT @start= MIN(start) ,
@Result =REPLICATE('0',1+DATEDIFF(DAY,MIN(start),MAX(finish)))
FROM @TimeSpan
SELECT @Result = STUFF(@Result,
DATEDIFF(DAY,@start,start)+1,
DATEDIFF(DAY,start,finish)+1,
REPLICATE('1',1+DATEDIFF(DAY,start,finish)))
FROM @TimeSpan
SELECT @Result
答案 1 :(得分:2)
我不得不使用递归CTE; - )
DECLARE @BitString varchar(100);
Declare @minStart datetime
DECLARE @MaxEnd datetime
declare @RangeDates table
(
start datetime
,finish datetime
)
-- this is a good data set, with overlapping and non-overlapping time spans
insert into @RangeDates values ('02/02/2010', '02/02/2010')
insert into @RangeDates values ('02/03/2010', '02/03/2010')
insert into @RangeDates values ('02/04/2010', '02/05/2010')
insert into @RangeDates values ('02/05/2010', '02/06/2010')
insert into @RangeDates values ('02/07/2010', '02/09/2010')
insert into @RangeDates values ('02/08/2010', '02/08/2010')
insert into @RangeDates values ('02/08/2010', '02/10/2010')
insert into @RangeDates values ('02/14/2010', '02/16/2010')
SELECT @minStart = MIN(start) FROM @RangeDates
SELECT @MaxEnd = MAX(finish) FROM @RangeDates
;WITH Dates AS (
SELECT myDate = CONVERT(DateTime, @minStart),
CASE
WHEN exists (SELECT * FROM @RangeDates where @minStart between start and finish) then '1'
else '0'
END as myBit
UNION ALL
SELECT myDate = DATEADD(DAY,1,myDate),
CASE
WHEN exists (SELECT * FROM @RangeDates where myDate between start and finish) then '1'
else '0'
END
FROM Dates
where myDate <= @MaxEnd
)
SELECT @BitString = COALESCE(@BitString,'') + myBit FROM Dates
SELECT @BitString
答案 2 :(得分:1)
好的,这是我的解决方案。比其他计数表解决方案快一点,但不是很好。此外,它受限于转换为数字只允许较小的最小 - 最大日期范围。递归CTE比计数表更快是很奇怪的。计数表是否更好地扩展?
declare @Tally table
(
N int identity(1,1),
T bit
)
insert into @Tally
select TOP 11000 0 as T
from master.dbo.SysColumns sc1, master.dbo.SysColumns sc2
declare @begin datetime = (select MIN(start) from @TimeSpan);
declare @end datetime = (select MAX(finish) from @TimeSpan);
with strings as
(
select S.*
,
'1'+
REPLICATE('0', DATEDIFF(DAY, @begin, DATEADD(DAY,N-1,S.start)))+
'1'+
REPLICATE('0', DATEDIFF(DAY, DATEADD(DAY,N-1,S.start), @end)) task
from @TimeSpan S
inner join
@Tally T ON DateAdd(DAY,T.N-1,S.start) <= S.finish
)
select SUM(DISTINCT convert(numeric(38,0),task))
- COUNT(DISTINCT task)*(convert(numeric(38,0), '1' + REPLICATE('0',DATEDIFF(d,@begin,@end)+1)))
from strings