sql - 时间点的最近邻

标签 sql sql-server-2008 datetime nearest-neighbor census

假设我有两个表 (SQL Fiddle) .一个记录了不同时间戳的值,另一个指示 ID 和日期时间以采样最接近的值。使用类似于 Kevin Meade 的 NEAREST NEIGHBOR PREFERENCE LOW 的东西(但在 SQL Server 2008 中),我想找到最接近目标(人口普查)日期但不在(人口普查)日期之后的指示 ID 的(非空值)。如果有一行与人口普查日期匹配,则使用该行(除非它具有空值)。如果没有在普查日期之前的行,则找到最接近普查日期但不早于普查日期的行并使用该行。

第一个表:

CREATE TABLE Recorded_Vent_Types
    ([PAT_ENC_CSN_ID] int, [RECORDED_TIME] datetime, [MEAS_VALUE] varchar(9));

INSERT INTO Recorded_Vent_Types
    ([PAT_ENC_CSN_ID], [RECORDED_TIME], [MEAS_VALUE])
VALUES
    (11117777,  '2013-06-08 19:36:00.000',  'SIMV/PRVC'),
    (11117777,  '2013-06-08 22:21:00.000',  'PRVC/AC'),
    (11117777,  '2013-06-09 00:10:00.000',  NULL),
    (11117777,  '2013-06-09 03:00:00.000',  'SIMV/PRVC'),
    (11117777,  '2013-06-09 23:59:00.000',  'SIMV/PRVC'),
    (11117777,  '2013-06-10 00:00:00.000',  'NAVA'),
    (11117777,  '2013-06-10 00:20:00.000',  'PS'),
    (11117777,  '2013-06-10 00:25:00.000',  NULL),
    (555999,    '2013-06-08 00:36:00.000',  NULL),
    (555999,    '2013-06-08 22:21:00.000',  'PRVC/AC'),
    (555999,    '2013-06-09 00:10:00.000',  'SIMV/PRVC'),
    (555999,    '2013-06-11 23:15:00.000',  'BIVENT'),
    (555999,    '2013-06-12 00:00:00.000',  NULL),
    (555999,    '2013-06-12 00:20:00.000',  'PS');

第二张表:

CREATE TABLE Census
    ([PAT_ENC_CSN_ID] int, [CENSUS_TIME] datetime);

INSERT INTO Census
    ([PAT_ENC_CSN_ID], [CENSUS_TIME])
VALUES
    (11117777, '2013-06-08 00:00:00'),
    (11117777, '2013-06-09 00:00:00'),
    (11117777, '2013-06-10 00:00:00'),
    (11117777, '2013-06-11 00:00:00'),
    (555999, '2013-06-08 00:00:00'),
    (555999, '2013-06-09 00:00:00'),
    (555999, '2013-06-11 00:00:00'),
    (555999, '2013-06-12 00:00:00');

这是 Meade 先生的 Oracle 代码,用于给定 ID 的一个表中的类似内容:

select *
from claim_history
where claim_id = 1
and status_date =
   (
    select min(status_date)
    from (
          select max(status_date) status_date
          from claim_history
          where claim_id = 1
          and status_date <= sysdate-3
          union all
          select min(status_date)
          from claim_history
          where claim_id = 1
          and status_date > sysdate-3
         )
   )
/

我的期望结果集:

PAT_ENC_CSN_ID    CENSUS_TIME           RECORDED_TIME               MEAS_VALUE
555999      June, 08 2013 00:00:00+0000 June, 08 2013 22:21:00+0000 PRVC/AC
555999      June, 09 2013 00:00:00+0000 June, 08 2013 22:21:00+0000 PRVC/AC
555999      June, 11 2013 00:00:00+0000 June, 09 2013 00:10:00+0000 SIMV/PRVC
555999      June, 12 2013 00:00:00+0000 June, 11 2013 23:15:00+0000 BIVENT
11117777    June, 08 2013 00:00:00+0000 June, 08 2013 19:36:00+0000 SIMV/PRVC
11117777    June, 09 2013 00:00:00+0000 June, 08 2013 22:21:00+0000 PRVC/AC
11117777    June, 10 2013 00:00:00+0000 June, 10 2013 00:00:00+0000 NAVA
11117777    June, 11 2013 00:00:00+0000 June, 10 2013 00:20:00+0000 PS

@Gordon Linoff 让我想到使用人口普查时间和记录时间之间的日期差异的绝对值。这导致我修改@bobs 解决方案 here .

SELECT * FROM
    (
    SELECT rvt.PAT_ENC_CSN_ID, CENSUS_TIME, RECORDED_TIME, MEAS_VALUE, ABS(DATEDIFF(s, c.CENSUS_TIME, RECORDED_TIME)) diff,
        ROW_NUMBER() OVER (PARTITION BY rvt.PAT_ENC_CSN_ID, c.CENSUS_TIME ORDER BY ABS(DATEDIFF(s, c.CENSUS_TIME, RECORDED_TIME))) AS SEQUENCE
    FROM Recorded_Vent_Types rvt join Census c on rvt.PAT_ENC_CSN_ID=c.PAT_ENC_CSN_ID
    WHERE MEAS_VALUE IS NOT NULL
    ) as m
WHERE SEQUENCE = 1
ORDER BY PAT_ENC_CSN_ID,CENSUS_TIME
;

但这会返回(绝对)最接近的记录时间,而不优先考虑普查时间之前的记录时间。 结果:

PAT_ENC_CSN_ID    CENSUS_TIME           RECORDED_TIME               MEAS_VALUE
555999      June, 08 2013 00:00:00+0000 June, 08 2013 22:21:00+0000 PRVC/AC
555999      June, 09 2013 00:00:00+0000 June, 09 2013 00:10:00+0000 SIMV/PRVC
555999      June, 11 2013 00:00:00+0000 June, 11 2013 23:15:00+0000 BIVENT
555999      June, 12 2013 00:00:00+0000 June, 12 2013 00:20:00+0000 PS
11117777    June, 08 2013 00:00:00+0000 June, 08 2013 19:36:00+0000 SIMV/PRVC
11117777    June, 09 2013 00:00:00+0000 June, 08 2013 22:21:00+0000 PRVC/AC
11117777    June, 10 2013 00:00:00+0000 June, 10 2013 00:00:00+0000 NAVA
11117777    June, 11 2013 00:00:00+0000 June, 10 2013 00:20:00+0000 PS

最佳答案

在 Oracle 和 SQL Server 中,您可以将此作为相关子查询来执行,因为除了前 1 个之外,这几乎是标准 SQL。

这里是查询:

select *,
       (select top 1 PAT_ENC_CSN_ID
        from census c
        where c.census_time <= rvt.recorded_time
        order by (case when c.census_time <= rvt.recorded_time then 1 else 0
                  end) desc,
                 (case when c.census_time <= rvt.recorded_time then c.census_time
                  end) desc,
                 c.census_time asc
       ) as nearestVal
from Recorded_Vent_Types rvt

子查询根据 order by 返回一行,这是查询的关键。它分为三个部分。

第一个将所有人口普查时间放在开头的记录时间之前。第二个按人口普查时间降序排列,第三个按时间升序排列其余部分。我想将最后两个替换为:

abs(c.census_time - rvt.recorded_time)

因为这在逻辑上就是它所做的。唉,这行不通,因为 abs()datetime 不起作用。然后我必须使用 datediff() 函数或 case 语句,它会开始看起来更复杂。

关于sql - 时间点的最近邻,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17033050/

相关文章:

c# - 使用linq to sql select的死锁

mysql - 关于日期时间类型值的问题

sql - 如何使用 CTE 将一组行和 xml 生成的列插入到单个查询中的表中

sql - 为什么窗口聚合函数的逻辑读取如此之高?

r - 根据空值在 RSQLite 中切碎的日期时间值

android - Kotlin - SimpleDateFormat 解析需要无限时间

MySQL - 在 CASE 中组合多个 WHEN 条件

mysql - 为多对多关系建模的正确方法

SQL 2008 FILESTREAM - 如何在更改数据库之前检查数据库是否已启用文件流

php - 如何显示指定日期之前的时间或自指定日期以来的时间javascript或php