Объедините строки, когда время окончания одного является временем начала другого (Oracle) - PullRequest
5 голосов
/ 14 сентября 2011

Я просто не могу понять этот запрос.Мне нужно объединить строки состояний, последовательных во времени, в одно состояние.

Этот вопрос похож на вопрос, найденный здесь, за исключением того, что я работаю с Oracle 10, а не с SQL Server: Объединение строк при окончании времениодного - время начала другого

Пример данных:

name      start_inst         end_inst            code     subcode
Person1 9/12/2011 10:55 9/12/2011 11:49           161   50
Person1 9/12/2011 11:49 9/12/2011 11:55           107   28
Person1 9/12/2011 11:55 9/12/2011 12:07           161   50
Person1 9/12/2011 12:07 9/12/2011 12:26           161   50
Person1 9/12/2011 12:26 9/12/2011 12:57           161   71
Person1 9/12/2011 12:57 9/12/2011 13:07           161   71
Person1 9/12/2011 13:07 9/12/2011 13:20            52   50

И я хотел бы получить следующий вывод:

name       start_inst       end_inst            code     subcode
Person1 9/12/2011 10:55     9/12/2011 11:49     161     50
Person1 9/12/2011 11:49     9/12/2011 11:55     107     28
Person1 9/12/2011 11:55     9/12/2011 12:26     161     50
Person1 9/12/2011 12:26     9/12/2011 13:07     161     71
Person1 9/12/2011 13:07     9/12/2011 13:20     52      50

Вотпример SQL:

CREATE TABLE Data (
    name varchar2(132 BYTE) not null,
    start_inst DATE not null,
    end_inst DATE not null,    
code number(3) not null,
subcode number(3) not null
);
INSERT INTO Data(name,start_inst,end_inst, code, code2) VALUES('Person1','9/12/2011 10:55','9/12/2011 11:49',161, 50);
INSERT INTO Data(name,start_inst,end_inst, code, code2) VALUES('Person1','9/12/2011 11:49','9/12/2011 11:55',107,28);
INSERT INTO Data(name,start_inst,end_inst, code, code2) VALUES('Person1','9/12/2011 11:55','9/12/2011 12:07',161,50);
INSERT INTO Data(name,start_inst,end_inst, code, code2) VALUES('Person1','9/12/2011 12:07','9/12/2011 12:26',161,50);
INSERT INTO Data(name,start_inst,end_inst, code, code2) VALUES('Person1','9/12/2011 12:26','9/12/2011 12:57',161,71);
INSERT INTO Data(name,start_inst,end_inst, code, code2) VALUES('Person1','9/12/2011 12:57','9/12/2011 13:07',161,71);
INSERT INTO Data(name,start_inst,end_inst, code, code2) VALUES('Person1','9/12/2011 13:07','9/12/2011 13:20',52,50);

Заранее спасибо!

Ответы [ 5 ]

3 голосов
/ 15 сентября 2011

Вот решение, использующее рекурсивный запрос вместо аналитических функций (как предложено @wildplasser):

SELECT   name, code, subcode, MIN(start_inst) AS start_inst, MAX(end_inst) AS end_inst
FROM     (SELECT     name,
                     start_inst,
                     end_inst,
                     code,
                     subcode,
                     MIN(CONNECT_BY_ROOT (start_inst)) AS root_start
          FROM       data d
          CONNECT BY PRIOR name = name 
                 AND PRIOR end_inst = start_inst 
                 AND PRIOR code = code 
                 AND PRIOR subcode = subcode
          GROUP BY   name, start_inst, end_inst, code, subcode)
GROUP BY name, code, subcode, root_start;

Предложение connect by в самом внутреннем запросе приводит к тому, что данные возвращаются в иерархической иерархии.мода.connect_by_root дает нам значение в корне каждой ветви.Поскольку у нас нет подходящего кандидата для предложения start with, мы получим все дочерние строки (где end_inst равно start_inst другой строки, а все остальные столбцы одинаковы) несколько раз: один раз как корень иодин раз (или больше) как ветка.Взятие min корня устраняет эти дополнительные строки, давая нам значение для группировки во внешнем запросе.

Во внешнем запросе мы выполняем еще один group by для объединения строк.Разница в том, что в этом случае у нас также есть root_start, чтобы определить, какие строки являются последовательными и, следовательно, должны быть объединены.

3 голосов
/ 14 сентября 2011

Может быть, это? (У меня нет машины SQL для запуска)

WITH
  sequenced_data AS
(
  SELECT
    ROW_NUMBER() OVER (PARTITION BY name                ORDER BY start_inst) NameSequenceID,
    ROW_NUMBER() OVER (PARTITION BY name, code, subcode ORDER BY start_inst) NameStateSequenceID,
    *
  FROM
    data
)
SELECT
  name,
  MIN(start_inst) start_inst,
  MAX(end_inst)   end_inst,
  code,
  subcode
FROM
  sequenced_data
GROUP BY
  name,
  code,
  subcode,
  NameSequenceID - NameStateSequenceID
2 голосов
/ 15 сентября 2011

Вот еще один подход:

SELECT
    name,
    min(start_inst) AS start_inst,
    max(end_inst) AS end_inst,
    code,
    subcode
FROM
    (
        SELECT
            A.*,
            COUNT
            (
                CASE WHEN start_inst = previous_end_inst THEN NULL
                ELSE 1
                END
            )
            OVER
            (
                ORDER BY
                    start_inst,
                    name,
                    code,
                    subcode
            ) AS group_number
        FROM
            (
                SELECT
                    name,
                    start_inst,
                    end_inst,
                    LAG
                    (
                      end_inst
                    )
                    OVER
                    (
                        PARTITION BY
                            name,
                            code,
                            subcode
                        ORDER BY
                            start_inst
                    ) AS previous_end_inst,
                    code,
                    subcode
                FROM
                    data
            ) A
        ) B
GROUP BY
    name,
    code,
    subcode,
    group_number
ORDER BY
    group_number

В основном:

  1. Для каждой строки подзапрос A находит предыдущее время окончания для заданного имени, кода и субкода.

  2. Для каждой строки подзапрос B вычисляет «номер группы» - текущий счет предыдущих строк (в порядке start_inst, имени, кода и субкода), где предыдущий конецвремя, вычисленное на шаге 1, не равно времени начала.

  3. Внешний запрос агрегируется по номеру группы.

В лучшую или в худшую сторону,этот подход, в отличие от @ stevo, создаст новую «группу», если будет «разрыв» между временем окончания одной записи и временем начала следующей.Например, если вы хотите создать разрыв между 12:57 и 13:00 следующим образом ...

UPDATE data
SET start_inst = TO_DATE('9/12/2011 13:00', 'MM/DD/YYYY HH24:MI')
WHERE start_inst = TO_DATE('9/12/2011 12:57', 'MM/DD/YYYY HH24:MI');

... запрос выше вернет две строки, подобные этой ...

NAME                 START_INST       END_INST               CODE    SUBCODE
-------------------- ---------------- ---------------- ---------- ----------
.
.
.
Person1              09/12/2011 12:26 09/12/2011 12:57        161         71
Person1              09/12/2011 13:00 09/12/2011 13:07        161         71
.
.
.

... тогда как запрос @ stevo вернет одну строку, подобную этой ...

NAME                 START_INST       END_INST               CODE    SUBCODE
-------------------- ---------------- ---------------- ---------- ----------
.
.
.
Person1              12/09/2011 12:26 12/09/2011 13:07        161         71
.
.
.

Надеюсь, это поможет.

1 голос
/ 14 сентября 2011

адаптируя запрос desm, я думаю, что это должно работать

WITH
  sequenced_data AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY name                ORDER BY start_inst) NameSequenceID,
ROW_NUMBER() OVER (PARTITION BY name, code, subcode ORDER BY start_inst)     NameStateSequenceID,
d.*
FROM
data d
) 
SELECT
  name,
  to_char(MIN(start_inst),'DD/MM/YYYY HH24:MI') start_inst,
  to_char(MAX(end_inst),'DD/MM/YYYY HH24:MI')   end_inst,
  code,
  subcode
FROM
  sequenced_data
GROUP BY
  name,
  code,
  subcode,
  NameSequenceID - NameStateSequenceID
ORDER BY name,start_inst
0 голосов
/ 14 сентября 2011

Вы можете сделать это с помощью рекурсивного запроса (что-то с CONNECT BY / PRIOR в oracle, IIRC). Я сделал то же самое для Postgres в этой теме: Получить общий интервал времени из нескольких строк, если последовательность не нарушена *

Возможно, потребуется немного переработать, чтобы он вписался в синтаксис оракула. ​​

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...