Oracle sql: фильтрация «поддельных» (повторяющихся) строк, которые отличаются лишь небольшим промежутком времени - PullRequest
1 голос
/ 10 июля 2020

Я задавал аналогичный вопрос несколько дней a go, но, к сожалению, я неправильно понял формулировку проблемы, которую я должен решить (точнее, я забыл о ее части). Вот почему этот пост может выглядеть как дублированный, но это не так.

Это мой исходный пост: Oracle sql: фильтрация повторяющихся строк, которые отличаются лишь небольшим промежутком времени

Итак, давайте снова go:

У меня есть таблица Oracle с аварийными сигналами событий, запускаемыми паркиметрами. Сигналы тревоги имеют состояние Открыто / Закрыто, и когда сигнализация Открыта (PKN_EVENTSTATUS = 'Open') и Закрыта (PKN_EVENTSTATUS = 'Close') между небольшим промежутком времени -RECEIVEDDATE- (скажем, например, Open RECEIVEDDATE = x | Close RECEIVEDDATE = x + 30 секунд и, конечно, PKN_EVENTNAME то же самое) обе строки событий (Открытие / Закрытие) считаются «ложным» сигналом («сигналом отказа», который сработал паркиметром по «ошибке») поэтому оба должны быть удалены.

Мне нужно было бы создать Oracle SQL запрос, который выберет все эти «поддельные» сигналы тревоги, чтобы я мог их удалить. Опять же, сигналы, которые имеют небольшую разницу во времени (ДАТА ПОЛУЧЕНИЯ) между состояниями «Открыто» и «Закрыто».

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

Мой текущий очень медленный запрос:

select t1.ID, t1.PKN_EVENTNAME, t1.PKN_EVENTSTATUS, t1.RECEIVEDDATE
from PARQUIMETERS_ALARMS t1
where
  exists
     (select 'x'
      from   PARQUIMETERS_ALARMS t2
      where  t1.PKN_EVENTNAME = t2.PKN_EVENTNAME
             and ((t1.PKN_EVENTSTATUS = 'Open' and t2.PKN_EVENTSTATUS = 'Close'
             and abs(t1.RECEIVEDDATE - t2.RECEIVEDDATE) * 24 * 60 * 60 < 30) -- < 30 sec
             or (t1.PKN_EVENTSTATUS = 'Close' and t2.PKN_EVENTSTATUS = 'Open'
             and abs(t2.RECEIVEDDATE - t1.RECEIVEDDATE) * 24 * 60 * 60 < 30))) -- < 30 sec

Ответы [ 2 ]

0 голосов
/ 10 июля 2020

Вы можете использовать pivot (), чтобы упростить задачу - для этого требуется всего 1 сканирование без дополнительного запроса к той же таблице:

select 
  level, 
  'EVENT'||trunc(dbms_random.value(1,5)), 
  case when dbms_random.value>0.5 then 'CLOSE' else 'OPEN' end case,
  date'2020-01-01' + numtodsinterval( trunc(level*dbms_random.value(1,60)) ,'second')
from dual
connect by level<=50
)
,v_pivot as (
   select 
       ID
     , PKN_EVENTNAME
     , OPEN
     , CLOSE
     , lag(open)over(partition by PKN_EVENTNAME order by coalesce(open,close)) last_open
   from (
      select *
      from PARQUIMETERS_ALARMS v
      pivot (
         max(RECEIVEDDATE)
         for (PKN_EVENTSTATUS) in ('OPEN' as open,'CLOSE' as close)
      )
   )
)
select
 *
from v_pivot
where close is null or
abs(close - last_open) * 24 * 60 * 60 > 30;

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

with PARQUIMETERS_ALARMS(ID, PKN_EVENTNAME, PKN_EVENTSTATUS, RECEIVEDDATE) as (
select 
  level, 
  'EVENT'||trunc(dbms_random.value(1,5)), 
  case when dbms_random.value>0.5 then 'CLOSE' else 'OPEN' end case,
  date'2020-01-01' + numtodsinterval( trunc(level*dbms_random.value(1,60)) ,'second')
from dual
connect by level<=50
)
,v_pivot as (
   select 
       ID
     , PKN_EVENTNAME
     , OPEN
     , CLOSE
     , lag(open)over(partition by PKN_EVENTNAME order by coalesce(open,close)) last_open
   from (
      select *
      from PARQUIMETERS_ALARMS v
      pivot (
         max(RECEIVEDDATE)
         for (PKN_EVENTSTATUS) in ('OPEN' as open,'CLOSE' as close)
      )
   )
)
select
 *
from v_pivot
where close is null or
abs(close - last_open) * 24 * 60 * 60 > 30
/
0 голосов
/ 10 июля 2020

Возможно, будет быстрее использовать два условия exists, а не одно:

select t1.id, t1.pkn_eventname, t1.pkn_eventstatus, t1.receiveddate
from parquimeters_alarms t1
where
    (
        t1.pkn_eventstatus = 'Open'
        and exists (
            select 1
            from parquimeters_alarms t2
            where 
                t2.pkn_eventname = t1.pkn_eventname 
                and t2.pkn_eventstatus = 'Close'
                and t2.receiveddate < t1.receiveddate + 30 / 60 / 60 / 24
        )
    )
    or (
        t1.pkn_eventstatus = 'Close'
        and exists (
            select 1
            from parquimeters_alarms t2
            where 
                t2.pkn_eventname = t1.pkn_eventname 
                and t2.pkn_eventstatus = 'Open'
                and t1.receiveddate < t2.receiveddate + 30 / 60 / 60 / 24
        )
    )
    

Этот запрос может использовать индекс на (pkn_eventname, pkn_eventstatus, receiveddate).

Вы также можете рассмотреть union all, что устраняет необходимость в условии or:

select t1.id, t1.pkn_eventname, t1.pkn_eventstatus, t1.receiveddate
from parquimeters_alarms t1
where
    t1.pkn_eventstatus = 'Open'
    and exists (
        select 1
        from parquimeters_alarms t2
        where 
            t2.pkn_eventname = t1.pkn_eventname 
            and t2.pkn_eventstatus = 'Close'
            and t2.receiveddate < t1.receiveddate + 30 / 60 / 60 / 24
    )
union all
select t1.id, t1.pkn_eventname, t1.pkn_eventstatus, t1.receiveddate
from parquimeters_alarms t1
where 
    t1.pkn_eventstatus = 'Close'
    and exists (
        select 1
        from parquimeters_alarms t2
        where 
            t2.pkn_eventname = t1.pkn_eventname 
            and t2.pkn_eventstatus = 'Open'
            and t1.receiveddate < t2.receiveddate + 30 / 60 / 60 / 24
    )
...