我正在尝试编写遵循此逻辑的查询:
查找以前状态代码为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状态代码,将显示第二个拨号的结果。
答案 0 :(得分:2)
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解决方案(其中一个版本)如下所示。
数据设置脚本
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;