Вопрос

Итак, вот еще одна задача «написать запрос к 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.Аналитические функции доступны, если это поможет.

Спасибо!

Это было полезно?

Решение

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

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

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

Другие советы

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

Чтобы сделать это самым простым способом,

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

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

    Вы можете использовать запрос овец-симулятора в качестве основного строительного блока для цикла (например,нахождение 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 и имя проблемы (или вы можете опубликуйте это как более точно сформулированный вопрос на StackOverflow - например.«Как реализовать алгоритм полного разделения леса деревьев в виде цикла в SQL».

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
SELECT machineid, min(start_time), max(ifnull(end_time, '3000-01-01 00:00'))
FROM faults
GROUP BY machineid

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

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

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;

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

нравиться:

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

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

Хе-хе.

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

ВЫБЕРИТЕ идентификатор машины, период ИЗ неисправностей.

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

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

Две подсказки:

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top