Предложение 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 всегда располагаются в конце. Иными словами, при сортировке по возрастанию они считаются больше всех остальных чисел, а при сортировке по убыванию — меньше остальных.
Для сортировки по значениям String можно указать collation (правила сравнения). Пример: ORDER BY SearchPhrase COLLATE 'tr' — для сортировки по ключевому слову в порядке возрастания с использованием турецкого алфавита, без учета регистра, при условии, что строки представлены в кодировке UTF-8. COLLATE можно независимо указывать или не указывать для каждого выражения в ORDER BY. Если указан ASC или DESC, COLLATE указывается после него. При использовании COLLATE сортировка всегда выполняется без учета регистра.
COLLATE поддерживается в 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. Поэтому запросы к большим объемам данных с небольшим 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 можно применять к полям с типами Numeric (все виды 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 интерпретируется as is для числовых типов, как 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 │ original │
│ 4 │ original │
│ 7 │ original │
└───┴──────────┘
Тот же запрос после применения модификатора 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 │ original │
│ 1.5 │ │
│ 2 │ │
│ 2.5 │ │
│ 3 │ │
│ 3.5 │ │
│ 4 │ original │
│ 4.5 │ │
│ 5 │ │
│ 5.5 │ │
│ 7 │ original │
└─────┴──────────┘
В случае с несколькими полями 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 │ original │
│ 1970-01-01 │ 1970-01-03 │ │
│ 1970-01-01 │ 1970-01-04 │ │
│ 1970-02-10 │ 1970-01-05 │ original │
│ 1970-01-01 │ 1970-01-06 │ │
│ 1970-01-01 │ 1970-01-07 │ │
│ 1970-03-12 │ 1970-01-08 │ original │
└────────────┴────────────┴──────────┘
Поле 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 │ original │
│ 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 │ original │
│ 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 │ original │
└────────────┴────────────┴──────────┘
В следующем запросе используется тип данных 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 │ original │
│ 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 │ original │
│ 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 │ original │
└────────────┴────────────┴──────────┘
Пример запроса без STALENESS:
SELECT number AS key, 5 * number value, 'original' 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, 'original' AS source
FROM numbers(16) WHERE key % 5 == 0
ORDER BY key WITH FILL STALENESS 3;
┌─key─┬─value─┬─source───┐
1. │ 0 │ 0 │ original │
2. │ 1 │ 0 │ │
3. │ 2 │ 0 │ │
4. │ 5 │ 25 │ original │
5. │ 6 │ 0 │ │
6. │ 7 │ 0 │ │
7. │ 10 │ 50 │ original │
8. │ 11 │ 0 │ │
9. │ 12 │ 0 │ │
10. │ 15 │ 75 │ original │
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 │ original │ 1 │
│ 1.5 │ │ 0 │
│ 2 │ │ 0 │
│ 2.5 │ │ 0 │
│ 3 │ │ 0 │
│ 3.5 │ │ 0 │
│ 4 │ original │ 4 │
│ 4.5 │ │ 0 │
│ 5 │ │ 0 │
│ 5.5 │ │ 0 │
│ 7 │ original │ 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 │ original │ 1 │
│ 1.5 │ │ 2 │
│ 2 │ │ 3 │
│ 2.5 │ │ 4 │
│ 3 │ │ 5 │
│ 3.5 │ │ 6 │
│ 4 │ original │ 4 │
│ 4.5 │ │ 5 │
│ 5 │ │ 6 │
│ 5.5 │ │ 7 │
│ 7 │ original │ 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 (включена по умолчанию)
Последнее изменение 10 июня 2026 г.