2010-07-21 2 views
7

Скажем, у меня есть таблица, в которой я запрашиваю как так:Как найти повторяющиеся последовательные значения в этой таблице?

select date, value from mytable order by date 

и это дает мне результаты:

date     value 
02/26/2009 14:03:39 1     
02/26/2009 14:10:52 2   (a) 
02/26/2009 14:27:49 2   (b) 
02/26/2009 14:34:33 3 
02/26/2009 14:48:29 2   (c) 
02/26/2009 14:55:17 3 
02/26/2009 14:59:28 4 

Я заинтересован в строках этого результирующего набора, где значение равно как в предыдущей или следующей строке, например, строка b, имеющая значение = 2, такая же, как строка a. Мне не нужны строки, такие как строка c, которая имеет значение = 2, но не приходит сразу после строки со значением = 2. Как я могу запросить таблицу, чтобы дать мне все строки, такие как a и b? Это касается Oracle, если это имеет значение.

+1

Существуют ли какие-либо другие поля в таблице, например, последовательный первичный ключ? Это облегчило бы! Является ли это таблицей транзакций или может быть удалена запись (т. Е. Выполнять объединения и тесты с идентификатором-id или id + 1)? –

+0

@Kieren да есть первичный ключ, но они раздаются в порядке, несовместимом с полем даты. Я не уверен, что вы собираетесь «присоединяться и тестировать», но да, записи часто удаляются и заменяются. – JenPartridge

ответ

11

Используйте аналитические функции для свинца и лага.

create table t3 (d number, v number); 
insert into t3(d, v) values(1, 1); 
insert into t3(d, v) values(2, 2); 
insert into t3(d, v) values(3, 2); 
insert into t3(d, v) values(4, 3); 
insert into t3(d, v) values(5, 2); 
insert into t3(d, v) values(6, 3); 
insert into t3(d, v) values(7, 4); 

select d, v, case when v in (prev, next) then '*' end match, prev, next from (
    select 
    d, 
    v, 
    lag(v, 1) over (order by d) prev, 
    lead(v, 1) over (order by d) next 
    from 
    t3 
) 
order by 
    d 
; 

Matching соседей помечены * в колонке матча,

alt text http://i28.tinypic.com/2drrojt.png

+0

Вау - большое спасибо! – JenPartridge

+0

Почему у вас есть ** заказ по d **, когда данные уже заказаны ** d ** на ваши вставки? Кроме того, исходная проблема не требовала корректной сортировки. –

1

Как @Janek Богуцкий отметил опережение и запаздывание, вероятно, самый простой способ сделать это - но просто для удовольствия попробуем сделать это, используя только базовые операции объединения:

SELECT mydate, VALUE FROM 
    (SELECT a.mydate, a.value, 
      CASE WHEN a.value = b.value THEN '*' ELSE NULL END AS flag1, 
      CASE WHEN a.value = c.value THEN '*' ELSE NULL END AS flag2 
    FROM 
     (SELECT ROWNUM AS outer_rownum, mydate, VALUE 
     FROM mytable 
     ORDER BY mydate) a 
    LEFT OUTER JOIN 
     (select ROWNUM-1 AS inner_rownum, mydate, VALUE 
     from mytable 
     order by myDATE) b 
     ON b.inner_rownum = a.outer_rownum 
    LEFT OUTER JOIN 
     (select ROWNUM+1 AS inner_rownum, mydate, VALUE 
     from mytable 
     order by myDATE) c 
     ON c.inner_rownum = a.outer_rownum 
    ORDER BY a.mydate) 
    WHERE flag1 = '*' OR 
     flag2 = '*'; 

Делитесь и наслаждайтесь.

+0

rownum назначается до того, как результаты будут отсортированы, чтобы это не гарантировало работу. Это показывает, как вместо этого можно использовать row_number(), чтобы получить восходящую последовательность чисел, которая соответствует порядку by by): выберите rownum, row_number() over (order by v-d) из порядка t3 по v-d; Это использует данные из моего ответа выше. –

2

Это упрощенная версия ответа @Bob Джарвиса, основное отличие является использование только одного подзапроса вместо четырех,

with f as (select row_number() over(order by d) rn, d, v from t3) 
select 
    a.d, a.v, 
    case when a.v in (prev.v, next.v) then '*' end match 
from 
    f a 
    left join 
    f prev 
    on a.rn = prev.rn + 1 
    left join 
    f next 
    on a.rn = next.rn - 1 
order by a.d 
; 
Смежные вопросы