Ближайший сосед на момент времени

Скажем, у меня есть две таблицы (SQL Fiddle). Один, который записал значения в различные временные метки, другой указывает идентификаторы и даты и время для выборки для ближайших значений. Используя что-то похожее на ПРЕФЕРЕНЦИЮ ПОСЛЕДНЕЙ СВЯЗИ КЕВИНА МИДА Кевина Мида (но в SQL Server 2008), я хочу найти (ненулевое значение) указанного идентификатора ближе к целевой дате (переписи), но не после даты (переписи). Если есть строка, соответствующая дате переписи, используйте ее (если она не имеет нулевого значения). Если нет строки, предшествующей дате переписи, найдите строку, ближайшую к дате переписи, но не раньше нее, и используйте ее.

Первая таблица:

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');

Вот код Oracle мистера Мида для чего-то похожего в одной таблице для данного идентификатора:

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

@ Гордон Линофф дал мне идею использовать абсолютные значения разницы дат между временем переписи и записанным временем. Это привело меня к изменению решения @bobs здесь.

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

1 ответ

Вы можете сделать это как коррелированный подзапрос - как в Oracle, так и в SQL Server, потому что это почти стандартный 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 заявление, и это будет выглядеть сложнее.

Другие вопросы по тегам