我的表中有两个名为Start_date和End_date的日期列。我需要先了解这两个日期之间有多少周并分割数据。
- 例如如果数据如下,
ID Start_date End_date No_Of_Weeks
1 25-Apr-11 8-May-11 2
2 23-Apr-11 27-May-11 6
- 我需要这样的结果:
ID Start_date End_date
1 25-Apr-2011 01-May-2011
1 02-May-2011 08-May-2011
2 23-Apr-2011 24-Apr-2011
2 25-Apr-2011 01-Apr-2011
2 02-May-2011 08-May-2011
2 09-May-2011 15-May-2011
2 16-May-2011 22-May-2011
2 23-May-2011 27-May-2011
请帮我解决一下这个问题。我的星期开始日期是星期一。
答案 0 :(得分:1)
根据最后的理解,这将有效:
with demo_cte as
(select id,
start_date,
dateadd(day,6,DATEADD(wk, DATEDIFF(wk,0,start_date), 0)) end_date,
end_date last_end_date,
no_of_weeks no_of_weeks from demo
union all
select id,dateadd(day,1,end_date),
dateadd(day,7,end_date),
last_end_date
,no_of_weeks-1 from demo_cte
where no_of_weeks-1>0)
select id, start_date,
case
when end_date<=last_end_date then end_date
else
last_end_date
end
end_date
from demo_cte order by id,no_of_weeks desc
如果无法使用周数,请使用:
with demo_cte as
(select id,
start_date,
dateadd(day,6,DATEADD(wk, DATEDIFF(wk,0,start_date), 0)) end_date,
end_date last_end_date
--,no_of_weeks no_of_weeks
from demo
union all
select id,dateadd(day,1,end_date),
dateadd(day,7,end_date),
last_end_date
--,no_of_weeks-1
from demo_cte
where --no_of_weeks-1>0
dateadd(day,7,end_date)<=last_end_date
)
select id, start_date,
case
when end_date<=last_end_date then end_date
else
last_end_date
end
end_date
from demo_cte order by id,start_date
--,no_of_weeks desc
答案 1 :(得分:1)
您可以使用定义周数的日历表并将其加入您的数据。
我为以下内容创建了sql fiddle:
CREATE TABLE Calendar_Weeks (
week_start_date date,
week_end_date date )
CREATE TABLE Sample_Data (
id int,
start_date date,
end_date date )
INSERT Calendar_Weeks (week_start_date, week_end_date) VALUES ('2011-04-18','2011-04-24')
INSERT Calendar_Weeks (week_start_date, week_end_date) VALUES ('2011-04-25','2011-05-01')
INSERT Calendar_Weeks (week_start_date, week_end_date) VALUES ('2011-05-02','2011-05-08')
INSERT Calendar_Weeks (week_start_date, week_end_date) VALUES ('2011-05-09','2011-05-15')
INSERT Calendar_Weeks (week_start_date, week_end_date) VALUES ('2011-05-16','2011-05-22')
INSERT Calendar_Weeks (week_start_date, week_end_date) VALUES ('2011-05-23','2011-05-29')
INSERT Sample_Data (id, start_date, end_date) VALUES (1, '2011-04-25','2011-05-08')
INSERT Sample_Data (id, start_date, end_date) VALUES (2, '2011-04-23','2011-05-27')
SELECT id, week_start_date, week_end_date
FROM Sample_Data CROSS JOIN Calendar_Weeks
WHERE week_start_date BETWEEN start_date AND end_date
UNION
SELECT id, week_start_date, week_end_date
FROM Sample_Data CROSS JOIN Calendar_Weeks
WHERE week_end_date BETWEEN start_date AND end_date
我必须承认{{1>}的查询感觉有点黑客在项目的开头或结尾包含行,所以您可能更喜欢使用Ravi Singh的解决方案
如果您愿意,也可以使用UNION
:
INNER JOIN
答案 2 :(得分:1)
设置环境测试
declare @dt table (ID int,Start_date datetime,
End_date datetime,No_Of_Weeks int)
insert into @dt (ID,Start_date,End_date,No_Of_Weeks)
select 1, '25-Apr-11', '8-May-11', 2
union all
select 2, '23-Apr-11' , '27-MAy-11' , 6;
试试这个......
with cte as (select d.ID
,d.Start_date
,(select MIN([end]) from (values(d.End_date),(DATEADD(day,-1,DATEADD(week,DATEDIFF(week,0,d.Start_date)+1,0))))V([end])) as End_date
,d.End_date as end_of_period
from @dt d
union all select d.ID
,DATEADD(day,1,d.End_date) as Start_date
, case when d.end_of_period < DATEADD(week,1,d.End_date) then d.end_of_period else DATEADD(week,1,d.End_date) end as End_date
,d.end_of_period as end_of_period
from cte d
where end_of_period <> End_date
)
select ID
,cast(Start_date as DATE) Start_date
,cast(End_date as date) End_date
from cte
order by cte.ID,cte.Start_date
option(maxrecursion 0)
结果集实现了......
ID Start_date End_date
1 2011-04-25 2011-05-01
1 2011-05-02 2011-05-08
2 2011-04-23 2011-04-24
2 2011-04-30 2011-05-01
2 2011-05-07 2011-05-08
2 2011-05-14 2011-05-15
2 2011-05-21 2011-05-22
2 2011-05-28 2011-05-27
答案 3 :(得分:1)
尝试此查询,希望它能正常工作
如果您的星期在星期日开始,请使用以下
set datefirst 7
declare @FromDate datetime = '20130110'
declare @ToDate datetime = '20130206'
select datepart(week, @ToDate) - datepart(week, @FromDate) + 1
如果您的一周从星期一开始,请使用以下
set datefirst 1
declare @FromDate datetime = '20100201'
declare @ToDate datetime = '20100228'
select datepart(week, @ToDate) - datepart(week, @FromDate) + 1
注意:两个查询都会产生不同的结果,因为它们的开始日期不同。
答案 4 :(得分:0)
您应该查看使用DATEDIFF功能。
我不确定你在问题的第二部分要求的是什么,但是一旦你得到了日期之间的差异,你可能想看看你的DATEDIFF结果使用CASE
答案 5 :(得分:0)
这是一个使用 datepart 功能帐户的解决方案,因为周从星期一开始 :
with demo_normalized as
(
select id,
start_date,
(datepart(dw,start_date) + 5) % 7 as test,
dateadd(d,
0 - ((datepart(dw,start_date) + 5) % 7),
start_date
) as start_date_firstofweek,
dateadd(d,
6 - ((datepart(dw,start_date) + 5) % 7),
start_date
) as start_date_lastofweek,
end_date,
dateadd(d,
0 - ((datepart(dw,end_date) + 5) % 7),
end_date
) as end_date_firstofweek,
dateadd(d,
6 - ((datepart(dw,end_date) + 5) % 7),
end_date
) as end_date_lastofweek,
datediff(week,
dateadd(d,
0 - ((datepart(dw,start_date) + 5) % 7),
start_date
),
dateadd(d,
6 - ((datepart(dw,end_date) + 5) % 7),
end_date
)
) as no_of_weeks
from demo
),
demo_cte as
(
select
id,
dateadd(day,7,start_date_firstofweek) as start_date,
dateadd(day,7,start_date_lastofweek) as end_date,
end_date_firstofweek,
no_of_weeks
from demo_normalized
where no_of_weeks >= 3
UNION ALL select
id,
dateadd(day,7,start_date) as start_date,
dateadd(day,7,end_date) as end_date,
end_date_firstofweek,
no_of_weeks
from demo_cte
where
(dateadd(day,8,start_date) < end_date_firstofweek)
),
demo_union as
(
select id, start_date, end_date, no_of_weeks from demo_normalized where no_of_weeks = 1
union all
select id, start_date, start_date_lastofweek as end_date, no_of_weeks
from demo_normalized where no_of_weeks >= 2
union all
select id, start_date, end_date, no_of_weeks from demo_cte
union all
select id, end_date_firstofweek as start_date, end_date, no_of_weeks
from demo_normalized where no_of_weeks >= 2
)
select
d0.id,
d0.no_of_weeks,
convert(varchar, d0.start_date, 106) as start_date,
convert(varchar, d0.end_date, 106) as end_date
from demo_union d0
order by d0.id, d0.start_date
编辑(两周之间加入CTE): 这是link to sqlfiddle。
注意:此解决方案不需要额外的DDL - 不必创建和维护其他实体。简而言之,它不会重新发明日历。 所有日历逻辑都包含在查询中。
答案 6 :(得分:0)
您应该使用类似于Jeff Moden在The "Numbers" or "Tally" Table: What it is and how it replaces a loop中建议的类型的日期统计表(必需登录)。
Tally表只不过是一个表,其中包含一列非常好的索引序列号,从0或1开始(我的开始时为1)并且上升到某个数字。 Tally表中最大的数字不应该只是一些任意选择。它应该基于您认为您将使用它的内容。我将VARCHAR(8000)与我分开,所以它必须至少有8000个数字。由于我偶尔需要生成30年的日期,因此我将大部分生产的Tally表保持在11,000或更多,超过365.25天30年。
我从Tony的SQL Fiddler开始,但实现了一个DateInformation
表更加通用。这可能是你可以重用的东西。
--Build Test Data, For production set the date
--range large enough to handle all cases.
CREATE TABLE DateInformation (
[Date] date,
WeekDayNumber int,
)
--From Tony
CREATE TABLE Sample_Data (
id int,
start_date date,
end_date date )
DECLARE @CurrentDate Date = '2010-12-27'
While @CurrentDate < '2014-12-31'
BEGIN
INSERT DateInformation VALUES (@CurrentDate,DatePart(dw,@CurrentDate))
SET @CurrentDate = DATEADD(DAY,1,@CurrentDate)
END
--From Tony
INSERT Sample_Data VALUES (1, '2011-04-25','2011-05-08')
INSERT Sample_Data VALUES (2, '2011-04-23','2011-05-27')
以下是使用CTE将样本数据连接到DateInformation
表的解决方案。
--Solution using CTE
with Week (WeekStart,WeekEnd) as
(
select d.Date
,dateadd(day,6,d.date) as WeekEnd
from DateInformation d
where d.WeekDayNumber = 2
)
select
s.ID
,case when s.Start_date > w.WeekStart then s.Start_Date
else w.WeekStart end as Start_Date
,case when s.End_Date < w.WeekEnd then s.End_Date
else w.WeekEnd end as End_Date
from Sample_Data s
join Week w on w.WeekStart > dateadd(day,-6,s.start_date)
and w.WeekEnd <= dateadd(day,6,s.end_date);
答案 7 :(得分:0)
set datefirst 1
GO
with cte as (
select ID, Start_date, End_date, Start_date as Week_Start_Date, (case when datepart(weekday, Start_date) = 7 then Start_Date else cast(null as datetime) end) as Week_End_Date, datepart(weekday, Start_date) as start_weekday, cast(0 as int) as week_id
from (
values (1, cast('25-Apr-2011' as datetime), cast('8-May-2011' as datetime)),
(2, cast('23-Apr-2011' as datetime), cast('27-May-2011' as datetime))
) t(ID, Start_date, End_date)
union all
select ID, Start_date, End_date, dateadd(day, 1, Week_Start_date) as Week_Start_Date, (case when start_weekday + 1 = 7 then dateadd(day, 1, Week_Start_date) else null end) as Week_End_date, (case when start_weekday = 7 then 1 else start_weekday + 1 end) as start_weekday, (case when start_weekday = 7 then week_id + 1 else week_id end) as week_id
from cte
where Week_Start_Date != End_date
)
select ID, min(Week_Start_Date), isnull(max(Week_End_Date), max(End_Date))
from cte
group by ID, Week_id
order by ID, 2
option (maxrecursion 0)
如果您想获得周数,可以在cte之后更改选择:
select ID, Start_date, End_date, count(distinct week_id) as Number_Of_Weeks
from cte
group by ID, Start_date, End_date
option (maxrecursion 0)
显然要更改使用的数据,将更改使用values()的cte的锚点(第一部分)。
这使用星期一作为一周的第一天。在另一天给我们,请更改顶部的set datefirst
语句 - http://msdn.microsoft.com/en-gb/library/ms181598.aspx
答案 8 :(得分:0)
享受!
WITH D AS (
SELECT id
, start_date
, end_date
, start_date AS WEEK_START
, start_date + 7 - DATEPART(weekday,start_date) + 1
AS week_end
FROM DATA
), W AS (
SELECT id
, start_date
, end_date
, WEEK_START
, WEEK_END
FROM D
UNION ALL
SELECT id
, start_date
, end_date
, WEEK_END + 1 AS WEEK_START
, WEEK_END + 7 AS WEEK_END
FROM W
WHERE WEEK_END < END_DATE
)
SELECT ID
, WEEK_START AS START_DATE
, WEEK_END AS END_DATE
FROM W
ORDER BY 1, 2;