如何在此SQL查询中获取每个帐户的第一个结果?

时间:2012-11-27 22:29:28

标签: sql sql-server

我正在尝试编写遵循此逻辑的查询:

查找以前状态代码为X的帐户的以下第一个状态代码。

所以,如果我有一张表:

id    account_num    status_code
64        1               X
82        1               Y
72        2               Y
87        1               Z
91        2               X
103       2               Z

结果将是:

id   account_num     status_code
82        1               Y
103       2               Z

我已经提出了几个解决方案但是我对SQL并不是那么好,所以到目前为止它们已经非常不合适了。我希望这里的某个人能指出我正确的方向。

查看:

SELECT account_number, id
FROM   table
WHERE  status_code = 'X'

查询:

SELECT account_number, min(id)
FROM   table
INNER JOIN view
ON table.account_number = view.account_number
WHERE table.id > view.id

此时我有我需要的id,但是我必须编写另一个使用id的查询来获取status_code。

编辑:要添加一些上下文,我正在尝试查找status_code为X的调用。如果调用的status_code为X,我们希望下次以不同的方式拨打它我们试一试。此查询的目的是提供一个报告,如果第一个拨号产生X状态代码,将显示第二个拨号的结果。

4 个答案:

答案 0 :(得分:2)

SQL Fiddle with subselect

select id, account_num, status_code
from mytable
where id in (select min(t1.id)
             from mytable t1
             join mytable t2 on t1.account_num = t2.account_num
                             and t1.id > t2.id
                             and t2.status_code = 'X'
             group by t1.account_num)
对于MS SQL Server 2012,

SQL Fiddle with join都返回相同的结果。

select id, account_num, status_code
from mytable
join (select min(t1.id) as min_id
      from mytable t1
      join mytable t2 on t1.account_num = t2.account_num
                      and t1.id > t2.id
                      and t2.status_code = 'X'
      group by t1.account_num) t on id = min_id

答案 1 :(得分:2)

这是一个SQL Server解决方案。

<强>更新

这个想法是为了避免Olaf提出的许多NESTED LOOP连接,因为它们大致具有O(N * M)复杂度,因此对你的性能非常不利。 MERGED JOINS复杂度为O(N Log(N)+ M Log(M)),这对现实场景来说要好得多。

以下查询的工作原理如下:

RankedCTE是一个子查询,它为每个由帐户分配的id分配一个行号,并按表示时间的id排序。所以对于这个

输出下面的数据
SELECT 
    id, 
    account_num, 
    status_code,
    ROW_NUMBER() OVER (PARTITION BY account_num ORDER BY id DESC) AS item_rank
FROM dbo.Test

将是:

id          account_num status_code item_rank
----------- ----------- ----------- ----------
87          1           Z           1
82          1           Y           2
64          1           X           3
103         2           Z           1
91          2           X           2
72          2           Y           3

一旦我们对它们进行了编号,我们就像这样加入结果:

WITH RankedCTE AS
(
    SELECT 
        id, 
        account_num, 
        status_code,
        ROW_NUMBER() OVER (PARTITION BY account_num ORDER BY id DESC) AS item_rank
    FROM dbo.Test
)
SELECT 
    *
FROM
    RankedCTE A
    INNER JOIN RankedCTE B ON 
            A.account_num = B.account_num
            AND A.item_rank = B.item_rank - 1    

将在同一个表中为我们提供事件和前一个事件

id          account_num status_code item_rank   id          account_num status_code item_rank
----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
87          1           Z           1           82          1           Y           2
82          1           Y           2           64          1           X           3
103         2           Z           1           91          2           X           2
91          2           X           2           72          2           Y           3

最后,我们只需要使用代码“X”进行前面的事件,使用代码的事件不是“X”:

    WITH RankedCTE AS
    (
        SELECT 
            id, 
            account_num, 
            status_code,
            ROW_NUMBER() OVER (PARTITION BY account_num ORDER BY id DESC) AS item_rank
        FROM dbo.Test
    )
    SELECT 
        A.id, 
        A.account_num, 
        A.status_code
    FROM 
        RankedCTE A
        INNER JOIN RankedCTE B ON 
            A.account_num = B.account_num
            AND A.item_rank = B.item_rank - 1
            AND A.status_code <> 'X'
            AND B.status_code = 'X'

此查询的查询计划和@Olaf Dietsche解决方案(其中一个版本)如下所示。

query plans

数据设置脚本

CREATE TABLE dbo.Test
(
    id int not null PRIMARY KEY,
    account_num int not null,
    status_code nchar(1)
)
GO

INSERT dbo.Test (id, account_num, status_code)
SELECT 64 ,       1,               'X' UNION ALL
SELECT 82 ,       1,               'Y' UNION ALL
SELECT 72 ,       2,               'Y' UNION ALL
SELECT 87 ,       1,               'Z' UNION ALL
SELECT 91 ,       2,               'X' UNION ALL
SELECT 103,       2,               'Z'

答案 2 :(得分:0)

SELECT MIN(ID), ACCOUNT_NUM, STATUS_CODE FROM (
    SELECT ID,  ACCOUNT_NUM, STATUS_CODE
    FROM ACCOUNT A1
    WHERE EXISTS 
       (SELECT 1 
        FROM ACCOUNT A2 
        WHERE A1.ACCOUNT_NUM = A2.ACCOUNT_NUM
         AND A2.STATUS_CODE = 'X'
         AND A2.ID < A1.ID)
    ) SUB
GROUP BY ACCOUNT_NUM

这里是SQLFIDDLE

答案 3 :(得分:0)

这是在PostgreSQL下检查您的数据的查询:

SELECT t0.*
FROM so13594339 t0 JOIN
 (SELECT min(t1.id), t1.account_num
   FROM so13594339 t1, so13594339 t2
   WHERE t1.account_num = t2.account_num AND t1.id > t2.id AND t2.status_code = 'X'
   GROUP BY t1.account_num
 ) z
 ON t0.id = z.min AND t0.account_num = z.account_num;