【问题标题】:How to get how many hour 4AM have there been between two dates in TSQL如何获取SQL中两个日期之间的凌晨4点有多少小时
【发布时间】:2019-10-18 09:07:11
【问题描述】:

我需要获取在 TSQL 中的两个日期之间发生了多少特定小时。一些例子:

以下将给出结果 = 1

declare @date1 datetime = '2019-10-01 00:00:00.000';
declare @date2 datetime = '2019-10-02 00:00:00.000';

以下将给出结果 = 0,因为中间有 0 个 4AM

declare @date1 datetime = '2019-10-01 05:00:00.000';
declare @date2 datetime = '2019-10-02 00:00:00.000';

以下将给出结果 = 2,因为中间有 2 个 4AM

declare @date1 datetime = '2019-10-01 03:00:00.000';
declare @date2 datetime = '2019-10-02 05:00:00.000';

以下将给出结果 = 2,因为已经有 2 个凌晨 4 点,即使它们只是在凌晨 4:00 时间

declare @date1 datetime = '2019-10-01 04:00:00.000';
declare @date2 datetime = '2019-10-02 04:00:00.000';

我尝试过这样的事情......但它给出的答案不正确

DECLARE @startdate AS DATETIME = '2019-10-01 03:00:00.000'
DECLARE @enddate   AS DATETIME = '2019-10-02 00:00:00.000'
DECLARE @hour int = 4

SELECT DATEDIFF(HOUR, @startdate, @endDate) / 24
     + 1 
     + CASE WHEN DATEPART(HOUR, @startdate) <= @hour AND 
                 DATEPART(HOUR, @endDate) >= @hour
            THEN 0
            ELSE -1
       END

谁能给我一些启发,我将不胜感激

【问题讨论】:

  • 为什么是4 AM?那是时区偏移吗?在这种情况下,您不应该存储值datetimeoffset,或者至少在计算之前将它们转换为正确的偏移量吗?

标签: sql sql-server tsql datetime


【解决方案1】:

最简单的方法是从两个值中删除 4 小时,然后得到天数的差异:

DECLARE @date1 datetime = '2019-10-01 00:00:00.000';
DECLARE @date2 datetime = '2019-10-02 00:00:00.000';

SELECT DATEDIFF(DAY,DATEADD(HOUR, -4, @date1),DATEADD(HOUR, -4, @date2)); --RETURNS 31, as there are 31 days bewteen 10 Jan and 10 Feb
GO

DECLARE @date1 datetime = '2019-10-01T04:30:00.000';
DECLARE @date2 datetime = '2019-10-02T03:59:00.000';

SELECT DATEDIFF(DAY,DATEADD(HOUR, -4, @date1),DATEADD(HOUR, -4, @date2)); --RETURNS 0, as 04:00 never got to

GO

DECLARE @date1 datetime = '2019-10-01T03:30:00.000';
DECLARE @date2 datetime = '2019-10-03T04:30:00.000';

SELECT DATEDIFF(DAY,DATEADD(HOUR, -4, @date1),DATEADD(HOUR, -4, @date2)); --RETURNS 3, as 04:00 occurs on 01, 02 and 03 of October

似乎 OP 也想计算 04:00 的事件。因此,我从开始时间再删除一秒:

DECLARE @date1 datetime = '2019-10-01T04:00:00.000';
DECLARE @date2 datetime = '2019-10-02T04:00:00.000';

SELECT DATEDIFF(DAY,DATEADD(SECOND,-1,DATEADD(HOUR, -4, @date1)),DATEADD(HOUR, -4, @date2)); --returns 2

DECLARE @date1 datetime = '2019-10-01T04:00:00.000';
DECLARE @date2 datetime = '2019-10-01T04:00:01.000';

SELECT DATEDIFF(DAY,DATEADD(SECOND,-1,DATEADD(HOUR, -4, @date1)),DATEADD(HOUR, -4, @date2)); --Returns 1

如果您实际存储的值精确到 1/300 秒,则不要使用 1 秒,而是使用 3 毫秒,以确保最大精度。

【讨论】:

  • 非常感谢,我测试一下
  • DECLARE @date1 datetime = '2019-10-01 04:00:00.000'; DECLARE @date2 datetime = '2019-10-02 04:00:00.000'; 返回 1 而我想要 2
  • @Larnu 4AM 非常有规律。我怀疑 OP 想要计算特定时区的差异,在这种情况下,更好的解决方案是转换为该时区而不是减去 - 或在整个过程中使用 datetimeoffset。
  • 可能是 OP 故意使用 UTC 时间,@PanagiotisKanavos。这也是处理时区数据时的标准做法。
  • 即便如此,更改时区实际上并不会改变计算“04:00”出现次数的解决方案。 DATEDIFF 计算滴答声,而不是特定时间的出现次数(比如“00:00”)。
【解决方案2】:

如果两个日期相隔n 小时,则它们之间有FLOOR(n / 24) 4AM;如果凌晨 4 点介于开始日期和结束日期之间,再加上一个(检查 04:00 是否在 03:00 - 05:00 之间很容易,检查 04:00 是否在 05:00 - 03:00 之间很难):

SELECT date1
     , date2
     , DATEDIFF(HOUR, date1, date2) / 24 + CASE
           WHEN H1 <= H2 AND (H1 <= 4 AND 4 <= H2) OR H1 >  H2 AND (H1 <= 4 OR  4 <= H2) THEN 1
           ELSE 0
       END AS result
FROM tests
CROSS APPLY (SELECT DATEPART(HOUR, date1), DATEPART(HOUR, date2)) AS x(h1, h2)

Demo on db<>fiddle

【讨论】:

    【解决方案3】:
    DECLARE @date1 datetime = '2019-10-01 04:00:00.000';
    DECLARE @date2 datetime = '2019-10-02 04:00:00.000';
    
    DECLARE @Hr INT=4
    DECLARE @AMorPM CHAR(2)='PM'
    
    ;WITH CTE AS
    (
        SELECT @date1 AS DateStart
        UNION ALL
        SELECT DATEADD(HOUR,1,DateStart)
        FROM CTE 
        WHERE DateStart < @date2
    )
    SELECT COUNT(CASE WHEN DateStart > 12 THEN DateStart-12 ELSE DateStart END) AS TotalTimes
    FROM CTE
    WHERE (
            (@AMorPM='AM' AND DATEPART(HOUR,DateStart)=@Hr) OR 
            (@AMorPM='PM' AND DATEPART(HOUR,DateStart)-12=@Hr)
          )
    

    【讨论】:

    • ; 是语句终止符,而不是“开始符”。它应该出现在您的所有语句的末尾,而不是 WITH/MERGE 子句的开头。
    • 这似乎也没有给出正确的结果。答案here 应该是2,而不是3
    • 这个新答案返回datetime?
    【解决方案4】:

    当值存储在另一个时区(可能是 UTC)时,这听起来像是试图计算一个时区中的天数。

    一种选择是使用TODATETIMEOFFSET将UTC日期转换为目标时区:

    SELECT datediff(d,todatetimeoffset(@date1,'+04:00'), todatetimeoffset(@date2,'+04:00'))
    

    这使得时区计算变得明确。尽管必须明确指定偏移量,但它不会处理日光变化。如果 DST 规则发生变化,则必须修改应用程序本身以更改偏移量。

    在 SQL Server 2016 及更高版本中,AT TIME ZONE 允许直接指定时区名称并应用为该时区指定的任何时区规则。

    select 
        datediff(d, @date1 at time zone 'Caucasus Standard Time',
                    @date2 at time zone 'Caucasus Standard Time')
    

    时区规则通过 Windows 更新分发,这意味着规则更改不再需要修改应用程序或数据库。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-24
      • 1970-01-01
      • 2011-01-29
      相关资源
      最近更新 更多