2015-08-09 3 views
2

У меня есть данные, поступающие из источника в моей базе данных Oracle.Выберите записи всего в течение 10 минут друг от друга

Если конкретный Office_ID был деактивирован, и у него есть все три клиента (A, B, C) в течение определенного дня, тогда мы должны проверить, все ли ушли клиенты. Если да, то нам нужно проверить, соответствует ли срок для всех клиентов в течение 10 минут.

Если это повторяется три раза в день для конкретного офиса, мы объявляем офис закрытым.

Вот некоторые примерные данные:

+-----------+-----------+--------------+--------+ 
| OFFICE_ID | FAIL_TIME | ACTIVITY_DAY | CLIENT | 
| 1002  | 5:39:00 | 23/01/2015 | A  | 
| 1002  | 17:49:00 | 23/12/2014 | A  | 
| 1002  | 18:41:57 | 1/5/2014  | B  | 
| 1002  | 10:32:00 | 1/7/2014  | A  | 
| 1002  | 10:34:23 | 1/7/2014  | B  | 
| 1002  | 10:35:03 | 1/7/2014  | C  | 
| 1002  | 12:08:52 | 1/7/2014  | B  | 
| 1002  | 12:09:00 | 1/7/2014  | A  | 
| 1002  | 12:26:10 | 1/7/2014  | B  | 
| 1002  | 13:31:32 | 1/7/2014  | B  | 
| 1002  | 15:24:06 | 1/7/2014  | B  | 
| 1002  | 15:55:06 | 1/7/2014  | C  | 
+-----------+-----------+--------------+--------+ 

Результат должен быть таким:

1002 10:32:00  A 
1002 10:34:23  B 
1002 10:35:03  C 

Любая помощь будет оценена. Я ищу SQL-запрос или PL/SQL-процедуру.

+0

Что тип данных FAIL_TIME? – APC

ответ

1

Раствор с использованием COUNTanalytic function с RANGE BETWEEN INTERVAL '10' MINUTE PRECEDING AND INTERVAL '10' MINUTE FOLLOWING, что позволяет избежать Самосоединения:

SQL Fiddle

Oracle 11g R2 Настройка схемы:

CREATE TABLE Test (OFFICE_ID, FAIL_TIME, ACTIVITY_DAY, CLIENT) AS 
      SELECT 1002, '5:39:00', '23/01/2015', 'A' FROM DUAL 
UNION ALL SELECT 1002, '17:49:00', '23/12/2014', 'A' FROM DUAL 
UNION ALL SELECT 1002, '18:41:57', '1/5/2014', 'B' FROM DUAL 
UNION ALL SELECT 1002, '10:32:00', '1/7/2014', 'A' FROM DUAL 
UNION ALL SELECT 1002, '10:34:23', '1/7/2014', 'B' FROM DUAL 
UNION ALL SELECT 1002, '10:35:03', '1/7/2014', 'C' FROM DUAL 
UNION ALL SELECT 1002, '12:08:52', '1/7/2014', 'B' FROM DUAL 
UNION ALL SELECT 1002, '12:09:00', '1/7/2014', 'A' FROM DUAL 
UNION ALL SELECT 1002, '12:26:10', '1/7/2014', 'B' FROM DUAL 
UNION ALL SELECT 1002, '13:31:32', '1/7/2014', 'B' FROM DUAL 
UNION ALL SELECT 1002, '15:24:06', '1/7/2014', 'B' FROM DUAL 
UNION ALL SELECT 1002, '15:55:06', '1/7/2014', 'C' FROM DUAL 

Запрос 1:

WITH Times AS (
    SELECT OFFICE_ID, 
     TO_DATE(ACTIVITY_DAY || ' ' || FAIL_TIME, 'DD/MM/YYYY HH24/MI/SS') AS FAIL_DATETIME, 
     CLIENT 
    FROM Test 
), 
Next_Times As (
    SELECT OFFICE_ID, 
     FAIL_DATETIME, 
     COUNT(CASE CLIENT WHEN 'A' THEN 1 END) OVER (PARTITION BY OFFICE_ID ORDER BY FAIL_DATETIME RANGE BETWEEN INTERVAL '10' MINUTE PRECEDING AND INTERVAL '10' MINUTE FOLLOWING) AS COUNT_A, 
     COUNT(CASE CLIENT WHEN 'B' THEN 1 END) OVER (PARTITION BY OFFICE_ID ORDER BY FAIL_DATETIME RANGE BETWEEN INTERVAL '10' MINUTE PRECEDING AND INTERVAL '10' MINUTE FOLLOWING) AS COUNT_B, 
     COUNT(CASE CLIENT WHEN 'C' THEN 1 END) OVER (PARTITION BY OFFICE_ID ORDER BY FAIL_DATETIME RANGE BETWEEN INTERVAL '10' MINUTE PRECEDING AND INTERVAL '10' MINUTE FOLLOWING) AS COUNT_C 
    FROM Times 
) 
SELECT OFFICE_ID, 
     TO_CHAR(FAIL_DATETIME, 'HH24:MI:SS') AS FAIL_TIME, 
     TO_CHAR(FAIL_DATETIME, 'DD/MM/YYYY') AS ACTIVITY_DAY  
FROM Next_Times 
WHERE COUNT_A > 0 
AND COUNT_B > 0 
AND COUNT_C > 0 
ORDER BY FAIL_DATETIME 

Results:

| OFFICE_ID | FAIL_TIME | ACTIVITY_DAY | 
|-----------|-----------|--------------| 
|  1002 | 10:32:00 | 01/07/2014 | 
|  1002 | 10:34:23 | 01/07/2014 | 
|  1002 | 10:35:03 | 01/07/2014 | 
+0

ОП задает 10 г в вопросе. Однако синтаксис RANGE BETWEEN PRECEDING поддерживается [10g] (http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions001.htm#i97640), поэтому это более элегантное решение. – APC

0

Для идентификации записей, вы можете присоединиться к таблице, чтобы это сам три раза, как это:

SELECT 
    a.*, b.*, c.* 
FROM FailLog a INNER JOIN 
    FailLog b ON b.OFFICE_ID = A.OFFICE_ID AND 
      a.CLIENT = 'A' AND 
      b.CLIENT = 'B' AND 
      b.ACTIVITY_DAY = a.ACTIVITY_DAY INNER JOIN 
    FailLog c ON c.OFFICE_ID = A.OFFICE_ID AND 
      c.CLIENT = 'C' AND 
      c.ACTIVITY_DAY = a.ACTIVITY_DAY AND 
      -- need to calculate difference in min here 
      GREATEST (a.FAIL_TIME, b. FAIL_TIME, c. FAIL_TIME) - 
      LEAST (a.FAIL_TIME, b. FAIL_TIME, c. FAIL_TIME) <= 10 

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

0

Первое, что нам нужно, это способ сравнения FAIL_TIME. Поскольку вы не разместили структуру таблицы, предположим, что мы имеем дело со строками.

У Oracle есть некоторые аккуратные встроенные функции для дат и строк литья. Если мы конкатенации ACTIVITY_DATE и FAIL_TIME мы можем преобразовать их в тип данных Дата:

to_date(ACTIVITY_DAY||' '||FAIL_TIME, 'dd/mm/yyyy hh24:mi:ss') 

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

to_char(to_date(ACTIVITY_DAY||' '||FAIL_TIME, 'dd/mm/yyyy hh24:mi:ss'), 'sssss') 

Тогда мы можем бросить что - число, которое мы можем использовать в некоторой арифметике для сравнения с другими строками; десять минут = 600 секунд.

Далее мы можем использовать факторинг подзапроса (предложение WITH). Одна из опрятных особенностей этого синтаксиса состоит в том, что мы можем передать вывод одного подзапроса в другой, поэтому нам нужно только написать это gnarly вложенное выражение выражения один раз.

with t as 
    (select OFFICE_ID 
       , ACTIVITY_DAY 
       , FAIL_TIME 
       , to_number(to_char(to_date(ACTIVITY_DAY||' '||FAIL_TIME, 'dd/mm/yyyy hh24:mi:ss'), 'sssss')) FAIL_TIME_SSSSS 
       , CLIENT 
     from faillog 
    ) 

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

Наконец, мы можем использовать аналитическую функцию COUNT(), чтобы отслеживать, сколько пучков FAIL_TIME у нас есть для каждой комбинации OFFICE и ACTIVITY_DATE.

count(*) over (partition by a.OFFICE_ID, a.ACTIVITY_DAY) 

Собираем все вместе в целях поточного позволяет проверить, можно ли «объявить пост закрытым».

select * from (
    with t as (select OFFICE_ID 
         , ACTIVITY_DAY 
         , FAIL_TIME 
         , to_number(to_char(to_date(ACTIVITY_DAY||' '||FAIL_TIME, 'dd/mm/yyyy hh24:mi:ss'), 'sssss')) FAIL_TIME_SSSSS 
         , CLIENT 
       from faillog 
       ) 
     , a as (select * 
        from t 
        where CLIENT = 'A') 
     , b as (select * 
        from t 
        where CLIENT = 'B') 
     , c as (select * 
        from t 
        where CLIENT = 'C') 
    select a.OFFICE_ID 
      , a.ACTIVITY_DAY 
      , a.FAIL_TIME as a_fail_time 
      , b.FAIL_TIME as b_fail_time 
      , c.FAIL_TIME as a_fail_time 
      , count(*) over (partition by a.OFFICE_ID, a.ACTIVITY_DAY) as fail_count 
    from a 
     join b on a.OFFICE_ID = b.OFFICE_ID and a.ACTIVITY_DAY = b.ACTIVITY_DAY 
     join c on a.OFFICE_ID = c.OFFICE_ID and a.ACTIVITY_DAY = c.ACTIVITY_DAY 
    where a.FAIL_TIME_SSSSS between b.FAIL_TIME_SSSSS - 600 and b.FAIL_TIME_SSSSS + 600 
    and a.FAIL_TIME_SSSSS between c.FAIL_TIME_SSSSS - 600 and c.FAIL_TIME_SSSSS + 600 
    and b.FAIL_TIME_SSSSS between a.FAIL_TIME_SSSSS - 600 and a.FAIL_TIME_SSSSS + 600 
    and b.FAIL_TIME_SSSSS between c.FAIL_TIME_SSSSS - 600 and c.FAIL_TIME_SSSSS + 600 
    and c.FAIL_TIME_SSSSS between a.FAIL_TIME_SSSSS - 600 and a.FAIL_TIME_SSSSS + 600 
    and c.FAIL_TIME_SSSSS between b.FAIL_TIME_SSSSS - 600 and b.FAIL_TIME_SSSSS + 600 
) 
where fail_count >= 3 
/

Примечание

  1. Очевидно, у меня есть жестко идентификатор клиента в подзапросах. Можно было бы избежать жесткого кодирования, но примерный запрос уже достаточно сложный.
  2. Этот запрос не ищет триплетов. Предоставление одного отказа для каждого из A, B и C в течение десяти минутного окна не имеет значения, сколько экземпляров каждый КЛИЕНТ происходит внутри окна. В ваших деловых правилах ничего не говорится, чтобы сказать, что это неправильно.
  3. Аналогичным образом, один и тот же экземпляр один КЛИЕНТ может быть сопоставлен с экземплярами других КЛИЕНТОВ в окнах . Теперь это может быть нежелательным: двойной или тройной подсчет может раздуть FAIL_COUNT. Но опять же, обработка этого будет сделать окончательный запрос более сложным.
  4. Представленный запрос имеет одну строку для каждой отдельной комбинации значений A, B и C FAIL_TIME. Набор результатов можно поворачивать, если вам действительно нужна строка для каждого CLIENT/FAIL_TIME.
Смежные вопросы