这比看起来更难。我需要一个函数来计算日期范围内给定工作日的数字。我不想要任何循环或递归 SQL。有数百万个例子就是这样做的。我需要一个快速的计算函数。

函数的输入将是工作日、从数据、到日期

-- counting fridays
set datefirst 1
SELECT dbo.f_countweekdays(5, '2011-07-01', '2011-07-31'),
dbo.f_countweekdays(5, '2011-07-08', '2011-07-15'),
dbo.f_countweekdays(5, '2011-07-09', '2011-07-15'),
dbo.f_countweekdays(5, '2011-07-09', '2011-07-14')

预期结果:
5, 2, 1, 0

最佳答案

@Mikael Eriksson 有一个绝妙的主意,但他的实现似乎有点过于复杂。

这是我想出的(我想强调的是 它基于 solution by @Mikael ,主要功劳应该归于谁):

ALTER FUNCTION dbo.f_countweekdays (@Dow int, @StartDate datetime, @EndDate datetime)
RETURNS int
AS BEGIN
  RETURN (
    SELECT
      DATEDIFF(wk, @StartDate, @EndDate)
      - CASE WHEN DATEPART(dw, @StartDate) > @Dow THEN 1 ELSE 0 END
      - CASE WHEN DATEPART(dw, @EndDate)   < @Dow THEN 1 ELSE 0 END
      + 1
  )
END

更新

正如 Mikael 在他的回答的评论线程中正确指出的那样,为了使上述解决方案正常工作,必须将 DATEFIRST 设置设置为 7(星期日)。虽然我找不到这个文档,但快速测试显示 DATEDIFF(wk) 忽略了实际的 DATEFIRST 设置并且确实返回了几周的差异,就好像 DATEFIRST 总是设置为 7 一样。同时 DATEPART(dw) 确实尊重 DATEFIRST ,所以 DATEFIRST 设置为7 以外的值这两个函数返回相互不一致的结果。

因此,必须修改上述脚本,以便在计算 DATEDIFF(wk) 时考虑 DATEFIRST 设置的不同值。令人高兴的是,在我看来,修复似乎并没有使解决方案比以前复杂得多。不过,请自行判断:
ALTER FUNCTION dbo.f_countweekdays (@Dow int, @StartDate datetime, @EndDate datetime)
RETURNS int
AS BEGIN
  RETURN (
    SELECT
      DATEDIFF(wk, DATEADD(DAY, -@@DATEFIRST, @StartDate),
                   DATEADD(DAY, -@@DATEFIRST, @EndDate))
      - CASE WHEN DATEPART(dw, @StartDate) > @Dow THEN 1 ELSE 0 END
      - CASE WHEN DATEPART(dw, @EndDate)   < @Dow THEN 1 ELSE 0 END
      + 1
  )
END

编辑: 两个 -@@DATEFIRST % 7 条目都已简化为 -@@DATEFIRST ,正如有人建议的 here

10-08 02:01