SQL 填充每月总工作日减去当前财政年度的银行假期
Posted
技术标签:
【中文标题】SQL 填充每月总工作日减去当前财政年度的银行假期【英文标题】:SQL populate total working days per month minus bank holidays for current financial year 【发布时间】:2012-02-07 16:48:08 【问题描述】:我正在寻找一个看起来像我的第一张附加图片的视图,但是右侧的列已填充且不为空白。逻辑如下:
数据必须是当前财政期间的数据。因此 4 月将是 2011 年,3 月将是 2012 年,依此类推。
单月的可用天数计算如下:
工作日总数(周一至周五)减去属于该特定月份的任何银行假期,对于该特定财政年度(我们已保存在表格中 - 请参见第二张图片)。
假日表的列名从左到右:holidaytypeid、name、holstart、holend。 表名:holidaytable
要计算出累积月份“可用天数”,需要对单个月份的已填充数据求和。例如 April-May 将是 April 和 May 的数据 SUMMED 等等。
我需要完美格式的 SQL 查询,以便可以直接粘贴并正常工作(即使用正确的列名和表名)
感谢收看。
【问题讨论】:
你有一个dates
表,一个包含每个日期列表的表吗?如果没有,那么我建议你买一个。
不,我没有这样的表,无法在这个数据库上创建表
【参考方案1】:
DECLARE @StartDate DATETIME, @EndDate DATETIME
SELECT @StartDate = '01/04/2011',
@EndDate = '31/03/2012'
CREATE TABLE #Data (FirstDay DATETIME NOT NULL PRIMARY KEY, WorkingDays INT NOT NULL)
;WITH DaysCTE ([Date]) AS
( SELECT @StartDate
UNION ALL
SELECT DATEADD(DAY, 1, [Date])
FROM DaysCTE
WHERE [Date] <= @Enddate
)
INSERT INTO #Data
SELECT MIN([Date]),
COUNT(*) [Day]
FROM DaysCTE
LEFT JOIN HolidayTable
ON [Date] BETWEEN HolStart AND HolEnd
WHERE HolidayTypeID IS NULL
AND DATENAME(WEEKDAY, [Date]) NOT IN ('Saturday', 'Sunday')
GROUP BY DATEPART(MONTH, [Date]), DATEPART(YEAR, [Date])
OPTION (MAXRECURSION 366)
DECLARE @Date DATETIME
SET @Date = (SELECT MIN(FirstDay) FROM #Data)
SELECT Period,
WorkingDays [Days Available (Minus the Holidays)]
FROM ( SELECT DATENAME(MONTH, Firstday) [Period],
WorkingDays,
0 [SortField],
FirstDay
FROM #Data
UNION
SELECT DATENAME(MONTH, @Date) + ' - ' + DATENAME(MONTH, Firstday),
( SELECT SUM(WorkingDays)
FROM #Data b
WHERE b.FirstDay <= a.FirstDay
) [WorkingDays],
1 [SortField],
FirstDay
FROM #Data a
WHERE FirstDay > @Date
) data
ORDER BY SortField, FirstDay
DROP TABLE #Data
如果您这样做超过 1 年,您将需要更改线路:
OPTION (MAXRECURSION 366)
否则你会得到一个错误 - 数字必须大于你查询的天数。
编辑
我刚刚遇到了我的这个旧答案,真的不喜欢它,有很多事情我现在认为是不好的做法,所以我要纠正所有问题:
-
I did not terminate statements with a semi colon properly
使用递归 CTE 生成日期列表
Generate a set or sequence without loops – part 1
Generate a set or sequence without loops – part 2
Generate a set or sequence without loops – part 3
Did not include the column list for an insert
使用 DATENAME 来消除周末,这是特定于语言的,最好明确设置
DATEFIRST
并使用 DATEPART
使用LEFT JOIN/IS NULL
而不是NOT EXISTS
来消除假期表中的记录。 In SQL Server LEFT JOIN/IS NULL is less efficient than NOT EXISTS
这些都是小事,但它们是我在审查别人的问题时会批评的事情(至少在我的脑海里,如果不是大声说出来的话),所以我真的不能不纠正我自己的工作!重写查询会给出。
SET DATEFIRST 1;
DECLARE @StartDate DATETIME = '20110401',
@EndDate DATETIME = '20120331';
CREATE TABLE #Data (FirstDay DATETIME NOT NULL PRIMARY KEY, WorkingDays INT NOT NULL);
WITH DaysCTE ([Date]) AS
( SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate) + 1)
DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @StartDate)
FROM sys.all_objects a
)
INSERT INTO #Data (FirstDay, WorkingDays)
SELECT FirstDay = MIN([Date]),
WorkingDays = COUNT(*)
FROM DaysCTE d
WHERE DATEPART(WEEKDAY, [Date]) NOT IN (6, 7)
AND NOT EXISTS
( SELECT 1
FROM dbo.HolidayTable ht
WHERE d.[Date] BETWEEN ht.HolStart AND ht.HolEnd
)
GROUP BY DATEPART(MONTH, [Date]), DATEPART(YEAR, [Date]);
DECLARE @Date DATETIME = (SELECT MIN(FirstDay) FROM #Data);
SELECT Period,
[Days Available (Minus the Holidays)] = WorkingDays
FROM ( SELECT DATENAME(MONTH, Firstday) [Period],
WorkingDays,
0 [SortField],
FirstDay
FROM #Data
UNION
SELECT DATENAME(MONTH, @Date) + ' - ' + DATENAME(MONTH, Firstday),
( SELECT SUM(WorkingDays)
FROM #Data b
WHERE b.FirstDay <= a.FirstDay
) [WorkingDays],
1 [SortField],
FirstDay
FROM #Data a
WHERE FirstDay > @Date
) data
ORDER BY SortField, FirstDay;
DROP TABLE #Data;
最后一点,使用 calendar table 存储所有日期并具有工作日、假期等标志,而不是使用仅存储假期的假期表,此查询变得更加简单。
【讨论】:
【参考方案2】:让我在这篇文章中加几分钱。刚刚获得计算计划时间和实际时间之间差异的任务。下面的代码被转换为一个函数。到目前为止,逻辑没有问题:
declare @date datetime = '11/07/2012'
declare @t table (HolidayID int IDENTITY(1,1) primary key,
HolidayYear int,
HolidayName varchar(50),
HolidayDate datetime)
INSERT @t
VALUES(2012, 'New Years Day', '01/02/2012'),
(2012,'Martin Luther King Day', '01/16/2012'),
(2012,'Presidents Day', '02/20/2012'),
(2012,'Memorial Day', '05/28/2012'),
(2012,'Independence Day', '07/04/2012'),
(2012,'Labor Day', '09/03/2012'),
(2012,'Thanksgiving Day', '11/22/2012'),
(2012,'Day After Thanksgiving', '11/23/2012'),
(2012,'Christmas Eve', '12/24/2012'),
(2012,'Christmas Day', '12/25/2012'),
(2013, 'New Years Day', '01/01/2013'),
(2013,'Martin Luther King Day', '01/21/2013'),
(2013,'Presidents Day', '02/18/2013'),
(2013,'Good Friday', '03/29/2013'),
(2013,'Memorial Day', '05/27/2013'),
(2013,'Independence Day', '07/04/2013'),
(2013,'Day After Independence Day', '07/05/2013'),
(2013,'Labor Day', '09/02/2013'),
(2013,'Thanksgiving Day', '11/28/2013'),
(2013,'Day After Thanksgiving', '11/29/2013'),
(2013,'Christmas Eve', NULL),
(2013,'Christmas Day', '12/25/2013')
DECLARE @START_DATE DATETIME,
@END_DATE DATETIME,
@Days int
SELECT @START_DATE = DATEADD(MONTH, DATEDIFF(MONTH, 0, @date), 0)
SELECT @END_DATE = DATEADD(month, 1,@START_DATE)
;WITH CTE AS
(
SELECT DATEADD(DAY, number, (DATEADD(MONTH, DATEDIFF(MONTH, 0, @date), 0) )) CDate
FROM master.dbo.spt_values where type = 'p' and number between 0 and 365
EXCEPT
SELECT HolidayDate FROM @t WHERE HolidayYear = YEAR(@START_DATE)
)
SELECT @Days = COUNT(CDate) --, datepart(dw, CDate) WDay
FROM CTE
WHERE (CDate >=@START_DATE and CDate < @END_DATE) AND DATEPART(dw, CDate) NOT IN(1,7)
SELECT @Days
【讨论】:
以上是关于SQL 填充每月总工作日减去当前财政年度的银行假期的主要内容,如果未能解决你的问题,请参考以下文章