T-SQL 不同的截止日期

T-SQL Distinct Close Dates

使用 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

这很容易使用一些 date/conversion 函数来删除 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 值组合在一起?

为我的目的找到答案:

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

加入 table 本身只会带回不是下一秒内时间的另一行的行。

决定你想要trim微秒的方式,然后按选择函数分组

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

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 服务器 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;

最简单直接的方法是使用日期时间秒近似值的 Distinct,如下所示:

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

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