sql - T-SQL获取2个日期之间的工作日数

标签 sql sql-server tsql

我想计算两个给定日期之间的工作日数。例如,如果我想计算 2013-01-10 和 2013-01-15 之间的工作日,结果必须是 3 个工作日(我不考虑该间隔的最后一天,我减去星期六和星期日)。我有以下代码,适用于大多数情况,除了我的示例中的情况。

  SELECT (DATEDIFF(day, '2013-01-10', '2013-01-15')) 
    - (CASE WHEN DATENAME(weekday, '2013-01-10') = 'Sunday' THEN 1 ELSE 0 END)
    - (CASE WHEN DATENAME(weekday, DATEADD(day, -1, '2013-01-15')) = 'Saturday' THEN 1 ELSE 0 END)

我怎样才能做到这一点?我必须把所有的日子都过一遍并检查它们吗?或者有没有一种简单的方法可以做到这一点。

最佳答案

请,请,请使用日历表。 SQL Server 不知道任何关于国家假日、公司事件、自然灾害等的信息。日历表相当容易构建,占用的空间极小,并且如果引用足够多,就会在内存中。

下面是一个创建包含 30 年日期(2000 -> 2029)的日历表的示例,但仅需要 200 KB 的磁盘空间(如果使用页面压缩,则为 136 KB)。这几乎可以保证小于在运行时处理某些 CTE 或其他集所需的内存授予。

CREATE TABLE dbo.Calendar
(
  dt DATE PRIMARY KEY, -- use SMALLDATETIME if < SQL Server 2008
  IsWorkDay BIT
);

DECLARE @s DATE, @e DATE;
SELECT @s = '2000-01-01' , @e = '2029-12-31';

INSERT dbo.Calendar(dt, IsWorkDay)
  SELECT DATEADD(DAY, n-1, '2000-01-01'), 1 
  FROM
  (
    SELECT TOP (DATEDIFF(DAY, @s, @e)+1) ROW_NUMBER() 
      OVER (ORDER BY s1.[object_id])
      FROM sys.all_objects AS s1
      CROSS JOIN sys.all_objects AS s2
  ) AS x(n);

SET DATEFIRST 1;

-- weekends
UPDATE dbo.Calendar SET IsWorkDay = 0 
  WHERE DATEPART(WEEKDAY, dt) IN (6,7);

-- Christmas
UPDATE dbo.Calendar SET IsWorkDay = 0 
  WHERE MONTH(dt) = 12
  AND DAY(dt) = 25
  AND IsWorkDay = 1;

-- continue with other holidays, known company events, etc.

现在您要编写的查询非常简单:

SELECT COUNT(*) FROM dbo.Calendar
  WHERE dt >= '20130110'
    AND dt <  '20130115'
    AND IsWorkDay = 1;

有关日历表的更多信息:

http://web.archive.org/web/20070611150639/http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-calendar-table.html

有关无循环生成集的更多信息:

http://www.sqlperformance.com/tag/date-ranges

还要注意一些小事情,例如依赖 DATENAME 的英文输出。我见过几个应用程序因某些用户的语言设置不同而崩溃,如果您依赖 WEEKDAY,请确保正确设置 DATEFIRST 设置...

关于sql - T-SQL获取2个日期之间的工作日数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14749877/

相关文章:

sql - 在 SELECT 查询中使用 TOP 1 的性能影响

mysql - 选择时间戳 x 和时间戳 y 之间的数据,返回为 z 行数(缩小结果集)

mysql - 合并唯一键包含多列的两个表

SQL Server : replace year of datetime with current year in Select

sql-server - T-SQL CASE 检查最年轻的日期,然后对照其他值

mysql - 将唯一值从datagridview插入到mysql数据库表

c# - 测试 SQL 连接字符串可用性的最有效方法

mysql - 新的 SQL 列,在创建新行时具有重复值?

sql - 组合 AND OR 结果

sql-server - SQL Server 2005 命名实例端口问题