如何:计算工作日-纯SQL方法。

这篇博客探讨了如何使用纯SQL方法计算工作日,即在给定时间段内排除周末和节假日的天数。文章通过一系列查询展示了一个动态生成日历、排除特定国家的周末和节假日、并将这些因素应用到联系人表中的日期时段的流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

恕我直言,以下内容不是解决特定问题的方法指导,而是更多的概念验证性内容,展示了SQL的可能性。

因此,让我们说问题是要计算工作日计数,该工作日计数定义为不包括周末和节假日的天数(在当前实施中为可选值)。

假设要计算的期间存储在与联系人关联的表中。

[tblPeriods]

keyPeriodID-自动编号(长),PK

keyContactID-Long,FK(tblContacts)

dteStart-日期/时间

dteEnd-日期/时间

目标是接收包含顺序日期的数据集,该日期属于表中存储的日期时段,不包括周末和节假日。 然后,一个简单的分组查询将返回所需的结果。

步骤1.获取记录。

为了使所有日期都落入时段,我们需要将[tblPeriods]与包含所有顺序日期的数据集进行外部联接。

显然,使用存储所有日期的表不是一个明智的选择。 ;)

更好地动态生成它。

显然,日历日期不过是1-31天数字,1-12个月数字和合理的年数范围的所有可能组合。 当然,不存在的日期(例如12月30日,有时是12月29日)必须省略。

这给出了使用以下数据集的笛卡尔联接的想法:

[tblDays]

lngDay-Long,PK-自然数设置为1-31

[tblMonts]

lngMonth-Long,PK-自然数设置为1-12

[tblYears]

lngYear-Long,PK-自然数集... :) ...让我们说2000-2014

以下查询会将这些值组合到2000-2014年的固定日历中,使用DateSerial()函数的功能将不存在的日期排除在外,以防止在非法的argumnets中包装日期。

查询:[qryFlatCalendar]


SELECT DateSerial(tblYears.lngYear,tblMonths.lngMonth,tblDays.lngDay) AS dteDate,
tblYears.lngYear, tblMonths.lngMonth, tblDays.lngDay
FROM tblYears, tblMonths, tblDays
WHERE tblDays.lngDay=Day(DateSerial([tblYears].[lngYear],[tblMonths].[lngMonth],[tblDays].[lngDay])); 
然后,使用[tblPeriods]进行外部联接:

查询:[qryContactsPeriodsDays]


SELECT qryFlatCalendar.*, tblPeriods.keyPeriodID, tblPeriods.keyContactID, tblPeriods.dteStart, tblPeriods.dteEnd
FROM qryFlatCalendar LEFT JOIN tblPeriods ON (tblPeriods.dteStart<=qryFlatCalendar.dteDate) AND (tblPeriods.dteEnd>=qryFlatCalendar.dteDate)
WHERE Not tblPeriods.keyContactID Is Null; 
注意查询的ON子句。 在当前的实施中,期间的两端都包括在内。 如果考虑其他逻辑,则应在该位置实现它。

步骤2.不包括周末和节假日。

现在,我们将排除周末和节假日。

并立即出现两个问题-像往常一样一个很容易,而另一个则不容易:)

  • 周末放假时间取决于特定国家/地区。
  • 假期设置也取决于国家/地区。 问题在于,在某些情况下,每年的假期日期是相同的,在某些情况下,它是使用某些规则计算的,而这些规则并不总是很容易嵌入到关系数据库中。 因此,为简单起见,让我们存储该“不规则”假日的明确的完整日期,而“常规”假日不需要多个具有空值的记录。
显然,在计算营业日时应将排除的日期(周末和节假日)与特定国家/地区相关联。 以及联系人。

这需要几个表:

[tblCountries]

keyCountryID-自动编号(长),PK

txtCountry-文字

[tblCountryDaysOff]

keyContactDayOffID-自动编号(长),PK

keyCountryID-Long,FK(tblCountries)

lngContactDayOff-长

[tblCountryHolidays]

keyCountryHolidayID-自动编号(长),PK

keyCountryID-Long,FK(tblCountries)

txtHolidayName-文字

[tblContacts]

keyContactID-自动编号(长),PK

keyCountryID-Long,FK(tblCountries)

txtContactName-文字

*下表将假期与日期相关联,如果假期发生在每年的特定月份,则使用[lngYear] = Null的单个记录,否则必须创建每年的记录

[tblHolydayDates]

keyHolidayID-自动编号(长),PK

lngDay-长

lngMonth-长

lngYear-长

keyCountryHolidayID-Long,FK(tblCountryHolidays)

现在有两个预加入以获得关联-Contact / DaysOff,Contact / HolidayDate

查询:[qryContactsDaysOff]


SELECT tblContacts.*, tblCountryDaysOff.lngContactDayOff
FROM tblContacts INNER JOIN tblCountryDaysOff
ON tblContacts.keyCountryID = tblCountryDaysOff.keyCountryID; 
查询:[qryContactsHolidaysDates]

SELECT tblContacts.*, tblCountryHolidays.keyCountryHolidayID, 
tblCountryHolidays.txtHolidayName, tblHolydayDates.keyHolidayID, tblHolydayDates.lngDay, tblHolydayDates.lngMonth, tblHolydayDates.lngYear
FROM (tblContacts INNER JOIN tblCountryHolidays
ON tblContacts.keyCountryID = tblCountryHolidays.keyCountryID)
INNER JOIN tblHolydayDates
ON tblCountryHolidays.keyCountryHolidayID=tblHolydayDates.keyCountryHolidayID; 
首先,我们将通过以下外部联接来排除休息日:

查询:[qryContactsPeriodsDaysWODaysOff]


SELECT qryContactsPeriodsDays.*, qryContactsDaysOff.lngContactDayOff
FROM qryContactsPeriodsDays
LEFT JOIN qryContactsDaysOff
ON 
(qryContactsPeriodsDays.keyContactID=qryContactsDaysOff.keyContactID) AND (WeekDay(qryContactsPeriodsDays.dteDate)=qryContactsDaysOff.lngContactDayOff)
WHERE qryContactsDaysOff.lngContactDayOff Is Null; 
接下来,我们将排除假期:

查询:[qryContactsPeriodsBusinessDays]


SELECT qryContactsPeriodsDaysWODaysOff.keyPeriodID, 
qryContactsPeriodsDaysWODaysOff.keyContactID, qryContactsPeriodsDaysWODaysOff.dteDate, 
qryContactsHolidaysDates.txtHolidayName
FROM qryContactsPeriodsDaysWODaysOff
LEFT JOIN qryContactsHolidaysDates
ON 
(qryContactsPeriodsDaysWODaysOff.lngYear=qryContactsHolidaysDates.lngYear Or qryContactsHolidaysDates.lngYear Is Null)
AND (qryContactsPeriodsDaysWODaysOff.lngMonth=qryContactsHolidaysDates.lngMonth) AND 
(qryContactsPeriodsDaysWODaysOff.lngDay=qryContactsHolidaysDates.lngDay) AND 
(qryContactsPeriodsDaysWODaysOff.keyContactID=qryContactsHolidaysDates.keyContactID)
WHERE qryContactsHolidaysDates.keyCountryHolidayID Is Null; 

步骤3.计算天数。

现在甜点。

查询:[qryContactsPeroidsBusinessDaysCounts]


SELECT qryContactsPeriodsBusinessDays.keyPeriodID, 
qryContactsPeriodsBusinessDays.keyContactID,
Count(qryContactsPeriodsBusinessDays.dteDate) 
AS CountOfdteDate
FROM qryContactsPeriodsBusinessDays
GROUP BY qryContactsPeriodsBusinessDays.keyPeriodID, 
qryContactsPeriodsBusinessDays.keyContactID; 
好。 快完成了

但是,工作日计数为零的期间不会出现在结果列表中。

因此,让Uroboros咬住他的尾巴。

查询:[qryFinalJoin]


SELECT tblPeriods.*, Nz(qryContactsPeroidsBusinessDaysCounts.CountOfdteDate,0) AS 
lngBusinessDays
FROM tblPeriods
LEFT JOIN qryContactsPeroidsBusinessDaysCounts
ON tblPeriods.keyPeriodID=qryContactsPeroidsBusinessDaysCounts.keyPeriodID; 
所附数据库中的PS Holidays列表可能不完整,错误或不相关。 我没有尽力去做一个好人。 :D

PPS好。 今天没有样品,因为允许的最大大小为5k。 :D这是byte.com当前状态的另一个错误。

From: https://bytes.com/topic/access/insights/856583-howto-calculate-business-days-pure-sql-approach

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值