如何根据特定条件从表的子集中选择行?

时间:2019-06-14 04:29:02

标签: sql-server tsql sql-server-2008-r2

我有一个带有ActionId列的测试表。该列包含值从1到5的不断增加的随机行,然后再次从1到5的另一个值子集开始。数据可以具有一个或多个这样的子集。

我对包含ActionId值为4或5但每个子集中只有最后一个的行感兴趣。因此,在此示例中,我想返回第7和11行。第7行是因为5是该值下降之前的最后一个值,第11行是因为4是该值再次下降之前的最后一个值。对于最后一个子集,该值无需再次下降。值4或5可能位于最后一行。

我可以用一种程序语言对此进行编程,但是我想不出基于集合的SQL解决方案。

 CREATE TABLE test (
    id  [int] IDENTITY(1,1)
    ,ActionId INT)

    INSERT INTO [test] (ActionId ) VALUES
    (1), (2), (3), (3), (4), (4), (5), (3), (3), (3), (4), (1),(2)

select * from test

http://sqlfiddle.com/#!18/4ffe71/3

4 个答案:

答案 0 :(得分:0)

如果我猜对了,则需要所有值,其中下一行的值小于当前值。如果我是正确的,则可以出于自身目的使用自我加入。以下脚本将为您提供所需的输出-

DECLARE @test TABLE 
(
    id  [int] IDENTITY(1,1),
    Actionid INT
)

INSERT INTO @test (Actionid ) 
VALUES
(1), (2), (3), (3), (4), (4), (5), (3), (3), (3), (4), (1),(2)

SELECT A.*
FROM @test A LEFT JOIN @test B ON A.id = B.id-1
WHERE B.Actionid < A.Actionid

输出为-

id  Actionid
7   5
11  4

如果在不考虑任何条件的情况下也需要最后一行的值,只需使用以下命令更改脚本。这将在输出中包含最后一个值2。

SELECT A.*
FROM @test A LEFT JOIN @test B ON A.id = B.id-1
WHERE B.Actionid < A.Actionid 
OR B.Actionid IS NULL

答案 1 :(得分:0)

递归CTE可以在这里为您提供帮助:

-您的样机表

DECLARE @test TABLE 
(
    id  [int] IDENTITY(1,1),
    Actionid INT
)

INSERT INTO @test (Actionid ) 
VALUES  (1), (2), (3), (3), (4), (4), (5), (3), (3), (3), (4), (1),(2);

-查询

WITH recCTE AS
(
    SELECT id
          ,Actionid
          ,1 AS GroupKey
          ,1 AS GroupStep 
    FROM @test t WHERE id=1 --the IDENTITY is the sorting key obviously and will start with a 1 in this test case.

    UNION ALL

    SELECT t.id
          ,t.Actionid
          ,CASE WHEN t.Actionid<=r.Actionid THEN r.GroupKey+1 ELSE r.GroupKey END
          ,CASE WHEN t.Actionid<=r.Actionid THEN 1 ELSE r.GroupStep+1 END
    FROM @test t
    INNER JOIN recCTE r ON t.id=r.id+1
)
SELECT * 
FROM recCTE;

简而言之:

我们从第一行开始,依次遍历集合逐行。我们测试的每一行,如果ActionId没有增加,并为GroupKeyGroupStep设置相应的值。

结果

+----+----------+----------+-----------+
| id | Actionid | GroupKey | GroupStep |
+----+----------+----------+-----------+
| 1  | 1        | 1        | 1         |
+----+----------+----------+-----------+
| 2  | 2        | 1        | 2         |
+----+----------+----------+-----------+
| 3  | 3        | 1        | 3         |
+----+----------+----------+-----------+
| 4  | 3        | 2        | 1         |
+----+----------+----------+-----------+
| 5  | 4        | 2        | 2         |
+----+----------+----------+-----------+
| 6  | 4        | 3        | 1         |
+----+----------+----------+-----------+
| 7  | 5        | 3        | 2         |
+----+----------+----------+-----------+
| 8  | 3        | 4        | 1         |
+----+----------+----------+-----------+
| 9  | 3        | 5        | 1         |
+----+----------+----------+-----------+
| 10 | 3        | 6        | 1         |
+----+----------+----------+-----------+
| 11 | 4        | 6        | 2         |
+----+----------+----------+-----------+
| 12 | 1        | 7        | 1         |
+----+----------+----------+-----------+
| 13 | 2        | 7        | 2         |
+----+----------+----------+-----------+

解决您的问题

我们可以通过将最终的SELECT更改为此来进行

SELECT TOP 1 WITH TIES * 
FROM recCTE
ORDER BY ROW_NUMBER() OVER(PARTITION BY GroupKey ORDER BY GroupStep DESC);

结果显示每个子集的最后一个条目

+----+----------+----------+-----------+
| id | Actionid | GroupKey | GroupStep |
+----+----------+----------+-----------+
| 3  | 3        | 1        | 3         |
+----+----------+----------+-----------+
| 5  | 4        | 2        | 2         |
+----+----------+----------+-----------+
| 8  | 3        | 4        | 1         |
+----+----------+----------+-----------+
| 9  | 3        | 5        | 1         |
+----+----------+----------+-----------+
| 11 | 4        | 6        | 2         |
+----+----------+----------+-----------+
| 7  | 5        | 3        | 2         |
+----+----------+----------+-----------+
| 13 | 2        | 7        | 2         |
+----+----------+----------+-----------+

您可以过滤到最后一个条目为4或5的子集。在这种情况下,我看到的是第7和11行,还有第5行。可能是因为我没有正确理解逻辑...

答案 2 :(得分:0)

这是我想到的查询:

WITH cte
AS
(SELECT id, Actionid, ROW_NUMBER() OVER (ORDER BY id) rn FROM test)

SELECT
    prev.id
   ,prev.Actionid prevActionId
   ,cur.Actionid curActionId
FROM cte cur
JOIN cte prev
    ON prev.rn = cur.rn - 1
WHERE
    prev.Actionid > cur.Actionid
    AND prev.Actionid IN (4, 5)

答案 3 :(得分:0)

我想出的解决方案涉及一个简单的相关子查询和一个公用表表达式:

;with cte as
(
select  id, 
        ActionId,
        isnull((
            select top 1 ActionId
            from test as t1
            where t0.id < t1.id
            order by t1.id 
        ), 0) as nextActionId
from test As t0
)

select id, ActionId
from cte
where actionId IN(4,5)
and actionId > nextActionId

子查询根据id列的顺序为每一行获取下一个actionId。 isnull在最后一行-返回0而不是null

然后,您所要做的就是查询操作ID为4或5且大于下一个操作ID的cte。