Перейти к основному содержанию
Перейти к основному содержанию

Оператор ORDER BY

Оператор ORDER BY содержит:

  • список выражений, например ORDER BY visits, search_phrase,
  • список чисел, указывающих на столбцы в операторе SELECT, например ORDER BY 2, 1, или
  • ALL, что означает все столбцы оператора SELECT, например ORDER BY ALL.

Чтобы отключить сортировку по номерам столбцов, установите настройку enable_positional_arguments = 0. Чтобы отключить сортировку по ALL, установите настройку enable_order_by_all = 0.

Оператор ORDER BY может иметь модификаторы DESC (по убыванию) или ASC (по возрастанию), определяющие направление сортировки. Если порядок сортировки явно не указан, по умолчанию используется ASC. Направление сортировки применяется к одному выражению, а не ко всему списку, например ORDER BY Visits DESC, SearchPhrase. Также сортировка выполняется с учетом регистра.

Строки с одинаковыми значениями сортировочных выражений возвращаются в произвольном и недетерминированном порядке. Если оператор ORDER BY опущен в операторе SELECT, порядок строк также является произвольным и недетерминированным.

Сортировка специальных значений

Существует два варианта порядка сортировки значений NaN и NULL:

  • По умолчанию или с модификатором NULLS LAST: сначала значения, затем NaN, затем NULL.
  • С модификатором NULLS FIRST: сначала NULL, затем NaN, затем остальные значения.

Пример

Для таблицы

┌─x─┬────y─┐
│ 1 │ ᴺᵁᴸᴸ │
│ 2 │    2 │
│ 1 │  nan │
│ 2 │    2 │
│ 3 │    4 │
│ 5 │    6 │
│ 6 │  nan │
│ 7 │ ᴺᵁᴸᴸ │
│ 6 │    7 │
│ 8 │    9 │
└───┴──────┘

Выполните запрос SELECT * FROM t_null_nan ORDER BY y NULLS FIRST, чтобы получить:

┌─x─┬────y─┐
│ 1 │ ᴺᵁᴸᴸ │
│ 7 │ ᴺᵁᴸᴸ │
│ 1 │  nan │
│ 6 │  nan │
│ 2 │    2 │
│ 2 │    2 │
│ 3 │    4 │
│ 5 │    6 │
│ 6 │    7 │
│ 8 │    9 │
└───┴──────┘

При сортировке чисел с плавающей запятой значения NaN отделяются от остальных. Независимо от порядка сортировки значения NaN всегда оказываются в конце. Другими словами, при сортировке по возрастанию они ведут себя так, как будто больше всех остальных чисел, а при сортировке по убыванию — так, как будто меньше всех остальных.

Поддержка collation

Для сортировки по значениям типа String вы можете указать collation (правила сравнения). Пример: ORDER BY SearchPhrase COLLATE 'tr' — сортировка по ключевому слову по возрастанию с использованием турецкого алфавита, без учета регистра, при условии, что строки закодированы в UTF-8. COLLATE может быть указан или не указан для каждого выражения в ORDER BY независимо. Если указано ASC или DESC, то COLLATE указывается после него. При использовании COLLATE сортировка всегда выполняется без учета регистра.

Collation поддерживается для типов LowCardinality, Nullable, Array и Tuple.

Мы рекомендуем использовать COLLATE только для окончательной сортировки небольшого количества строк, так как сортировка с COLLATE менее эффективна, чем обычная сортировка по байтам.

Примеры сравнения строк

Пример только со значениями String:

Входная таблица:

┌─x─┬─s────┐
│ 1 │ bca  │
│ 2 │ ABC  │
│ 3 │ 123a │
│ 4 │ abc  │
│ 5 │ BCA  │
└───┴──────┘

Запрос:

SELECT * FROM collate_test ORDER BY s ASC COLLATE 'en';

Результат:

┌─x─┬─s────┐
│ 3 │ 123a │
│ 4 │ abc  │
│ 2 │ ABC  │
│ 1 │ bca  │
│ 5 │ BCA  │
└───┴──────┘

Пример с типом данных Nullable:

Входная таблица:

┌─x─┬─s────┐
│ 1 │ bca  │
│ 2 │ ᴺᵁᴸᴸ │
│ 3 │ ABC  │
│ 4 │ 123a │
│ 5 │ abc  │
│ 6 │ ᴺᵁᴸᴸ │
│ 7 │ BCA  │
└───┴──────┘

Запрос:

SELECT * FROM collate_test ORDER BY s ASC COLLATE 'en';

Результат:

┌─x─┬─s────┐
│ 4 │ 123a │
│ 5 │ abc  │
│ 3 │ ABC  │
│ 1 │ bca  │
│ 7 │ BCA  │
│ 6 │ ᴺᵁᴸᴸ │
│ 2 │ ᴺᵁᴸᴸ │
└───┴──────┘

Пример с типом Array:

Входная таблица:

┌─x─┬─s─────────────┐
│ 1 │ ['Z']         │
│ 2 │ ['z']         │
│ 3 │ ['a']         │
│ 4 │ ['A']         │
│ 5 │ ['z','a']     │
│ 6 │ ['z','a','a'] │
│ 7 │ ['']          │
└───┴───────────────┘

Запрос:

SELECT * FROM collate_test ORDER BY s ASC COLLATE 'en';

Результат:

┌─x─┬─s─────────────┐
│ 7 │ ['']          │
│ 3 │ ['a']         │
│ 4 │ ['A']         │
│ 2 │ ['z']         │
│ 5 │ ['z','a']     │
│ 6 │ ['z','a','a'] │
│ 1 │ ['Z']         │
└───┴───────────────┘

Пример со строкой типа LowCardinality:

Входная таблица:

┌─x─┬─s───┐
│ 1 │ Z   │
│ 2 │ z   │
│ 3 │ a   │
│ 4 │ A   │
│ 5 │ za  │
│ 6 │ zaa │
│ 7 │     │
└───┴─────┘

Запрос:

SELECT * FROM collate_test ORDER BY s ASC COLLATE 'en';

Результат:

┌─x─┬─s───┐
│ 7 │     │
│ 3 │ a   │
│ 4 │ A   │
│ 2 │ z   │
│ 1 │ Z   │
│ 5 │ za  │
│ 6 │ zaa │
└───┴─────┘

Пример с типом Tuple:

┌─x─┬─s───────┐
│ 1 │ (1,'Z') │
│ 2 │ (1,'z') │
│ 3 │ (1,'a') │
│ 4 │ (2,'z') │
│ 5 │ (1,'A') │
│ 6 │ (2,'Z') │
│ 7 │ (2,'A') │
└───┴─────────┘

Запрос:

SELECT * FROM collate_test ORDER BY s ASC COLLATE 'en';

Результат:

┌─x─┬─s───────┐
│ 3 │ (1,'a') │
│ 5 │ (1,'A') │
│ 2 │ (1,'z') │
│ 1 │ (1,'Z') │
│ 7 │ (2,'A') │
│ 4 │ (2,'z') │
│ 6 │ (2,'Z') │
└───┴─────────┘

Детали реализации

ОЗУ расходуется меньше, если помимо ORDER BY указано достаточно маленькое значение LIMIT. В противном случае объём используемой памяти пропорционален объёму данных для сортировки. При распределённой обработке запросов, если GROUP BY опущен, сортировка частично выполняется на удалённых серверах, а результаты объединяются на сервере, инициировавшем запрос. Это означает, что при распределённой сортировке объём данных для сортировки может превышать объём памяти одного сервера.

Если ОЗУ недостаточно, сортировку можно выполнять во внешней памяти (с созданием временных файлов на диске). Для этого используйте настройку max_bytes_before_external_sort. Если она установлена в 0 (значение по умолчанию), внешняя сортировка отключена. Если она включена, то при достижении объёмом данных для сортировки указанного числа байт накопленные данные сортируются и сбрасываются во временный файл. После чтения всех данных все отсортированные файлы объединяются, и результат выводится. Файлы записываются в каталог /var/lib/clickhouse/tmp/ согласно конфигурации (по умолчанию, но вы можете изменить этот путь с помощью параметра tmp_path). Вы также можете использовать сброс на диск только при превышении запросом лимитов памяти, то есть max_bytes_ratio_before_external_sort=0.6 включит сброс на диск только после того, как запрос достигнет 60% лимита памяти (для пользователя/сервера).

Выполнение запроса может потреблять больше памяти, чем max_bytes_before_external_sort. По этой причине это значение должно быть существенно меньше, чем max_memory_usage. Например, если на вашем сервере 128 ГБ ОЗУ и вам нужно выполнить один запрос, установите max_memory_usage в 100 ГБ, а max_bytes_before_external_sort — в 80 ГБ.

Внешняя сортировка работает значительно менее эффективно, чем сортировка в ОЗУ.

Оптимизация чтения данных

Если выражение ORDER BY имеет префикс, который совпадает с ключом сортировки таблицы, вы можете оптимизировать запрос с помощью настройки optimize_read_in_order.

Когда настройка optimize_read_in_order включена, сервер ClickHouse использует индекс таблицы и читает данные в порядке ключа ORDER BY. Это позволяет избежать полного чтения всех данных при указании LIMIT. Таким образом, запросы к большим объёмам данных с небольшим значением лимита обрабатываются быстрее.

Оптимизация работает как с ASC, так и с DESC, но не работает одновременно с оператором GROUP BY и модификатором FINAL.

Когда настройка optimize_read_in_order отключена, сервер ClickHouse не использует индекс таблицы при обработке запросов SELECT.

Рассмотрите возможность ручного отключения optimize_read_in_order при выполнении запросов, которые содержат оператор ORDER BY, большое значение LIMIT и условие WHERE, требующее чтения огромного количества записей до того, как будут найдены запрашиваемые данные.

Оптимизация поддерживается следующими движками таблиц:

В таблицах с движком MaterializedView оптимизация работает с представлениями вида SELECT ... FROM merge_tree_table ORDER BY pk. Однако она не поддерживается в запросах вида SELECT ... FROM view ORDER BY pk, если запрос представления не содержит оператора ORDER BY.

Модификатор ORDER BY Expr WITH FILL

Этот модификатор также может быть использован совместно с модификатором LIMIT ... WITH TIES.

Модификатор WITH FILL может быть задан после ORDER BY expr с необязательными параметрами FROM expr, TO expr и STEP expr. Все пропущенные значения столбца expr будут последовательно заполнены, а остальные столбцы будут заполнены значениями по умолчанию.

Чтобы заполнить несколько столбцов, добавьте модификатор WITH FILL с необязательными параметрами после каждого имени поля в разделе ORDER BY.

ORDER BY expr [WITH FILL] [FROM const_expr] [TO const_expr] [STEP const_numeric_expr] [STALENESS const_numeric_expr], ... exprN [WITH FILL] [FROM expr] [TO expr] [STEP numeric_expr] [STALENESS numeric_expr]
[INTERPOLATE [(col [AS expr], ... colN [AS exprN])]]

WITH FILL может быть применён к полям с числовыми типами (все виды float, decimal, int) или типами Date/DateTime. При применении к полям типа String пропущенные значения заполняются пустыми строками. Когда FROM const_expr не задан, последовательность заполнения начинается с минимального значения поля expr из ORDER BY. Когда TO const_expr не задан, последовательность заполнения заканчивается максимальным значением поля expr из ORDER BY. Когда задан STEP const_numeric_expr, const_numeric_expr интерпретируется без преобразования для числовых типов, как количество дней (days) для типа Date и как количество секунд (seconds) для типа DateTime. Также поддерживается тип данных INTERVAL, представляющий интервалы времени и дат. Когда STEP const_numeric_expr опущен, последовательность заполнения использует 1.0 для числового типа, 1 day для типа Date и 1 second для типа DateTime. Когда задан STALENESS const_numeric_expr, запрос будет генерировать строки до тех пор, пока разница с предыдущей строкой в исходных данных не превысит const_numeric_expr. INTERPOLATE может быть применён к столбцам, не участвующим в ORDER BY WITH FILL. Такие столбцы заполняются на основе значений предыдущих строк путём применения expr. Если expr не указан, будет повторено предыдущее значение. Если список столбцов не задан, будут включены все допустимые столбцы.

Пример запроса без WITH FILL:

SELECT n, source FROM (
   SELECT toFloat32(number % 10) AS n, 'original' AS source
   FROM numbers(10) WHERE number % 3 = 1
) ORDER BY n;

Результат:

┌─n─┬─source───┐
│ 1 │ исходный │
│ 4 │ исходный │
│ 7 │ исходный │
└───┴──────────┘

Тот же запрос, но с модификатором WITH FILL:

SELECT n, source FROM (
   SELECT toFloat32(number % 10) AS n, 'original' AS source
   FROM numbers(10) WHERE number % 3 = 1
) ORDER BY n WITH FILL FROM 0 TO 5.51 STEP 0.5;

Результат:

┌───n─┬─source───┐
│   0 │          │
│ 0.5 │          │
│   1 │ оригинал │
│ 1.5 │          │
│   2 │          │
│ 2.5 │          │
│   3 │          │
│ 3.5 │          │
│   4 │ оригинал │
│ 4.5 │          │
│   5 │          │
│ 5.5 │          │
│   7 │ оригинал │
└─────┴──────────┘

В случае с несколькими полями ORDER BY field2 WITH FILL, field1 WITH FILL порядок заполнения будет соответствовать порядку полей в предложении ORDER BY.

Пример:

SELECT
    toDate((number * 10) * 86400) AS d1,
    toDate(number * 86400) AS d2,
    'original' AS source
FROM numbers(10)
WHERE (number % 3) = 1
ORDER BY
    d2 WITH FILL,
    d1 WITH FILL STEP 5;

Результат:

┌───d1───────┬───d2───────┬─source───┐
│ 1970-01-11 │ 1970-01-02 │ оригинал │
│ 1970-01-01 │ 1970-01-03 │          │
│ 1970-01-01 │ 1970-01-04 │          │
│ 1970-02-10 │ 1970-01-05 │ оригинал │
│ 1970-01-01 │ 1970-01-06 │          │
│ 1970-01-01 │ 1970-01-07 │          │
│ 1970-03-12 │ 1970-01-08 │ оригинал │
└────────────┴────────────┴──────────┘

Поле d1 остаётся незаполненным и принимает значение по умолчанию, поскольку у нас нет повторяющихся значений для d2, и последовательность для d1 не может быть корректно вычислена.

Следующий запрос с изменённым полем в ORDER BY:

SELECT
    toDate((number * 10) * 86400) AS d1,
    toDate(number * 86400) AS d2,
    'original' AS source
FROM numbers(10)
WHERE (number % 3) = 1
ORDER BY
    d1 WITH FILL STEP 5,
    d2 WITH FILL;

Результат:

┌───d1───────┬───d2───────┬─source───┐
│ 1970-01-11 │ 1970-01-02 │ исходный │
│ 1970-01-16 │ 1970-01-01 │          │
│ 1970-01-21 │ 1970-01-01 │          │
│ 1970-01-26 │ 1970-01-01 │          │
│ 1970-01-31 │ 1970-01-01 │          │
│ 1970-02-05 │ 1970-01-01 │          │
│ 1970-02-10 │ 1970-01-05 │ исходный │
│ 1970-02-15 │ 1970-01-01 │          │
│ 1970-02-20 │ 1970-01-01 │          │
│ 1970-02-25 │ 1970-01-01 │          │
│ 1970-03-02 │ 1970-01-01 │          │
│ 1970-03-07 │ 1970-01-01 │          │
│ 1970-03-12 │ 1970-01-08 │ исходный │
└────────────┴────────────┴──────────┘

В следующем запросе используется тип данных INTERVAL с интервалом в 1 день для каждого значения, записываемого в столбец d1:

SELECT
    toDate((number * 10) * 86400) AS d1,
    toDate(number * 86400) AS d2,
    'original' AS source
FROM numbers(10)
WHERE (number % 3) = 1
ORDER BY
    d1 WITH FILL STEP INTERVAL 1 DAY,
    d2 WITH FILL;

Результат:

┌─────────d1─┬─────────d2─┬─source───┐
│ 1970-01-11 │ 1970-01-02 │ оригинал │
│ 1970-01-12 │ 1970-01-01 │          │
│ 1970-01-13 │ 1970-01-01 │          │
│ 1970-01-14 │ 1970-01-01 │          │
│ 1970-01-15 │ 1970-01-01 │          │
│ 1970-01-16 │ 1970-01-01 │          │
│ 1970-01-17 │ 1970-01-01 │          │
│ 1970-01-18 │ 1970-01-01 │          │
│ 1970-01-19 │ 1970-01-01 │          │
│ 1970-01-20 │ 1970-01-01 │          │
│ 1970-01-21 │ 1970-01-01 │          │
│ 1970-01-22 │ 1970-01-01 │          │
│ 1970-01-23 │ 1970-01-01 │          │
│ 1970-01-24 │ 1970-01-01 │          │
│ 1970-01-25 │ 1970-01-01 │          │
│ 1970-01-26 │ 1970-01-01 │          │
│ 1970-01-27 │ 1970-01-01 │          │
│ 1970-01-28 │ 1970-01-01 │          │
│ 1970-01-29 │ 1970-01-01 │          │
│ 1970-01-30 │ 1970-01-01 │          │
│ 1970-01-31 │ 1970-01-01 │          │
│ 1970-02-01 │ 1970-01-01 │          │
│ 1970-02-02 │ 1970-01-01 │          │
│ 1970-02-03 │ 1970-01-01 │          │
│ 1970-02-04 │ 1970-01-01 │          │
│ 1970-02-05 │ 1970-01-01 │          │
│ 1970-02-06 │ 1970-01-01 │          │
│ 1970-02-07 │ 1970-01-01 │          │
│ 1970-02-08 │ 1970-01-01 │          │
│ 1970-02-09 │ 1970-01-01 │          │
│ 1970-02-10 │ 1970-01-05 │ оригинал │
│ 1970-02-11 │ 1970-01-01 │          │
│ 1970-02-12 │ 1970-01-01 │          │
│ 1970-02-13 │ 1970-01-01 │          │
│ 1970-02-14 │ 1970-01-01 │          │
│ 1970-02-15 │ 1970-01-01 │          │
│ 1970-02-16 │ 1970-01-01 │          │
│ 1970-02-17 │ 1970-01-01 │          │
│ 1970-02-18 │ 1970-01-01 │          │
│ 1970-02-19 │ 1970-01-01 │          │
│ 1970-02-20 │ 1970-01-01 │          │
│ 1970-02-21 │ 1970-01-01 │          │
│ 1970-02-22 │ 1970-01-01 │          │
│ 1970-02-23 │ 1970-01-01 │          │
│ 1970-02-24 │ 1970-01-01 │          │
│ 1970-02-25 │ 1970-01-01 │          │
│ 1970-02-26 │ 1970-01-01 │          │
│ 1970-02-27 │ 1970-01-01 │          │
│ 1970-02-28 │ 1970-01-01 │          │
│ 1970-03-01 │ 1970-01-01 │          │
│ 1970-03-02 │ 1970-01-01 │          │
│ 1970-03-03 │ 1970-01-01 │          │
│ 1970-03-04 │ 1970-01-01 │          │
│ 1970-03-05 │ 1970-01-01 │          │
│ 1970-03-06 │ 1970-01-01 │          │
│ 1970-03-07 │ 1970-01-01 │          │
│ 1970-03-08 │ 1970-01-01 │          │
│ 1970-03-09 │ 1970-01-01 │          │
│ 1970-03-10 │ 1970-01-01 │          │
│ 1970-03-11 │ 1970-01-01 │          │
│ 1970-03-12 │ 1970-01-08 │ оригинал │
└────────────┴────────────┴──────────┘

Пример запроса без параметра STALENESS:

SELECT number AS key, 5 * number value, 'оригинал' AS source
FROM numbers(16) WHERE key % 5 == 0
ORDER BY key WITH FILL;

Результат:

    ┌─key─┬─value─┬─source───┐
 1. │   0 │     0 │ original │
 2. │   1 │     0 │          │
 3. │   2 │     0 │          │
 4. │   3 │     0 │          │
 5. │   4 │     0 │          │
 6. │   5 │    25 │ original │
 7. │   6 │     0 │          │
 8. │   7 │     0 │          │
 9. │   8 │     0 │          │
10. │   9 │     0 │          │
11. │  10 │    50 │ original │
12. │  11 │     0 │          │
13. │  12 │     0 │          │
14. │  13 │     0 │          │
15. │  14 │     0 │          │
16. │  15 │    75 │ original │
    └─────┴───────┴──────────┘

Тот же запрос с параметром STALENESS 3:

SELECT number AS key, 5 * number value, 'исходный' AS source
FROM numbers(16) WHERE key % 5 == 0
ORDER BY key WITH FILL STALENESS 3;

Результат:

    ┌─key─┬─value─┬─source───┐
 1. │   0 │     0 │ исходный │
 2. │   1 │     0 │          │
 3. │   2 │     0 │          │
 4. │   5 │    25 │ исходный │
 5. │   6 │     0 │          │
 6. │   7 │     0 │          │
 7. │  10 │    50 │ исходный │
 8. │  11 │     0 │          │
 9. │  12 │     0 │          │
10. │  15 │    75 │ исходный │
11. │  16 │     0 │          │
12. │  17 │     0 │          │
    └─────┴───────┴──────────┘

Пример запроса без использования INTERPOLATE:

SELECT n, source, inter FROM (
   SELECT toFloat32(number % 10) AS n, 'original' AS source, number AS inter
   FROM numbers(10) WHERE number % 3 = 1
) ORDER BY n WITH FILL FROM 0 TO 5.51 STEP 0.5;

Результат:

┌───n─┬─source───┬─inter─┐
│   0 │          │     0 │
│ 0.5 │          │     0 │
│   1 │ оригинал │     1 │
│ 1.5 │          │     0 │
│   2 │          │     0 │
│ 2.5 │          │     0 │
│   3 │          │     0 │
│ 3.5 │          │     0 │
│   4 │ оригинал │     4 │
│ 4.5 │          │     0 │
│   5 │          │     0 │
│ 5.5 │          │     0 │
│   7 │ оригинал │     7 │
└─────┴──────────┴───────┘

Тот же запрос после применения INTERPOLATE:

SELECT n, source, inter FROM (
   SELECT toFloat32(number % 10) AS n, 'original' AS source, number AS inter
   FROM numbers(10) WHERE number % 3 = 1
) ORDER BY n WITH FILL FROM 0 TO 5.51 STEP 0.5 INTERPOLATE (inter AS inter + 1);

Результат:

┌───n─┬─source───┬─inter─┐
│   0 │          │     0 │
│ 0.5 │          │     0 │
│   1 │ оригинал │     1 │
│ 1.5 │          │     2 │
│   2 │          │     3 │
│ 2.5 │          │     4 │
│   3 │          │     5 │
│ 3.5 │          │     6 │
│   4 │ оригинал │     4 │
│ 4.5 │          │     5 │
│   5 │          │     6 │
│ 5.5 │          │     7 │
│   7 │ оригинал │     7 │
└─────┴──────────┴───────┘

Заполнение, сгруппированное по сортировочному префиксу

Иногда полезно заполнять строки, которые имеют одинаковые значения в определённых столбцах, независимо друг от друга — хороший пример — заполнение пропущенных значений во временных рядах. Предположим, что есть следующая таблица временных рядов:

CREATE TABLE timeseries
(
    `sensor_id` UInt64,
    `timestamp` DateTime64(3, 'UTC'),
    `value` Float64
)
ENGINE = Memory;

SELECT * FROM timeseries;

┌─sensor_id─┬───────────────timestamp─┬─value─┐
│       234 │ 2021-12-01 00:00:03.000 │     3 │
│       432 │ 2021-12-01 00:00:01.000 │     1 │
│       234 │ 2021-12-01 00:00:07.000 │     7 │
│       432 │ 2021-12-01 00:00:05.000 │     5 │
└───────────┴─────────────────────────┴───────┘

И мы хотим заполнять пропущенные значения для каждого датчика независимо друг от друга с интервалом 1 секунда. Для этого нужно использовать столбец sensor_id в качестве префикса сортировки при заполнении столбца timestamp:

SELECT *
FROM timeseries
ORDER BY
    sensor_id,
    timestamp WITH FILL
INTERPOLATE ( value AS 9999 )

┌─sensor_id─┬───────────────timestamp─┬─value─┐
│       234 │ 2021-12-01 00:00:03.000 │     3 │
│       234 │ 2021-12-01 00:00:04.000 │  9999 │
│       234 │ 2021-12-01 00:00:05.000 │  9999 │
│       234 │ 2021-12-01 00:00:06.000 │  9999 │
│       234 │ 2021-12-01 00:00:07.000 │     7 │
│       432 │ 2021-12-01 00:00:01.000 │     1 │
│       432 │ 2021-12-01 00:00:02.000 │  9999 │
│       432 │ 2021-12-01 00:00:03.000 │  9999 │
│       432 │ 2021-12-01 00:00:04.000 │  9999 │
│       432 │ 2021-12-01 00:00:05.000 │     5 │
└───────────┴─────────────────────────┴───────┘

Здесь столбец value был заполнен значением 9999, чтобы заполненные строки были более заметны. Это поведение управляется параметром use_with_fill_by_sorting_prefix (включен по умолчанию).