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

Рассмотрим данные в таблице, отсортированной по дате дес.

enter image description here

Если есть несколько последовательных строк с одинаковым описанием, я бы хотел взять только одну с самой старой датой. Например, строки 2 и 3: Неизвестно , я хочу оставить только одну на 9/12/2014 .

Я пробовал использовать CTE в сочетании с ROW_NUMBER (), но я не могу ограничить его строками с последовательным идентичным описанием.

;WITH removeConsecutiveRows AS (
  SELECT ph.Description,
       ph.Price,
       ph.Date,
       ROW_NUMBER() OVER (
          PARTITION BY ph.Description
          ORDER BY ph.Date
       ) AS rowNum 
  FROM #PriceHistory ph (NOLOCK)
)
SELECT s.Description,
       s.Price,
       s.Date,
       s.rowNum
FROM removeConsecutiveRows s
WHERE s.rowNum = 1
ORDER BY s.Date DESC

Итак, в конце все должно выглядеть так:

enter image description here

Следует отметить, что это SQL Server 2008.

1 Ответ

1 голос
/ 12 апреля 2019

Это похоже на проблему "пробелов и островов" с "топ-1-на-группу" сверху после обнаружения групп / островов.

Вот один из способов сделать это.

Пример данных

CREATE TABLE #temptable ( Descr varchar(50), [Price] int, dt date )
INSERT INTO #temptable
VALUES
( 'Active', 799900, N'2019-02-27T00:00:00' ), 
( 'Unknown', 629900, N'2014-09-24T00:00:00' ), 
( 'Unknown', 629900, N'2014-09-12T00:00:00' ), 
( 'Sold', 625900, N'2014-09-08T00:00:00' ), 
( 'Unknown', 629900, N'2014-08-10T00:00:00' ), 
( 'Active', 629900, N'2014-07-27T00:00:00' ), 
( 'Pending', 629900, N'2014-07-25T00:00:00' ), 
( 'Pending', 629900, N'2014-07-24T00:00:00' ), 
( 'Unknown', 629900, N'2014-07-20T00:00:00' ), 
( 'Active', 629900, N'2014-07-16T00:00:00' ), 
( 'Active', 629900, N'2014-07-15T00:00:00' ), 
( 'Taking Backup Offers', 629900, N'2014-07-11T00:00:00' ), 
( 'Active', 629900, N'2014-06-28T00:00:00' ), 
( 'Active', 629900, N'2014-06-27T00:00:00' ), 
( 'Taking Backup Offers', 629900, N'2014-06-27T00:00:00' ), 
( 'Active', 629900, N'2014-06-23T00:00:00' ), 
( 'Active', 629900, N'2014-06-11T00:00:00' ), 
( 'Active', 629900, N'2014-06-10T00:00:00' ), 
( 'Sold', 570000, N'2010-01-22T00:00:00' ), 
( 'Sold', 288000, N'2000-09-01T00:00:00' );

Запрос

WITH
CTE_RN
AS
(
    SELECT
        * 
        ,ROW_NUMBER() OVER (ORDER BY dt DESC) AS rn1
        ,ROW_NUMBER() OVER (PARTITION BY Descr ORDER BY dt DESC) AS rn2
    FROM #temptable
)
,CTE_Groups
AS
(
    SELECT
        *
        ,rn1 - rn2 AS Groups
        ,ROW_NUMBER() OVER (PARTITION BY Descr, rn1 - rn2 ORDER BY dt) AS rn
    FROM CTE_RN
)
SELECT Descr, Price, dt
FROM CTE_Groups
WHERE rn = 1
ORDER BY dt DESC;

Результат

+----------------------+--------+------------+
|        Descr         | Price  |     dt     |
+----------------------+--------+------------+
| Active               | 799900 | 2019-02-27 |
| Unknown              | 629900 | 2014-09-12 |
| Sold                 | 625900 | 2014-09-08 |
| Unknown              | 629900 | 2014-08-10 |
| Active               | 629900 | 2014-07-27 |
| Pending              | 629900 | 2014-07-24 |
| Unknown              | 629900 | 2014-07-20 |
| Active               | 629900 | 2014-07-15 |
| Taking Backup Offers | 629900 | 2014-07-11 |
| Taking Backup Offers | 629900 | 2014-06-27 |
| Active               | 629900 | 2014-06-27 |
| Active               | 629900 | 2014-06-10 |
| Sold                 | 288000 | 2000-09-01 |
+----------------------+--------+------------+

Обратите внимание, что, поскольку есть две строки с одинаковой датой 2014-06-27, сервер может вернуть их, как вы показали в ожидаемом результате, или он может вернуть их, как показано здесь. Скорее всего, у вас есть столбец ID, поэтому используйте его для разрешения сортировки.


Чтобы понять, как это работает, запустите промежуточный запрос и изучите его результат (столбцы rn1, rn2, Groups, rn).

WITH
CTE_RN
AS
(
    SELECT
        * 
        ,ROW_NUMBER() OVER (ORDER BY dt DESC) AS rn1
        ,ROW_NUMBER() OVER (PARTITION BY Descr ORDER BY dt DESC) AS rn2
    FROM #temptable
)
,CTE_Groups
AS
(
    SELECT
        *
        ,rn1 - rn2 AS Groups
        ,ROW_NUMBER() OVER (PARTITION BY Descr, rn1 - rn2 ORDER BY dt) AS rn
    FROM CTE_RN
)
SELECT *
FROM CTE_Groups
ORDER BY dt DESC;

Результат

+----------------------+--------+------------+-----+-----+--------+----+
|        Descr         | Price  |     dt     | rn1 | rn2 | Groups | rn |
+----------------------+--------+------------+-----+-----+--------+----+
| Active               | 799900 | 2019-02-27 |   1 |   1 |      0 |  1 |
| Unknown              | 629900 | 2014-09-24 |   2 |   1 |      1 |  2 |
| Unknown              | 629900 | 2014-09-12 |   3 |   2 |      1 |  1 |
| Sold                 | 625900 | 2014-09-08 |   4 |   1 |      3 |  1 |
| Unknown              | 629900 | 2014-08-10 |   5 |   3 |      2 |  1 |
| Active               | 629900 | 2014-07-27 |   6 |   2 |      4 |  1 |
| Pending              | 629900 | 2014-07-25 |   7 |   1 |      6 |  2 |
| Pending              | 629900 | 2014-07-24 |   8 |   2 |      6 |  1 |
| Unknown              | 629900 | 2014-07-20 |   9 |   4 |      5 |  1 |
| Active               | 629900 | 2014-07-16 |  10 |   3 |      7 |  2 |
| Active               | 629900 | 2014-07-15 |  11 |   4 |      7 |  1 |
| Taking Backup Offers | 629900 | 2014-07-11 |  12 |   1 |     11 |  1 |
| Active               | 629900 | 2014-06-28 |  13 |   5 |      8 |  2 |
| Active               | 629900 | 2014-06-27 |  14 |   6 |      8 |  1 |
| Taking Backup Offers | 629900 | 2014-06-27 |  15 |   2 |     13 |  1 |
| Active               | 629900 | 2014-06-23 |  16 |   7 |      9 |  3 |
| Active               | 629900 | 2014-06-11 |  17 |   8 |      9 |  2 |
| Active               | 629900 | 2014-06-10 |  18 |   9 |      9 |  1 |
| Sold                 | 570000 | 2010-01-22 |  19 |   2 |     17 |  2 |
| Sold                 | 288000 | 2000-09-01 |  20 |   3 |     17 |  1 |
+----------------------+--------+------------+-----+-----+--------+----+

Слово предостережения

Добавление ORDER BY dt DESC, rn1 ASC к основному запросу не гарантирует, что он даст ожидаемый результат. rn1 со значениями 14 и 15 можно поменять местами, поскольку их дата (2014-06-27) одинакова. Если даты не уникальны, вам нужен дополнительный уникальный столбец, чтобы сортировка была стабильной и предсказуемой. В данных примера таких столбцов нет, но обычно таблицы имеют уникальный первичный ключ, поэтому его следует использовать.

Итак, для ваших примеров данных вполне нормально, чтобы запрос приводил такой результат:

Intermediate

+----------------------+--------+------------+-----+-----+--------+----+
|        Descr         | Price  |     dt     | rn1 | rn2 | Groups | rn |
+----------------------+--------+------------+-----+-----+--------+----+
| Active               | 799900 | 2019-02-27 |   1 |   1 |      0 |  1 |
| Unknown              | 629900 | 2014-09-24 |   2 |   1 |      1 |  2 |
| Unknown              | 629900 | 2014-09-12 |   3 |   2 |      1 |  1 |
| Sold                 | 625900 | 2014-09-08 |   4 |   1 |      3 |  1 |
| Unknown              | 629900 | 2014-08-10 |   5 |   3 |      2 |  1 |
| Active               | 629900 | 2014-07-27 |   6 |   2 |      4 |  1 |
| Pending              | 629900 | 2014-07-25 |   7 |   1 |      6 |  2 |
| Pending              | 629900 | 2014-07-24 |   8 |   2 |      6 |  1 |
| Unknown              | 629900 | 2014-07-20 |   9 |   4 |      5 |  1 |
| Active               | 629900 | 2014-07-16 |  10 |   3 |      7 |  2 |
| Active               | 629900 | 2014-07-15 |  11 |   4 |      7 |  1 |
| Taking Backup Offers | 629900 | 2014-07-11 |  12 |   1 |     11 |  1 |
| Active               | 629900 | 2014-06-28 |  13 |   5 |      8 |  1 |
| Taking Backup Offers | 629900 | 2014-06-27 |  14 |   2 |     12 |  1 |
| Active               | 629900 | 2014-06-27 |  15 |   6 |      9 |  4 |
| Active               | 629900 | 2014-06-23 |  16 |   7 |      9 |  3 |
| Active               | 629900 | 2014-06-11 |  17 |   8 |      9 |  2 |
| Active               | 629900 | 2014-06-10 |  18 |   9 |      9 |  1 |
| Sold                 | 570000 | 2010-01-22 |  19 |   2 |     17 |  2 |
| Sold                 | 288000 | 2000-09-01 |  20 |   3 |     17 |  1 |
+----------------------+--------+------------+-----+-----+--------+----+

Final

+----------------------+--------+------------+
|        Descr         | Price  |     dt     |
+----------------------+--------+------------+
| Active               | 799900 | 2019-02-27 |
| Unknown              | 629900 | 2014-09-12 |
| Sold                 | 625900 | 2014-09-08 |
| Unknown              | 629900 | 2014-08-10 |
| Active               | 629900 | 2014-07-27 |
| Pending              | 629900 | 2014-07-24 |
| Unknown              | 629900 | 2014-07-20 |
| Active               | 629900 | 2014-07-15 |
| Taking Backup Offers | 629900 | 2014-07-11 |
| Active               | 629900 | 2014-06-28 |
| Taking Backup Offers | 629900 | 2014-06-27 |
| Active               | 629900 | 2014-06-10 |
| Sold                 | 288000 | 2000-09-01 |
+----------------------+--------+------------+

Как видите, этот результат отличается от первого результата, потому что есть две строки с одинаковой датой, и механизм может свободно размещать их в любом порядке.

В этом результате есть Active с другой датой 2014-06-28, потому что Active с 2014-06-27 оказалось ниже Taking Backup Offers 2014-06-27.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...