Вызов SQL-запроса - PullRequest
       9

Вызов SQL-запроса

9 голосов
/ 06 октября 2009

Итак, вот еще одна задача «написать запрос в X».

Я наблюдаю за несколькими сетевыми торговыми автоматами. Каждая машина имеет несколько частей, например, приемник банкнот, система монет, принтер и т. д.

Проблемы с деталями машины заносятся в таблицу, назовем ее «неисправностями», которая выглядит примерно так (ненужные поля опущены):

machineid           partid         start_time            end_time
---------           ------         ----------------      ----------------
       1                2          2009-10-05 09:00      NULL
       1                3          2009-10-05 08:00      2009-10-05 10:00
       2                2          2009-09-30 12:00      2009-09-30 14:00
       3                4          2009-09-28 13:00      2009-09-28 15:00
       3                2          2009-09-28 12:00      2009-09-28 14:00

end_date имеет значение NULL, если проблема продолжается в настоящее время.

Мне нужен запрос, который показывает периоды времени, в течение которых машина в целом не работает, и который может учитывать перекрывающиеся диапазоны, объединяя их в одну запись. Таким образом, для приведенных выше примерных данных будет получено:

machineid          start_time            end_time
---------          ----------------      ----------------
       1           2009-10-05 08:00      NULL
       2           2009-09-30 12:00      2009-09-30 14:00
       3           2009-09-28 12:00      2009-09-28 15:00

Нетрудно написать процедурный код, чтобы сделать эту строку построчно, но хороший декларативный SQL-запрос был бы более полезным, более элегантным. Кажется, это должно быть возможно, но я просто не могу туда добраться.

Диалект SQL - это Oracle. Аналитические функции доступны, если это поможет.

Спасибо!

Ответы [ 8 ]

7 голосов
/ 06 октября 2009

используя аналитику, вы можете создать запрос, который будет выполнять один проход данных (при большом наборе данных это будет наиболее эффективно):

SELECT machineid, MIN(start_time), MAX(end_time)
  FROM (SELECT machineid, start_time, end_time, 
               SUM(gap) over(PARTITION BY machineid 
                             ORDER BY start_time) contiguous_faults
           FROM (SELECT machineid, start_time, 
                        coalesce(end_time, DATE '9999-12-31') end_time,
                         CASE
                            WHEN start_time > MAX(coalesce(end_time, 
                                                           DATE '9999-12-31'))
                                              over(PARTITION BY machineid 
                                                   ORDER BY start_time 
                                                   ROWS BETWEEN UNBOUNDED PRECEDING
                                                            AND 1 preceding)
                            THEN 1
                         END gap
                    FROM faults))
 GROUP BY machineid, contiguous_faults
 ORDER BY 1, 2

Этот запрос начинается с определения, является ли строка смежной с какой-либо строкой, которая начиналась раньше. Затем мы группируем смежные строки.

2 голосов
/ 06 октября 2009

В принципе, вы не можете сделать это (найти покрывающий набор разделов леса) в чистой теории множеств (например, в виде ограниченного числа запросов без цикла).

Чтобы сделать это наиболее подходящим способом,

  1. Создание временной таблицы для разбиения леса (10 или 11 столбцов, 4 из ошибки # 1, 4 из ошибки # 2, 1 для идентификатора раздела, 1 для раунда, в который был вставлен узел, и 1 для ассортимента оптимизаций Я не могу думать с температурой 38 С.

  2. Запустите цикл (BFS или DFS, что бы вы ни нашли, чтобы легче реализовать алгоритм разбиения леса). Самое сложное, по сравнению с графиками, это то, что у вас может быть много поддеревьев, соединенных сверху вниз с текущим поддеревом

    Вы можете использовать запрос sheepsimulator в качестве основного строительного блока для цикла (например, найти 2 подключенных узла)

  3. Когда цикл разделения завершится, просто выполните

   select min(p1.start_time), max(p2.end_time), p1.partition,p2.partition
   from partitions p1, partitions p2
   where p1.partition = p2.partition
   group by p1.partition,p2.partition


    /* This will need to be tweaked using COALESCE 
       to deal with NULL end times in obvious way) */

Я прошу прощения за то, что не написал точный код для разбиения леса (он может быть сохранен при разбиении по дереву) - я очень устал, и я уверен, что какой-то поиск в Google даст один, теперь, когда вы знаете структуру tdata и имя проблемы или вы можете опубликовать это как более точно сформулированный Q в StackOverflow - например, «Как реализовать алгоритм полного разбиения леса деревьев в виде цикла в SQL».

2 голосов
/ 06 октября 2009
SELECT  DISTINCT 
        t1.machineId, 
        MIN(t2.start_time) start_time, 
        MAX(COALESCE(t2.end_time, '3210/01/01')) end_time
FROM FAULTS t1
JOIN FAULTS t2 ON t1.machineId = t2.machineId
                  AND ((t2.start_time >= t1.start_time
                       AND (t1.end_time IS NULL OR t2.start_time <= t1.end_time)
                  )
                  OR
                  (t1.start_time >= t2.start_time 
                       AND (t2.end_time IS NULL OR t1.start_time <= t2.end_time) 
                  ))
GROUP BY t1.machineId, t1.part_id

Я проверил этот запрос на следующих данных:

machine_id   |part_id |start_time           |end_time
-------------------------------------------------------------------------
1           |2       |05 Oct 2009 09:00:00  |NULL
1           |3       |05 Oct 2009 08:00:00  |05 Oct 2009 10:00:00
2           |2       |30 Sep 2009 12:00:00  |30 Sep 2009 14:00:00
2           |3       |30 Sep 2009 15:00:00  |30 Sep 2009 16:00:00
2           |4       |30 Sep 2009 16:00:00  |30 Sep 2009 17:00:00
3           |2       |28 Sep 2009 12:00:00  |28 Sep 2009 14:00:00
3           |4       |28 Sep 2009 13:00:00  |28 Sep 2009 15:00:00

Я получил это:

machine_id   |start_time             |end_time
-----------------------------------------------------------------
1           |05 Oct 2009 08:00:00   |01 Jan 3210 00:00:00
2           |30 Sep 2009 12:00:00   |30 Sep 2009 14:00:00
2           |30 Sep 2009 15:00:00   |30 Sep 2009 17:00:00
3           |28 Sep 2009 12:00:00   |28 Sep 2009 15:00:00
0 голосов
/ 06 октября 2009

Хех.

В SIRA_PRISE, который поддерживает интервальные типы, решение этой проблемы будет таким же простым, как

ВЫБЕРИТЕ ID машины, период ОТ НЕИСПРАВНОСТЕЙ.

В котором 'period' является атрибутом типа временного интервала, начальная и конечная точки которого являются start_time и end_time вашей таблицы SQL.

Но так как вы, вероятно, вынуждены решать эту проблему в SQL, и с системой, которая не поддерживает интервальные типы, я могу только пожелать вам большой смелости.

Два совета:

Объединение двух интервалов может быть обработано в SQL с использованием сложных конструкций CASE (если interval_values_overlap, затем lower_start_time самый высокий_ конец_все, все такого рода вещи).

Поскольку вы не можете заранее определить, сколько строк будет объединено в одну, вы, вероятно, будете вынуждены писать рекурсивный SQL.

0 голосов
/ 06 октября 2009
0 голосов
/ 06 октября 2009

Я полагаю, что для этого вам понадобится сохраненный процесс, или что-то вроде рекурсивных выражений общих таблиц (CTE) (как существует в SQL srever), или иначе (в одном операторе SQL) вы не сможете получить правильный ответ, когда 3 или более строк образуют непрерывный диапазон покрываемых дат.

как:

 |----------|
           |---------------|
                        |----------------|

На самом деле, не выполняя упражнение, я мог бы предложить, чтобы в хранимой процедуре была построена таблица всех «подходящих дат», а затем создана таблица, содержащая все даты, которые НЕ охватываются диапазоном дат в существующей строке затем создайте свой выходной набор результатов, "отрицая" этот набор.

0 голосов
/ 06 октября 2009

Хотелось бы, чтобы у меня было время дать полный ответ, но вот подсказка, чтобы найти перекрывающиеся простои:

select a.machineid, a.start_time, a.end_time, b.start_time, b.end_time
from faults a,
     faults b,
where a.machineid = b.machineid
  and b.start_time >= a.start_time
  and b.start_time <= a.end_time;
0 голосов
/ 06 октября 2009
SELECT machineid, min(start_time), max(ifnull(end_time, '3000-01-01 00:00'))
FROM faults
GROUP BY machineid

должен выполнить эту работу (при необходимости заменить ifnull эквивалентной функцией Oracle).

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