3

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

查找先前状态代码为 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

4 回答 4

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 解决方案(其中一个版本)的查询计划如下。

查询计划

数据设置脚本

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'
于 2012-11-27T22:54:29.870 回答
2

SQL Fiddle 与子选择

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)

SQL Fiddle with join,都适用于 MS SQL Server 2012,都返回相同的结果。

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
于 2012-11-27T22:41:38.627 回答
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

于 2012-11-27T22:51:38.230 回答
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;
于 2012-11-27T22:52:10.200 回答