【问题标题】:T-SQL Distinct Close DatesT-SQL 不同的结束日期
【发布时间】:2016-05-07 10:58:56
【问题描述】:

使用 SQL Server 2005 我使用 UNION 从一组两个表中选择 DATETIME。其中许多非常接近:例如:

2016-01-29 10:28:57.540
2016-01-29 10:28:57.647
2016-01-29 11:23:18.193
2016-01-29 11:23:18.240

在这个例子中,我只想返回

2016-01-29 10:28:57.000  
2016-01-29 11:23:18.000

这很容易使用一些日期/转换函数来删除 ms 部分。但是,如果我们得到以下信息:

2016-01-29 10:18:58.105
2016-01-29 10:18:57.952
2016-01-29 11:13:18.193
2016-01-29 11:13:18.240

当我只想要 2 个时,我会得到 3 个日期时间:

2016-01-29 10:18:58.000
2016-01-29 10:18:57.000
2016-01-29 11:13:18.000

代替:

2016-01-29 10:18:58.000
2016-01-29 11:13:18.000

因为 2016-01-29 10:18:58.105 和 2016-01-29 10:18:57.952 相差不到一秒。

所以问题是如何将彼此相隔一秒的 DATETIME 值组合在一起?

【问题讨论】:

  • 所以你可以分成两个以上的块,对吧?
  • 正确。 1000ms 内的任意时间(即使它们有不同的第二分量也应该分组)
  • 还有什么,比如说,三个连续的日期,间隔为 999 毫秒。中间的应该属于哪一组?

标签: sql-server tsql sql-server-2005


【解决方案1】:

MS SQL 2005 更新

DECLARE @TestDate AS TABLE (
    dt DATETIME NOT NULL
)

INSERT INTO @TestDate (dt)
VALUES 
('2016-01-29 10:18:58.105')
,('2016-01-29 10:18:57.952')
,('2016-01-29 11:13:18.193')
,('2016-01-29 11:13:18.240')


;WITH IdDt AS (
    SELECT Id = ROW_NUMBER() OVER (ORDER BY dt)
            ,dt
    FROM @TestDate)

SELECt IdDt.dt
FROM IdDt
    LEFT JOIN IdDt LagDt ON IdDt.ID = LagDt.ID + 1
WHERE CASE WHEN DATEDIFF(MILLISECOND, LagDt.dt, IdDt.dt) < 1000 Then 0 ELSE 1 END = 1

MS SQL Server 2012 +

MSDN Analytic function LAG()

DECLARE @TestDate AS TABLE (
    dt DATETIME NOT NULL
)

INSERT INTO @TestDate (dt)
VALUES 
('2016-01-29 10:18:58.105')
,('2016-01-29 10:18:57.952')
,('2016-01-29 11:13:18.193')
,('2016-01-29 11:13:18.240')

SELECT dt
FROM (
    SELECT dt, CASE WHEN DATEDIFF(MILLISECOND, LAG(dt) OVER (ORDER BY dt), dt) < 1000 Then 0 ELSE 1 END ToRemove
    FROM @TestDate
    ) Filter
WHERE ToRemove = 1;

【讨论】:

  • 与我自己的回答类似。在 SQL 2005 示例中,使用 CASE WHEN 是否有优势?
【解决方案2】:

决定如何修剪微秒,然后按所选函数分组

select
    DT,
    -- Round down to nearest second
    DT_Floor_MS =
        dateadd(ms,-datepart(ms,a.DT),a.DT),
    -- Round up to nearest second
    DT_Ceiling_MS = 
        dateadd(ms,(1000-datepart(ms,a.DT))%1000,a.DT),
    -- Round to nearest second
    DT_Round_Off_MS =
        dateadd(ms,500-((datepart(ms,a.DT)+500)%1000),a.DT)
from
    (select --test data
        '2016-01-29 10:28:57.540' DT union all select
        '2016-01-29 10:28:57.647'union all select
        '2016-01-29 11:23:18.193'union all select
        '2016-01-29 11:23:18.240' ) a

DT	                DT_Floor_MS	                DT_Ceiling_MS	                DT_Round_Off_MS
2016-01-29 10:28:57.540	January, 29 2016 10:28:57	January, 29 2016 10:28:58	January, 29 2016 10:28:58
2016-01-29 10:28:57.647	January, 29 2016 10:28:57	January, 29 2016 10:28:58	January, 29 2016 10:28:58
2016-01-29 11:23:18.193	January, 29 2016 11:23:18	January, 29 2016 11:23:19	January, 29 2016 11:23:18
2016-01-29 11:23:18.240	January, 29 2016 11:23:18	January, 29 2016 11:23:19	January, 29 2016 11:23:18
select 

        dateadd(ms,(1000-datepart(ms,a.DT))%1000,a.DT)
    -- Round to nearest second      
        from
    (select --test data
        '2016-01-29 10:28:57.540' DT union all select
        '2016-01-29 10:28:57.647'union all select
        '2016-01-29 11:23:18.193'union all select
        '2016-01-29 11:23:18.240' ) a

    group by

        dateadd(ms,(1000-datepart(ms,a.DT))%1000,a.DT)
    -- Round to nearest second  

2016-01-29 10:28:58.000
2016-01-29 11:23:19.000

【讨论】:

  • 这并不能解决两个时间非常接近但第二分量不同的问题:例如:10:18:57.205 和 10:18:58.105 将导致两个日期时间为 10:18: 57 和 10:18:58,因此尽管彼此相距不到一秒钟,但不会被分组。我使用连接找到了适合我需要的答案。看我的回答。谢谢
  • @apc 哦,我误会了你的要求,抱歉。
【解决方案3】:

为我的目的找到了答案:

SELECT t1.UpdatedDateTime 
FROM MyTable t1 
LEFT JOIN MyTable t2 ON t1.UpdatedDateTime > t2.UpdatedDateTime AND t1.UpdatedDateTime < DATEADD(SECOND,1,t2.UpdatedDateTime)
WHERE t2.UpdatedDateTime IS NULL

加入表本身只会带回在下一秒内不是另一行的行。

【讨论】:

    【解决方案4】:

    最简单直接的方法是使用 Distinct of the datetime second approximation 如下:

    SELECT DISTINCT DATEADD(MILLISECOND, 500 - DATEPART(MILLISECOND, TimeField + '00:00:00.500'),TimeField)  FROM T1
    

    演示: http://sqlfiddle.com/#!3/b208c/3

    【讨论】:

    • 这并不能解决两个时间非常接近但第二个组件不同的问题:例如:10:18:57.205 和 10:18:58.105 将导致两个日期时间为 10:18: 57 和 10:18:58,因此尽管彼此相距不到一秒钟,但不会被分组。我使用连接找到了适合我需要的答案。看我的回答。谢谢
    • @apc 现在已修复,请尝试演示 这个答案现在是最优化和最简单的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-13
    相关资源
    最近更新 更多