【问题标题】:SQL Server function to get count of night hours between two datesSQL Server 函数获取两个日期之间的夜间小时数
【发布时间】:2021-08-31 11:20:32
【问题描述】:

我必须计算两个日期之间的夜间时间。 SQL 服务器:

我有以下情况:

@date1=2021-06-01 05:00:00.000
@date2=2021-06-03 05:00:00.000

我需要获取夜间时间,介于 22:00:00 和 06:00:00 之间

对于@date1 和@date2,我需要得到结果:16 小时。

2021-06-01 05:00:00.000 to 2021-06-01 06:00:00.000 - 1.0 hour
2021-06-01 22:00:00.000 to 2021-06-02 06:00:00.000 - 8.0 hour
2021-06-02 22:00:00.000 to 2021-06-03 05:00:00.000 - 7.0 hour

示例 2:

@date1=2021-06-22 04:55:00.000
@date2=2021-06-22 16:45:00.000

我需要得到以下结果:1.083333 小时

结果最多应为 6 位小数。 不会超过 4 天。

请给我什么建议?

谢谢!

【问题讨论】:

  • 一整天总是 8 小时。您只需要调整第一天和最后一天。

标签: sql-server


【解决方案1】:
  1. 将两个日期之间的范围变成一个集合。
  2. 提取每个小时的开始和结束的 DATEPART(HOUR)。
  3. 仅在结束 = 22 时计算小时

这是我最终得到的查询,假设您真的不会超出 4 天(96 小时)的范围。如果您的范围超过 100 小时,您将需要 MAXRECURSION 提示:

/*  basic test harness - just change @test to change the range to use  */
DECLARE @test int = 1, @d1 datetime, @d2 datetime, @lb datetime;

SELECT @d1 = d1, @d2 = d2, @lb = SMALLDATETIMEFROMPARTS(
      YEAR(d1),MONTH(d1),DAY(d1),DATEPART(HOUR,d1),0) 
  FROM (SELECT * FROM (VALUES
  (1, '2021-06-01T05:00:00.000', '2021-06-03T05:00:00.000'),
  (2, '2021-06-22T04:55:00.000', '2021-06-22T16:45:00.000'),
  (3, '2021-06-01T10:50:00.000', '2021-06-01T22:50:00.000'))
  AS t(t,d1,d2)) AS t WHERE t = @test;

;WITH n(d) AS     (
  SELECT DATEADD(HOUR, 1, @lb) UNION ALL 
  SELECT DATEADD(HOUR, 1, d) FROM n WHERE d < @d2
), 
d(d) AS     (
  SELECT d FROM n UNION ALL SELECT d FROM (VALUES(@d1),(@d2)) AS v(d)
), 
seg(s, e, starting_hour, ending_hour, diff_seconds) AS     (
  SELECT LAG(d,1) OVER (ORDER BY d), d, 
      DATEPART(HOUR, LAG(d,1) OVER (ORDER BY d)), 
      DATEPART(HOUR, d), DATEDIFF(SECOND, LAG(d,1) OVER (ORDER BY d), d) 
  FROM d
),     src AS 
(
  SELECT *, count_it = CASE 
    WHEN (ending_hour <= 6 OR starting_hour >= 22) AND e <= @d2
        THEN COALESCE(diff_seconds,0)/60/60.0 ELSE 0 END 
  FROM seg
)
SELECT CONVERT(decimal(12,6), SUM(count_it)) FROM src;

@test = 1 (fiddle)、@test = 2 (fiddle)、@test = 3 (fiddle) 的结果:

        1               2               3    
---------       ---------       ---------
16.000000        1.083333        0.833333

如果你想避免递归,你可以创建一个函数来生成一系列数字(一些很棒的背景信息in this series);我为 SQL 2016+ 选择了一个简单的:

CREATE FUNCTION dbo.GetNums_Split(@low bigint,@high bigint)
RETURNS table AS RETURN
  WITH spaces AS (SELECT REPLICATE(CONVERT(varchar(max),' '),
          CONVERT(integer, CEILING(SQRT(CONVERT(float, 
          IIF(@high >= @low, @high - @low + 1, 0)))))) AS spc),
       splt AS (SELECT CONVERT(bit,NULL) b FROM spaces 
          CROSS APPLY STRING_SPLIT(spaces.spc,' ')),
       Nums AS (SELECT ROW_NUMBER() OVER(ORDER BY @@SPID) AS rownum 
          FROM splt AS A, splt AS B)
    SELECT TOP(@high - @low + 1) rownum AS rn, 
        @high + 1 - rownum AS op,
        @low - 1 + rownum AS n
    FROM Nums ORDER BY rownum;
GO

然后你可以用这个替换第一个 CTE:

;WITH n(d) AS     (
  SELECT DATEADD(HOUR, n, @lb) 
    FROM dbo.GetNums_Split(1,DATEDIFF(HOUR, @lb, @d2)-1)
),

【讨论】:

  • 一切都很好,非常感谢您的解决方案。
猜你喜欢
  • 2011-01-29
  • 2011-08-29
  • 2021-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-17
  • 2014-06-11
相关资源
最近更新 更多