Как выполнить стабильную сортировку по нескольким столбцам?

Представьте, что у меня есть набор данных, который содержит:

Date            Id
--------------  ----
11/1/2017       null
11/4/2017       3
11/5/2017       null
11/12/2017      10
null            1
null            2
null            7
null            8
null            9

Я хочу, чтобы строки были упорядочены так, чтобы оба столбца увеличивались.

Используя наивный ORDER BY Date, ID не делает это:

Есть заказ

Существует порядок, который удовлетворяет результатам моего желаемого порядка сортировки:

  • столбец даты всегда увеличивается
  • значение столбца id всегда увеличивается

Или, конечно, это не уникальный порядок:

Date            Id
--------------  ---------------
null            1
11/1/2017       null
null            2
11/4/2017       3
null            7
null            8
null            9
11/5/2017       null
11/12/2017      10

Язык программирования может сделать это

Я знаю, что могу сделать это на стороне клиента. В функционально-функциональном языке программирования: используйте алгоритм стабильной сортировки:

Стабильная сортировка - это та, которая сохраняет исходный порядок входного набора, где алгоритм сравнения не различает два или более элементов.

Рассмотрим алгоритм сортировки, который сортирует карточки по рангу, но не по масти. Стабильная сортировка гарантирует, что первоначальный порядок карт одинакового ранга сохраняется; нестабильная сортировка не будет.

К сожалению у меня есть

  • 9,1 миллиона строк
  • 1,8 ГБ

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

Как я могу выполнить стабильную сортировку в SQL Server?


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

CREATE TABLE #SortDemo (Date datetime NULL, Id int NULL)

INSERT INTO #SortDemo (Date, Id)
VALUES 
    ('20171101', null),
    ('20171104',    3),
    ('20171105', null),
    ('20171112',   10),
    (null,          1),
    (null,          2),
    (null,          7),
    (null,          8),
    (null,          9)


SELECT * FROM #SortDemo
ORDER BY Date, Id

1 ответ

Решение

Это решаемая проблема. SMor не нужно поднимать руки и говорить, что компьютеры не могут быть использованы для решения проблем.

Начните с данных и добавьте новый суррогатный столбец "Ранг".

Date            Id    Rank
--------------  ----  ----
null            7     null
null            1     null
null            9     null
null            2     null  
null            8     null
11/1/2017       null  null
11/4/2017       3     null
11/5/2017       null  null
11/12/2017      10    null
11/13/2017      null  null

Тогда семя Rank к Id:

UPDATE SortDemo SET Rank = Id
WHERE Id IS NOT NULL

Date            Id    Rank
--------------  ----  ----
null            7     7
null            1     1 
null            9     9 
null            2     2 
null            8     8 
11/1/2017       null  null
11/4/2017       3     3
11/5/2017       null  null 
11/12/2017      10    10
11/13/2017      null  null

Затем для оставшихся элементов с датой нам нужно присвоить ему "следующий" ранг:

Date            Id    Rank
--------------  ----  ----
null            7     7
null            1     1 
null            9     9 
null            2     2 
null            8     8 
11/1/2017       null  null  <-- 3
11/4/2017       3     3     
11/5/2017       null  null  <-- 10
11/12/2017      10    10    
11/13/2017      null  null  <-- ?

с

UPDATE SortDemo SET Rank = (
      SELECT MIN(Rank) FROM SortDemo s2 
      WHERE s2.Date >= SortDemo.Date)
WHERE SortDemo.Date IS NOT NULL

Date            Id    Rank
--------------  ----  ----
null            7     7
null            1     1 
null            9     9 
null            2     2 
null            8     8 
11/1/2017       null  3    <--
11/4/2017       3     3     
11/5/2017       null  10   <--
11/12/2017      10    10    
11/13/2017      null  null <-- ?

Есть также крайний случай, когда нет предметов "после" нас; который мы можем исправить, вернувшись назад к одному из самых высоких предыдущих уровней:

UPDATE SortDemo SET Rank = (
      SELECT MAX(Rank) FROM SortDemo s2 
      WHERE s2.Date <= SortDemo.Date)
WHERE SortDemo.Date IS NOT NULL

Date            Id    Rank
--------------  ----  ----
null            7     7
null            1     1 
null            9     9 
null            2     2 
null            8     8 
11/1/2017       null  3
11/4/2017       3     3     
11/5/2017       null  10
11/12/2017      10    10    
11/13/2017      null  10 <--10

И мы сделали

Теперь мы можем сортировать по суррогатным Rankи разрывать связи, сортируя по Date:

SELECT * FROM SortDemo
ORDER BY Rank, Date

Date            Id    Rank
--------------  ----  ----
null            1     1 
null            2     2 
11/1/2017       null  3
11/4/2017       3     3     
null            7     7
null            8     8 
null            9     9 
11/5/2017       null  10
11/12/2017      10    10    
11/13/2017      null  10

Решение завершено. Sql Fiddle

Ответ на условном депонировании до понедельника, поэтому я могу дать людям возможность заработать репутацию для решения уникальной проблемы.

Другие вопросы по тегам