棘手的SQL问题

时间:2011-03-15 11:26:10

标签: sql exists

我有一张这样的桌子......

Key  Seq     Val 
A     1      123
A     4      129 
A     9      123
A     10     105 
B     3      100
B     6      101
B     12     102

我想找到案例(如A,4),其中值(在本例中为123)在之前(在本例中为A,1)和之后(在本例中为A,9)是相同的。 seq严格增加,但可能有差距。 有什么建议吗?

5 个答案:

答案 0 :(得分:3)

虽然我只在sql server 2005中测试了这个(因为我没有2000个实例),在用真实表替换@t后,这应该仍然适用于该平台。

select k, seq, val
from (
    select k, seq, val,
          (select top 1 val from @t aux where aux.k = main.k and aux.seq < main.seq order by seq desc) as prev_val,
          (select top 1 val from @t aux where aux.k = main.k and aux.seq > main.seq order by seq asc) as next_val
    from @t main
) x
where prev_val = next_val

如果您在k, seq上有索引,则性能不应该坏,因为相关子查询是简单的索引扫描。

遗憾的是,我认为laglead函数的支持不在SQL Server路线图上。

[如果有人感兴趣,我的意思是在某些数据库中你可以写:

select key, seq, val
from (
    select key, seq, val,
           lag(val) over(partition by key order by seq) as prev_val,
           lead(val) over(partition by key order by seq) as next_val
    from t
    ) x
where prev_val = next_val;

如果您想要查看前两个或更多值,这肯定会自成一体,因为您可以编写lag(val, 2)来查看2行等。查找前一个或下一个值是一个更简单的情况select top 1 ...处理得非常好。 ]

答案 1 :(得分:2)

我不希望这会消耗数千行:

SELECT
    * /* TODO - pick columns */
FROM
    Table t1
        inner join
    Table t2
        on
            t1.Key = t2.Key and
            t1.Seq < t2.Seq
        inner join
    Table t3
        on
            t1.Key = t3.Key and
            t1.Seq > t3.Seq and
            t2.Val = t3.Val
        left join
    Table t4
        on
            t1.Key = t4.Key and
            t1.Seq < t4.Seq and
            t4.Seq < t2.Seq
        left join
    Table t5
        on
            t1.Key = t5.Key and
            t1.Seq > t5.Seq and
            t5.Seq > t3.Seq
WHERE
     t4.Key is null and t5.Key is null

基本上,根据你的定义,表的前3个实例将表连接到自身,以找到围绕“有趣”行的两行。后续连接(t4和t5)确保t2和t3搜索找到的行最接近t1行。

答案 2 :(得分:1)

编辑:我在你说SQL Server 2000之前写过这个。这在SQL Server 2005或更高版本中有效,所以它对你没有帮助,但我会留在这里为后人:)

我正在使用CTE向表中添加顺序(不间断)排序,然后连接两次以获取上一行和下一行。

declare @t table (k char(1), seq int, val int)

insert into @t values ('A', 1, 100)
insert into @t values ('A', 4, 101)
insert into @t values ('A', 9, 100)
insert into @t values ('A', 10, 105)
insert into @t values ('B', 3, 100)
insert into @t values ('B', 6, 101)
insert into @t values ('B', 12, 102)

; with q as (
    select *, row_number() over (partition by k order by seq) [rownum] from @t
)     
select * 
from q
join q q1 on q1.rownum=q.rownum-1 and q.k=q1.k
join q q2 on q2.rownum=q.rownum+1 and q.k=q2.k
where q1.val=q2.val

答案 3 :(得分:1)

如果您不需要seq字段,则此方法有效:

;with cte as
(
select COUNT( 1 ) as cnt, val, [key] from tbl 
group by val, [key]
)
select * from cte where cnt > 1

如果你这样做:

;with cte as
(
select COUNT( 1 ) as cnt, val, [key] from tbl 
group by val, [key]
)
select tbl.* from tbl inner join cte on cte.cnt > 1 and cte.[Key] = tbl.[Key] and cte.Val = tbl.Val

编辑:一种tmptbl方法,它不会给你seq:

CREATE TABLE #tmptbl (
    cnt int,
    [key] nchar(10),
    Val nchar(10)
 )

insert into #tmptbl
select COUNT( 1 ) as cnt, [key], Val from tbl 
group by tbl.Val, tbl.[key]
select * from #tmptbl where cnt > 1

drop table #tmptbl

根据您的字段类型,这可能很容易更改,以便为您提供seq。

答案 4 :(得分:1)

假设表的名称是“Table”,这里是普通的vanilla sql。

SELECT 
Key, 
Seq 
from Table A
WHERE EXISTS
 (SELECT 1 FROM Table B, Table C 
   WHERE B.Key = A.Key
     AND C.Key = A.Key
     AND B.Seq = (SELECT MAX(Seq) FROM Table D WHERE D.Key = A.Key AND D.Seq < A.Seq) --This ensures that B retrieves previous row
     AND C.Seq = (SELECT MIN(Seq) FROM Table E WHERE E.Key = A.Key AND E.Seq > A.Seq) --This ensures that C retrieves next row
     AND B.Val = C.Val
 )