2016-09-14 3 views
0

У нас есть запрос Oracle SQL для идентификации записей, в которых значение столбца таблицы изменилось с одной записи на другую. Соответствующие столбцы (ID, SOME_COLUMN, FROM_DATE, TO_DATE), где идентификатор не является уникальным, и FROM_DATE и TO_DATE определить временной интервал, для которого конкретный строка для этого ID была эффективной, т.е.Оптимизировать самостоятельный запрос Oracle SQL с аналитическими функциями LAG/LEAD?

(ID1, VAL1, 01/01/2016, 03/01/2016) 
(ID1, VAL2, 04/01/2016, 09/01/2016) 
(ID1, VAL3, 10/01/2016, 19/01/2016) 

т.д.

Мы могли бы реализовать это с помощью следующего автообъединения

SELECT N.ID 
     O.SOME_COLUMN OLD_VALUE, 
     N.SOME_COLUMN NEW_VALUE 
FROM OUR_TABLE N, OUR_TABLE O 
WHERE N.ID = O.ID 
    AND N.FROM_DATE - 1 = O.TO_DATE 
    AND N.SOME_COLUMN <> O.SOME_COLUMN 

однако поскольку таблица содержит 100 миллионов записей, это довольно поражает производительность. Есть ли более эффективный способ сделать это? Кто-то намекнул на аналитические функции (например, LAG), но пока мы не смогли найти рабочего решения. Любые идеи будут оценены

+0

Ваш вопрос непонятен. Ваши даты не пересекаются вообще, и неясно, какие результаты вы хотите. –

+0

Даты действительно не перекрываются, они представляют временные интервалы, в течение которых эффективна определенная строка таблицы, принадлежащая этому идентификатору. Как вы видите в примере, FROM_DATE интервала всегда добавляется +1 к TO_DATE предыдущего интервала. Нам нужны результаты, когда значение SOME_COLUMN изменилось с одного интервала на другой. Фактически, проверяя ваш ответ ниже, вы, кажется, правильно все интерпретировали. – hammerfest

ответ

2

Да, вы можете использовать LEAD() для извлечения последнего значения:

SELECT t.id, 
     t.some_column as OLD_VALUE, 
     LEAD(t.some_column) OVER(PARTITION BY t.id ORDER BY t.from_date) as NEW_VALUE 
FROM YourTable t 

Если вы хотите только изменения, завернуть его другим выбирать и фильтровать OLD_VALUE <> NEW_VALUE

1

Если вы хотите старое значение и новое значение в одной строке, а затем использовать lag():

select t.*, 
     lag(some_column) over (partition by id order by from_date) as prev_val 
from t; 

Если значения не могут быть изменены (как предполагают, ed по вашему образцовому запросу):

select t.* 
from (select t.*, 
      lag(some_column) over (partition by id order by from_date) as prev_val 
     from t 
    ) t 
where prev_val <> some_column; 
1

Я думаю, что это подход LAG(), о котором вы говорили.

SELECT * 
    FROM (
    SELECT ID 
      N.SOME_COLUMN NEW_VALUE, 
      N.FROM_DATE, 
      lag(N.SOME_COLUMN) over (partition by N.ID order by FROM_DATE) OLD_VALUE, 
      lag(N.TO_DATE) over (partition by N.ID order by FROM_DATE) OLD_TO_DATE, 
    FROM OUR_TABLE N 
) T 
WHERE FROM_DATE - 1 = OLD_TO_DATE 
    AND NEW_VALUE<> OLD_VALUE; 
+1

Спасибо. Первый из трех почти одинаковых ответов, я помещаю знак accept здесь. – hammerfest

+0

@hammerfest. , , На самом деле это был третий ответ. По вашим рассуждениям вы должны принять ответ Саги. Если вы нажмете над «временем» над именем (которое теперь говорит «ответили 2 часа назад»), вы увидите точное время ответа. –

+0

Кажется, вы правы, я просто исказил два ответа, попробовав решения и вернувшись на страницу вопросов. Теперь я изменил знак принятия. @vercelli: Извините, ваш ответ так же корректен, но это был действительно не первый. – hammerfest

Смежные вопросы