我有一个查询(下面)将在特定时间开始。从那时起我需要一次增加一周(每个新行将是一个新的星期 - 从星期日到星期六)我已经完成了 - 但新的皱纹是,如果它是月底它需要在那个日期停下来,并在本月的第一天开始,但仍然在周六停止。结果集好/坏列在下面,也是我的查询到目前为止。
结果不好:
2016-05-22 2016-05-28
2016-05-29 2016-06-04
我需要什么:
2016-05-22 2016-05-28
2016-05-29 2016-05-31
2016-06-01 2016-06-04
代码:
BEGIN
DECLARE @StartDate DATE = DATEFROMPARTS(2016, 5, 22);
DECLARE @EndDate DATE = CAST(GETDATE() AS DATE);
DECLARE @Today DATE = @EndDate;
DECLARE @EndOfMonth DATE = @StartDate
; WITH [Dates] AS (
SELECT
@StartDate AS [StartDate],
DATEADD(DAY, 6, @StartDate) AS [EndDate]
UNION ALL
SELECT
DATEADD(DAY, 7, [StartDate]),
DATEADD(DAY, 7, [EndDate])
FROM [Dates]
WHERE DATEADD(DAY, 7, [StartDate]) <= @EndDate
)
SELECT
[tcw].[StartDate],
[tcw].[EndDate]
FROM [Dates] AS [tcw]
OPTION (MAXRECURSION 0)
END
GO
答案 0 :(得分:1)
鉴于您使用DATEFROMPARTS
,我假设您使用的是SQL 2012或更高版本。在确定第一个和最后一个日期时,我也依赖于您现有的逻辑。在此基础上,使用以下内容替换您的查询:
; WITH [Dates] AS (
SELECT
@StartDate AS [StartDate],
DATEADD(DAY, 6, @StartDate) AS [EndDate]
UNION ALL
SELECT
DATEADD(DAY, 7, [StartDate]),
DATEADD(DAY, 7, [EndDate])
FROM [Dates]
WHERE DATEADD(DAY, 7, [StartDate]) <= @EndDate
)
-- All weeks where all dates are within the same month
SELECT
StartDate
,EndDate
FROM [Dates]
WHERE MONTH(StartDate) = MONTH(EndDate)
-- For weeks where all dates not within the same month, the last week in the month
UNION ALL SELECT
StartDate
,EOMONTH(StartDate)
FROM [Dates]
WHERE MONTH(StartDate) <> MONTH(EndDate)
-- For weeks where all dates not within the same month, the first week in the (next) month
UNION ALL SELECT
DATEADD(dd, 1, EOMONTH(StartDate))
,EndDate
FROM [Dates]
WHERE MONTH(StartDate) <> MONTH(EndDate)
工会让它有点尴尬,但除非你每次通过处理几个世纪,否则它会跑得足够快。另请注意,某些“周”将包含一天,例如2016年7月31日至2016年7月31日。
- 补遗,基于评论---------------------------------
以下查询执行此操作,但有一个很大的警告......
; WITH cteDates AS (
SELECT
@StartDate AS StartDate,
DATEADD(DAY, 6, @StartDate) AS EndDate,
CASE
WHEN DATEPART(dw, EOMONTH(@StartDate)) between 2 and 6 then 0 -- Assumes SET DATEFIRST is 1!
ELSE 1
END AS MonthEndsOnWeekend
UNION ALL
SELECT
DATEADD(DAY, 7, StartDate),
DATEADD(DAY, 7, EndDate),
CASE
WHEN DATEPART(dw, EOMONTH(DATEADD(DAY, 7, StartDate))) between 2 and 6 then 0
ELSE 1
END
FROM cteDates
WHERE DATEADD(DAY, 7, StartDate) <= @EndDate
)
-- All weeks where all dates are within the same month,
-- and all month-ending weeks where the last day of the month is Saturday or Sunday
SELECT
StartDate
,EndDate
FROM cteDates
WHERE MONTH(StartDate) = MONTH(EndDate)
OR MonthEndsOnWeekend = 1
-- For weeks where all dates not within the same month,
-- and the the last day of the month is NOT Saturday or Sunday,
-- the last week in the month
UNION ALL SELECT
StartDate
,EOMONTH(StartDate)
FROM cteDates
WHERE MONTH(StartDate) <> MONTH(EndDate)
AND MonthEndsOnWeekend = 0
-- For weeks where all dates not within the same month,
-- and the the last day of the month is NOT Saturday or Sunday,
-- the first week in the (next) month
UNION ALL SELECT
DATEADD(dd, 1, EOMONTH(StartDate))
,EndDate
FROM cteDates
WHERE MONTH(StartDate) <> MONTH(EndDate)
AND MonthEndsOnWeekend = 0
我使用DATEPART
函数来识别星期几(星期六,星期日等)。 SQL将为此函数返回一个数字,其中一周中返回的数字取决于SET DATEFIRST的设置。对于我的安装,我们使用默认值,其中1 =星期一。如果您无法完全控制SET DATEFIRST 到处的设置,您的代码可能会被运行,永远,那么您可能会遇到本讨论范围之外的问题。我使用的另一种方法是使用DATENAME
,它将返回字符串,例如星期六,星期天等等......但是这与SET LANGUAGE的设置有同样的问题。
(fyi,我也取出了[]并重命名为cte,因为他们在烦我。)
答案 1 :(得分:0)
查看您的代码我假设您使用的是sql-server。 EOMONTH()在sql-server 2012 +中可用,可以更轻松地查找结束日期。基本上,您需要测试开始日期的星期几以及开始日期的月末,然后通过递归进行平衡。如果需要,您可以添加另一个LEVEL列并跟踪周数。例如。 1在UNION Level + 1中作为Level的第一个查询中的LEVEL。
DECLARE @StartDate DATE = '2016-05-22'
DECLARE @EndDate DATE = GETDATE()
--if @StartDate provided is middle of the week and you want to adjust to sunday use this
SET @StartDate = CASE
WHEN DATEPART(WEEKDAY,@StartDate) = 1 THEN @StartDate
ELSE DATEADD(day,-7 + DATEPART(WEEKDAY,@StartDate),@StartDate)
END
;WITH cteRecursiveDates AS (
SELECT
@StartDate as Startdate
,CASE
WHEN DATEDIFF(day,@StartDate,EOMONTH(@StartDate)) < (7 - DATEPART(WEEKDAY,@StartDate))
THEN DATEADD(day,DATEDIFF(day,@StartDate,EOMONTH(@StartDate)) ,@StartDate)
ELSE DATEADD(day,(7- DATEPART(WEEKDAY,@StartDate)),@StartDate)
END as EndDate
UNION ALL
SELECT
DATEADD(day,1,c.EndDate) as StartDate
,CASE
WHEN DATEDIFF(day,DATEADD(day,1,c.EndDate),EOMONTH(DATEADD(day,1,c.EndDate))) < (7 - DATEPART(WEEKDAY,DATEADD(day,1,c.EndDate)))
THEN DATEADD(day,DATEDIFF(day,DATEADD(day,1,c.EndDate),EOMONTH(DATEADD(day,1,c.EndDate))) ,DATEADD(day,1,c.EndDate))
ELSE DATEADD(day,(7- DATEPART(WEEKDAY,DATEADD(day,1,c.EndDate))),DATEADD(day,1,c.EndDate))
END as EndDate
FROM
cteRecursiveDates c
WHERE
DATEADD(day,1,c.EndDate) <= @EndDate
)
SELECT *
FROM
cteRecursiveDates
答案 2 :(得分:0)
我添加了星期几(乞讨/结束)只是为了说明
Declare @Date1 Date='2016-05-22'
Declare @Date2 Date='2016-07-31'
;with cteBase as (
Select *
,Flag1=IIF(Row_Number() over (Partition By Month(RetVal) Order By RetVal)=1 or DatePart(WEEKDAY,RetVal)=1 or Day(RetVal)=1 ,1,0)
,Flag2=IIF(DatePart(WEEKDAY,RetVal)=7 or Lead(Day(RetVal),1) over (Order By RetVal)=1,1,0)
,RowNr=Row_Number() over (Order By RetVal)
From [dbo].[udf-Create-Range-Date](@Date1,@Date2,'DD',1)
)
Select DateR1=cast(A.RetVal as Date)
,B.DateR2
,DOW1=DateName(Weekday,A.RetVal)
,DOW2=DateName(Weekday,B.DateR2)
From cteBase A
Cross Apply (Select DateR2=min(cast(RetVal as date)) From cteBase Where Flag2=1 and RowNr>=A.RowNr) B
Where A.Flag1=1 and B.DateR2 is not null
返回
DateR1 DateR2 DOW1 DOW2
2016-05-22 2016-05-28 Sunday Saturday
2016-05-29 2016-05-31 Sunday Tuesday
2016-06-01 2016-06-04 Wednesday Saturday
2016-06-05 2016-06-11 Sunday Saturday
2016-06-12 2016-06-18 Sunday Saturday
2016-06-19 2016-06-25 Sunday Saturday
2016-06-26 2016-06-30 Sunday Thursday
2016-07-01 2016-07-02 Friday Saturday
2016-07-03 2016-07-09 Sunday Saturday
2016-07-10 2016-07-16 Sunday Saturday
2016-07-17 2016-07-23 Sunday Saturday
2016-07-24 2016-07-30 Sunday Saturday
我用来创建动态日期范围的UDF
CREATE FUNCTION [dbo].[udf-Create-Range-Date] (@DateFrom datetime,@DateTo datetime,@DatePart varchar(10),@Incr int)
Returns
@ReturnVal Table (RetVal datetime)
As
Begin
With DateTable As (
Select DateFrom = @DateFrom
Union All
Select Case @DatePart
When 'YY' then DateAdd(YY, @Incr, df.dateFrom)
When 'QQ' then DateAdd(QQ, @Incr, df.dateFrom)
When 'MM' then DateAdd(MM, @Incr, df.dateFrom)
When 'WK' then DateAdd(WK, @Incr, df.dateFrom)
When 'DD' then DateAdd(DD, @Incr, df.dateFrom)
When 'HH' then DateAdd(HH, @Incr, df.dateFrom)
When 'MI' then DateAdd(MI, @Incr, df.dateFrom)
When 'SS' then DateAdd(SS, @Incr, df.dateFrom)
End
From DateTable DF
Where DF.DateFrom < @DateTo
)
Insert into @ReturnVal(RetVal) Select DateFrom From DateTable option (maxrecursion 32767)
Return
End
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2020-10-01','YY',1)
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2020-10-01','DD',1)
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2016-10-31','MI',15)
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2016-10-02','SS',1)
答案 3 :(得分:0)
将任何违反月末的任何一周拆分为两个,然后联合回到那些没有超过月底的行。
declare
@StartDate date = datefromparts(2016, 5, 22),
@EndDate date = cast(getdate() as date)
;
with
dates as
(
select
@StartDate as StartDate,
dateadd(day, 6, @StartDate) as EndDate,
eomonth(@StartDate) as EndOfMonth,
dateadd(day, 1 - day(dateadd(month, 1, @StartDate)),
dateadd(month, 1, @StartDate)) as FirstDayOfNextMonth
union all
select
dateadd(day, 7, d.StartDate),
dateadd(day, 7, d.EndDate),
eomonth(dateadd(day, 7, d.StartDate)),
dateadd(day, 1 - day(dateadd(month, 1, d.StartDate)),
dateadd(month, 1, d.StartDate))
from
dates as d
where
dateadd(day, 7, StartDate) <= @EndDate
)
-- week within the month
select
d.StartDate,
d.EndDate
from
dates as d
where
d.EndDate <= d.EndOfMonth
union all
-- week breach to next month, first part
select
d.StartDate,
d.EndOfMonth as EndDate
from
dates as d
where
d.EndDate > d.EndOfMonth
union all
-- week breach to next month, second part
select
d.FirstDayOfNextMonth as StartDate,
d.EndDate
from
dates as d
where
d.EndDate > d.EndOfMonth
order by 1
option (maxrecursion 0)
结果:
| StartDate | EndDate |
|------------|------------|
| 2016-05-22 | 2016-05-28 |
| 2016-05-29 | 2016-05-31 |
| 2016-06-01 | 2016-06-04 |
| 2016-06-05 | 2016-06-11 |
| 2016-06-12 | 2016-06-18 |
| 2016-06-19 | 2016-06-25 |
| 2016-06-26 | 2016-06-30 |
| 2016-07-01 | 2016-07-02 |
| 2016-07-03 | 2016-07-09 |
| 2016-07-10 | 2016-07-16 |
| 2016-07-17 | 2016-07-23 |
| 2016-07-24 | 2016-07-30 |
| 2016-07-31 | 2016-07-31 |
| 2016-08-01 | 2016-08-06 |
| 2016-08-07 | 2016-08-13 |
| 2016-08-14 | 2016-08-20 |