棘手的 SQL 问题

发布于 2024-10-22 01:18:53 字数 275 浏览 5 评论 0原文

我有一个像这样的表...

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

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

I have a table like this ...

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

I want to find cases (like A,4) where the value(in this case 123) is the same before (in this case A,1) and after (in this case A,9).
The seq increases strictly, but may have gaps.
Any suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

触ぅ动初心 2024-10-29 01:18:53

虽然我只在 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 上有一个索引,那么性能应该不会差,因为相关子查询是简单的索引扫描。

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

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

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)< /code> 向后查看 2 行等。查找紧邻的上一个或下一个值是一个更简单的情况,select top 1 ... 可以很好地处理。 ]

Although I have only tested this in sql server 2005 (since I don't have a 2000 instance around), after replacing @t with a real table this should still work on that platform.

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

Provided you have an index on k, seq the performance shouldn't be too bad, as the correlated subqueries are simple index scans.

Sadly, I don't think support for the lag and lead functions is on the SQL Server roadmap.

[ In case anyone's interested, I mean that in some databases you can write:

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;

This would definitely come into its own if you wanted to look at the previous two or more values, because you can write lag(val, 2) to look 2 rows back etc. Finding the immediately previous or next value is a simpler case that select top 1 ... handles quite nicely. ]

迷雾森÷林ヴ 2024-10-29 01:18:53

我不希望这会跨越数千行:

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 行。

I wouldn't expect this to blaze through thousands of rows:

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

Basically, the first 3 instances of the table joins the table on itself to find two rows which surround an "interesting" row, per your definition. The subsequent joins (t4 and t5) ensure that the rows found by the t2 and t3 searches are the closest to the t1 row.

念三年u 2024-10-29 01:18:53

编辑:我在你说 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

Edit: I wrote this before you said SQL Server 2000. This works in SQL Server 2005 or later, so it won't help you, but I'll leave it here for posterity :)

I'm using a CTE to add a sequential (unbroken) ordering to the table, then joining twice to get previous and next rows.

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
森末i 2024-10-29 01:18:53

如果您不需要 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

编辑:一种不会为您提供 seq 的 tmptbl 方法:

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出色地。

This works if you don't need the seq field:

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

this if you do:

;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

EDIT: A tmptbl approach that doesn't give you the 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

Depending on the types of your fields this may be easy to change to give you the seq as well.

秋千易 2024-10-29 01:18:53

假设表的名称是“Table”,这里是普通的 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
 )

Assuming the name of the table is "Table" here is the plain 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
 )
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文