How can I find duplicate consecutive values in this table?
Say I have a table which I query like so:
select date, value from mytable order by date
and this gives me results:
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:4开发者_运维技巧8:29 2 (c)
02/26/2009 14:55:17 3
02/26/2009 14:59:28 4
I'm interested in the rows of this result set where the value is the same as the one in the previous or next row, like row b which has value=2
the same as row (a). I don't care about rows like row (c) which has value=2
but does not come directly after a row with value=2
. How can I query the table to give me all rows like (a) and (b) only? This is on Oracle, if it matters.
Use the lead and lag analytic functions.
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 neighbours are marked with * in the match column,
This is a simplified version of @Bob Jarvis' answer, the main difference being the use of just one subquery instead of four,
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
;
As @Janek Bogucki has pointed out LEAD and LAG are probably the easiest way to accomplish this - but just for fun let's try to do it by using only basic join operations:
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 = '*';
Share and enjoy.
精彩评论