Как выбрать строки на основе определенных критериев из подмножеств в таблице? - PullRequest
1 голос
/ 14 июня 2019

У меня есть тестовая таблица со столбцом 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 голосов
/ 16 июня 2019

Решение, которое я придумал, включает в себя простой коррелированный подзапрос и общее табличное выражение:

;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

Подзапрос получает следующий actionId для каждой строки на основе порядка столбца id.isnull есть для последней строки - чтобы вернуть 0 вместо null.

Затем все, что вам нужно сделать, это запросить cte, где actionId равен 4 или 5, и этобольше, чем следующий идентификатор действия.

0 голосов
/ 14 июня 2019

Рекурсивный 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 не увеличивается, и устанавливаем соответствующие значения в GroupKey и GroupStep.

Результат

+----+----------+----------+-----------+
| 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. Возможно, я неправильно понял логику ...

0 голосов
/ 15 июня 2019

Это мой запрос:

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)
0 голосов
/ 14 июня 2019

Если я думаю, правильно, вам нужны все значения, где значение следующей строки меньше текущего значения.Если я прав, Вы можете использовать self join для своих целей.Следующий скрипт даст вам желаемый вывод:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...