SQL 计算 20 个工作日,删除银行存款并引用另一个查询
Posted
技术标签:
【中文标题】SQL 计算 20 个工作日,删除银行存款并引用另一个查询【英文标题】:SQL Calculate 20 business days, remove bank hols and reference to another query 【发布时间】:2017-01-11 17:33:16 【问题描述】:抱歉,我对在 SQL Server 2008 R2 中创建函数很陌生。我基本上可以通过使用 T-SQL 语句来获得。
但是,我需要创建一个返回带有日期(程序开始日期)的记录的报告,这部分很简单,但是对于每个行日期,我想根据 20 个工作日计算目标完成日期。我也想避免计算银行假期。我有一个名为 dCalendar 的表,它在过去几年中每天都保存着标记,表明每一天是工作日还是银行假日。
我找到了很多关于如何计算两个日期之间的工作日数的资料,但这更棘手。
我已经创建了这个函数
ALTER function [warehouse].[MS_fnAddBusinessDays]
(@StartDate datetime,
@nDays int)
RETURNS TABLE
AS
RETURN
(SELECT calDt
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY calDt ASC) AS rownumber,
calDt
FROM
warehouse.dCalendar
WHERE
(calDt >= @StartDate)
AND (weekDayFg = 1)
AND (BankHolidayFg = 0)) AS Results
WHERE
(rownumber = @nDays)
并且可以使用以下方式调用它
SELECT *
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY calDt ASC) AS rownumber,
calDt, BankHolidayFg, weekDayFg, dateStr
FROM
warehouse.dCalendar
WHERE
(calDt >= CONVERT(DATETIME, '2016-12-11 00:00:00', 102))
AND (weekDayFg = 1) AND (BankHolidayFg = 0)) AS TblResults
WHERE
(rownumber = 20)
我只是无法弄清楚如何在以下示例中嵌入它,其中 progStartDate 是我想要计算目标日期的日期
SELECT
end_user.fContactVwDn.client_no,
end_user.fProgrammeVwDn.prtyProgrammeType,
end_user.fProgrammeVwDn.progStartDate,
SUM(CASE WHEN comtContactMeetingType = 'Visit' THEN 1 ELSE 0 END) AS InitialVisitTotal,
MAX(CASE WHEN comtContactMeetingType = 'Visit' THEN contPlannedFromDate ELSE 0 END) AS InitialVisitDate
FROM
end_user.fContactVwDn
INNER JOIN
warehouse.fContactProgramme ON end_user.fContactVwDn.contKy = warehouse.fContactProgramme.contKy
INNER JOIN
end_user.fProgrammeVwDn ON warehouse.fContactProgramme.progGuid = end_user.fProgrammeVwDn.progprogGuid
GROUP BY
end_user.fProgrammeVwDn.prtyProgrammeType,
end_user.fProgrammeVwDn.progStartDate, end_user.fContactVwDn.client_no
HAVING
(end_user.fProgrammeVwDn.prtyProgrammeType = 'Application')
AND (end_user.fProgrammeVwDn.progStartDate > CONVERT(DATETIME, '2016-07-01 00:00:00', 102))
任何帮助将不胜感激。
【问题讨论】:
"并且可以使用以下命令调用它" @nDays 参数? 【参考方案1】:该功能看起来不错。不过,我会添加TOP
,以避免扫描整个Calendar
表。我希望calDt
是主键。
ALTER function [warehouse].[MS_fnAddBusinessDays]
(@StartDate datetime,
@nDays int)
RETURNS TABLE
AS
RETURN
(
SELECT calDt
FROM
(
SELECT TOP(@nDays)
ROW_NUMBER() OVER (ORDER BY calDt ASC) AS rownumber,
calDt
FROM
warehouse.dCalendar
WHERE
(calDt >= @StartDate)
AND (weekDayFg = 1)
AND (BankHolidayFg = 0)
ORDER BY calDt ASC
) AS Results
WHERE
rownumber = @nDays
)
这个函数是内联表值函数,这意味着它返回一个表,而不是一个标量值。这很好,因为标量函数通常会使查询变慢,但内联(单语句)表值函数可以由优化器内联。
要调用此类函数,请使用CROSS APPLY
。它最初被引入到 SQL Server 专门用于调用表值函数,但它可以做much more(即所谓的横向连接)。
我将您的原始查询包装在 CTE 中,以使最终查询可读。
WITH
CTE
AS
(
SELECT
end_user.fContactVwDn.client_no,
end_user.fProgrammeVwDn.prtyProgrammeType,
end_user.fProgrammeVwDn.progStartDate,
SUM(CASE WHEN comtContactMeetingType = 'Visit' THEN 1 ELSE 0 END) AS InitialVisitTotal,
MAX(CASE WHEN comtContactMeetingType = 'Visit' THEN contPlannedFromDate ELSE 0 END) AS InitialVisitDate
FROM
end_user.fContactVwDn
INNER JOIN warehouse.fContactProgramme ON end_user.fContactVwDn.contKy = warehouse.fContactProgramme.contKy
INNER JOIN end_user.fProgrammeVwDn ON warehouse.fContactProgramme.progGuid = end_user.fProgrammeVwDn.progprogGuid
GROUP BY
end_user.fProgrammeVwDn.prtyProgrammeType,
end_user.fProgrammeVwDn.progStartDate,
end_user.fContactVwDn.client_no
HAVING
(end_user.fProgrammeVwDn.prtyProgrammeType = 'Application')
AND (end_user.fProgrammeVwDn.progStartDate > CONVERT(DATETIME, '2016-07-01 00:00:00', 102))
)
SELECT
CTE.client_no,
CTE.prtyProgrammeType,
CTE.progStartDate,
CTE.InitialVisitTotal,
CTE.InitialVisitDate,
F.calDt
FROM
CTE
CROSS APPLY [warehouse].[MS_fnAddBusinessDays](CTE.progStartDate, 20) AS F
;
【讨论】:
以上是关于SQL 计算 20 个工作日,删除银行存款并引用另一个查询的主要内容,如果未能解决你的问题,请参考以下文章