我有一个脚本,可以生成日历表,并填写英格兰和威尔士的公共假日和周末。我希望将非工作日作为开始和结束时间,因此请输入
1980-04-01 00:00:00.000 Tuesday 0
1980-04-02 00:00:00.000 Wednesday 0
1980-04-03 00:00:00.000 Thursday 0
1980-04-04 00:00:00.000 Friday 1
1980-04-05 00:00:00.000 Saturday 1
1980-04-06 00:00:00.000 Sunday 1
1980-04-07 00:00:00.000 Monday 1
1980-04-08 00:00:00.000 Tuesday 0
1980-04-09 00:00:00.000 Wednesday 0
它将给出输出
Start end
1980-04-03 1980-04-08
换句话说,是组中第一个非工作日的前一天和最后一个非工作日的后一天。要做到这一点,我想可以将min和max与group和dateadd一起使用,但是如何获得一个唯一的数字来对每组天进行分组?这是使用2008 R2。
IF OBJECT_ID('tempdb.dbo.#calendar', 'U') IS NOT NULL
DROP TABLE #calendar
IF OBJECT_ID('tempdb.dbo.#easter', 'U') IS NOT NULL
DROP TABLE #easter;
-- CREATE TABLE CALENDAR
CREATE TABLE #calendar
(
[CalendarDate] DATETIME,
dayofwk varchar(20),
)
DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
--INPUTS GO HERE
SET @StartDate = '01-01-1980'
SET @EndDate = '31-12-2018'
DECLARE @Startyear int
DECLARE @endyear int
set @startyear = YEAR(@StartDate)
set @endyear = YEAR(@EndDate)
WHILE @StartDate <= @EndDate
BEGIN
INSERT INTO #calendar
(
CalendarDate, dayofwk
)
SELECT
@StartDate, datename(dw,@startdate)
SET @StartDate = DATEADD(dd, 1, @StartDate)
END
---CREATE LIST OF EASTER MONDAY & GOOD FRIDAYS
create table #easter(
eastersunday_goodfriday date)
DECLARE @y int,
@EpactCalc INT,
@PaschalDaysCalc INT,
@NumOfDaysToSunday INT,
@EasterMonth INT,
@EasterDay INT
WHILE @Startyear <= @endyear
BEGIN
SET @y = @startyear
SET @EpactCalc = (24 + 19 * (@Y % 19)) % 30
SET @PaschalDaysCalc = @EpactCalc - (@EpactCalc / 28)
SET @NumOfDaysToSunday = @PaschalDaysCalc - (
(@Y + @Y / 4 + @PaschalDaysCalc - 13) % 7
)
SET @EasterMonth = 3 + (@NumOfDaysToSunday + 40) / 44
SET @EasterDay = @NumOfDaysToSunday + 28 - (
31 * (@EasterMonth / 4)
)
insert into #easter
SELECT dateadd(d,-2, CONVERT
( SMALLDATETIME,
RTRIM(@Y)
+ RIGHT('0'+RTRIM(@EasterMonth), 2)
+ RIGHT('0'+RTRIM(@EasterDay), 2) ))
insert into #easter
SELECT dateadd(d,1, CONVERT
( SMALLDATETIME,
RTRIM(@Y)
+ RIGHT('0'+RTRIM(@EasterMonth), 2)
+ RIGHT('0'+RTRIM(@EasterDay), 2) ))
SET @Startyear =@Startyear +1
end
select calendardate, dayofwk,
--NEW YEAR'S DAY
case
when day(calendardate) = 1 and month(calendardate) = 1 and dayofwk not in ('Saturday', 'sunday') then 1
when day(calendardate) between 2 and 3 and month(calendardate) = 1 and dayofwk = 'Monday' then 1
--GOOD FRIDAY, EASTER MONDAY
WHEN eastersunday_goodfriday IS NOT NULL THEN 1
--EARLY MAY BANK HOLIDAY
WHEN (datepart(weekday, CALENDARDATE) + @@DATEFIRST - 2) % 7 + 1 = 1
and (datepart(day, CALENDARDATE) - 1) / 7 + 1 = 1
AND MONTH(CALENDARDATE) = 5
THEN 1
--LATE MAY BANK HOLIDAY
WHEN (datepart(weekday, CALENDARDATE) + @@DATEFIRST - 2) % 7 + 1 = 1
and (datepart(day, CALENDARDATE) - 1) / 7 + 1 = 4
AND MONTH(CALENDARDATE) = 5
THEN 1
--LATE AUGUST BANK HOLIDAY
WHEN (datepart(weekday, CALENDARDATE) + @@DATEFIRST - 2) % 7 + 1 = 1
and (datepart(day, CALENDARDATE) - 1) / 7 + 1 = 4
AND MONTH(CALENDARDATE) = 8
THEN 1
--CHRISTMAS DAY
WHEN DAY(CalendarDate) = 25 AND MONTH(CALENDARDATE) = 12 AND dayofwk NOT IN ('SATURDAY', 'SUNDAY') THEN 1
--BOXING DAY
WHEN DAY(CalendarDate) = 26 AND MONTH(CALENDARDATE) = 12 AND dayofwk NOT IN ('SATURDAY', 'SUNDAY') THEN 1
WHEN DAY(CalendarDate) between 27 and 28 AND MONTH(CALENDARDATE) = 12 AND DAYOFWK IN ('MONDAY','TUESDAY') THEN 1
--SAT&SUN
WHEN DATENAME(DW,CALENDARDATE) IN ('SATURDAY','SUNDAY') THEN 1
ELSE 0
end as ISNONWORKINGDAY
from #calendar c
left join #easter e
on c.calendardate = e.eastersunday_goodfriday
ORDER BY C.CALENDARDATE
答案 0 :(得分:0)
此接缝就像一个典型的岛屿问题。鉴于您发布的内容,您可以执行以下操作:
DECLARE @table TABLE (dt DATE, isNonWorkDay BIT);
INSERT @table(dt,isNonWorkDay)
VALUES
('1980-04-01', 0),
('1980-04-02', 0),
('1980-04-03', 0),
('1980-04-04', 1),
('1980-04-05', 1),
('1980-04-06', 1),
('1980-04-07', 1),
('1980-04-08', 0),
('1980-04-09', 0);
SELECT DATEADD(DAY,-1,MIN(t.dt)), DATEADD(DAY,1,MAX(t.dt))
FROM @table t
WHERE t.isNonWorkDay = 1;
返回:1980-04-03、1980-04-08
对于多个岛屿,您可以执行以下操作:
DECLARE @table TABLE (dt DATE, isNonWorkDay BIT);
INSERT @table(dt,isNonWorkDay)
VALUES
('1980-04-01', 0),
('1980-04-02', 0),
('1980-04-03', 0),
('1980-04-04', 1),
('1980-04-05', 1),
('1980-04-06', 1),
('1980-04-07', 1),
('1980-04-08', 0),
('1980-04-09', 0),
('1980-04-10', 0),
('1980-04-11', 1),
('1980-04-12', 1),
('1980-04-13', 1),
('1980-04-14', 0),
('1980-04-15', 0);
WITH x AS
(
SELECT *, rn = ROW_NUMBER() OVER (ORDER BY t.dt)
FROM @table t
),
xx AS
(
SELECT *, grouper = x.rn - ROW_NUMBER() OVER (ORDER BY x.dt)
FROM x
WHERE x.isNonWorkDay = 1
)
SELECT dtStart = DATEADD(DAY,-1,MIN(xx.dt)), dtend = DATEADD(DAY,1,MAX(xx.dt))
FROM xx
GROUP BY xx.grouper;
返回:
dtStart dtend
---------- ----------
1980-04-03 1980-04-08
1980-04-10 1980-04-14
答案 1 :(得分:0)
您可以使用全天连续编号与所有非工作日连续编号之差来进行分组。 对于连续的一组非工作日,这些差异将具有相同的值,并且由于各组之间的间隔(工作日),下一组将具有不同的(较高)值。
对于全天编号,您可以使用DATEDIFF,对于非工作日编号,您可以使用ROW_NUMBER。查询看起来可能像这样:
WITH
numbering (calendardate, grp_num) AS (
SELECT
calendardate,
DATEDIFF(day, 0, calendardate) - ROW_NUMBER() OVER (ORDER BY calendardate)
FROM YourTable WHERE ISNONWORKINGDAY = 1
)
SELECT
DATEADD(day, -1, MIN(calendardate)) AS StartDate,
DATEADD(day, +1, MAX(calendardate)) AS EndDate
FROM numbering GROUP BY grp_num;