20 октября 2026

Использование COLLATION для управления сортировкой текстовых данных

Пять лет тому назад я уже писал о сортировке результата SQL-запроса по строковому полю содержащему комбинацию букв и цифр в "естественном" порядке. Естественный порядок сортировки – это способ упорядочивания строк, при котором вложенные числа рассматриваются как целые числовые значения, а не как последовательность символов. Тогда мной рассматривалось создание функций, которые реализуют такую сортировку под MS SQL Server, Oracle и PostgreSQL. В новом проекте на PostgreSQL мы снова столкнулись с необходимостью сортировки строк в естественном порядке. Когда я предложил разработчикам свою функцию, коллега мне подсказал другое решение – для полей нуждающихся в естественном порядке сортировки создать свой специальный COLLATION.

Слон-бюрократ PostgreSQL: Отклонено! Кто такой kn-true? Принеси мне заполненный бланк CREATE COLLATION!

COLLATION – это набор правил определяющих, как символы сопоставляются друг с другом во время сравнения и сортировки текстовых данных. В стандартной установке PostgreSQL они не имеют поддержки естественной сортировки. Но PostgreSQL позволять создать собственные COLLATION:

CREATE COLLATION [ IF NOT EXISTS ] имя (
  [ LOCALE = локаль, ]
  [ LC_COLLATE = категория_сортировки, ]
  [ LC_CTYPE = категория_типов_символов, ]
  [ PROVIDER = провайдер, ]
  [ DETERMINISTIC = boolean, ]
  [ RULES = правила, ]
  [ VERSION = версия ]
);
Имя COLLATION может быть дополненное схемой. Если схема не указана, то он создается в текущей схеме пользователя. Чтобы вызвать команду пользователю необходимо в целевой схеме иметь право CREATE.

У команды CREATE COLLATION нас интересуют два параметра:

  • PROVIDER – библиотека, предоставляющая правила сортировки. Стандартная библиотека libc не поддерживает естественный порядок сортировки. Поэтому мы должны воспользоваться International Components for Unicode (ICU). ICU – это open-source библиотеки для C/C++ (ICU4C) и Java (ICU4J), которые позволяют программам работать с разными языками, алфавитами, датами и часовыми поясами. Эти библиотеки даже встроены в операционные системы (Windows, Linux, Android, macOS), и используются в подуктах многих известных компаний (Google, Microsoft, Apple, IBM, Oracle, Adobe, Amazon, Intel...) и open-source проектов (Apache, LibreOffice, Mozilla, Wikipedia...). Если при выполнении CREATE COLLATION c PROVIDER = icu возникнет ошибка "ICU is not supported", то значит ваша сборка PostgreSQL была скомпилирована без флага --with-icu.
  • LOCALE – имя локали, которое состоит из языковой метки и набора правил для работы с текстом. Языковые метки определяются стандартом IETF BCP 47, используемым для обозначения человеческих языков и регионов в программном обеспечении. Например: "en" обозначает английский язык, а "en-US" – это вариант английского языка для США (язык дополнен кодом региона).
    На что конкретно влияет языковая метка в LOCALE?
    1. На уникальные буквы и диакритические знаки (умлауты, тильды). В разных языках одни и те же символы сортируются совершенно по-разному. Универсальный язык und (undetermined) использует стандартный порядок Unicode, но в национальных алфавитах правила могут отличаться. Например:
      • Буква Å. В норвежском языке (no) она считается полноценной и стоит в самом конце алфавита (после Z). В локали und или en она будет сортироваться рядом с обычной буквой A.
      • Буква Ö. В немецком языке (de) она обычно сортируется как O, а в шведском (sv) – это отдельная буква и стоит в конце алфавита.
    2. На порядок следования алфавитов в многоязычном тексте. Если в одном поле перемешан текст на разных языках, языковая метка определяет, какой алфавит будет идти первым:
      • Если выбрать ru, то сначала сортируются все символы на кириллице, а затем на латинице.
      • Если выбрать en или und, то сначала пойдет латиница, а кириллица уйдет в конец списка.
    3. На игнорирование или учет специфических символов. В некоторых языках определенные комбинации символов имеют особый приоритет при сравнении, который зашит в используемые ICU языковые стандарты CLDR (Common Locale Data Repository).
    Языковую метку можно дополнить параметрами сортировки с помощью ключа -u, за которым следуют один или несколько параметров правил сортировки и их значения. Библиотека ICU предоставляет широкие возможности для настройки локали: чувствительность (или нечувствительность) к диакритическим знакам, регистру и пунктуации, обработка цифр в тексте и прочее. Для решения нашей задачи воспользуемся параметром "kn" (Keyword Numeric), который включает нужную нам числовую ("естественную") сортировку.

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

CREATE COLLATION IF NOT EXISTS natural_sort_und (PROVIDER = icu, LOCALE = 'und-u-kn-true');
CREATE COLLATION IF NOT EXISTS natural_sort_no (PROVIDER = icu, LOCALE = 'no-u-kn-true');
CREATE COLLATION IF NOT EXISTS natural_sort_en (PROVIDER = icu, LOCALE = 'en-u-kn-true');
CREATE COLLATION IF NOT EXISTS natural_sort_ru (PROVIDER = icu, LOCALE = 'ru-u-kn-true');
Выполним тестовые запросы
SELECT unnest('{10, 20, 100, 10A, 40, 40Б, 40Z, 40Å}'::text[]) AS code;
SELECT unnest('{10, 20, 100, 10A, 40, 40Б, 40Z, 40Å}'::text[]) COLLATE natural_sort_und AS code ORDER BY code;
SELECT unnest('{10, 20, 100, 10A, 40, 40Б, 40Z, 40Å}'::text[]) COLLATE natural_sort_no  AS code ORDER BY code;
SELECT unnest('{10, 20, 100, 10A, 40, 40Б, 40Z, 40Å}'::text[]) COLLATE natural_sort_en  AS code ORDER BY code;
SELECT unnest('{10, 20, 100, 10A, 40, 40Б, 40Z, 40Å}'::text[]) COLLATE natural_sort_ru  AS code ORDER BY code;
и посмотрим, как строки сортируются в зависимости от COLLATION:
 стандартный   natural_sort_und   natural_sort_no   natural_sort_en   natural_sort_ru 
10 10 10 10 10
20 10A 10A 10A 10A
100 20 20 20 20
10A 40 40 40 40
40 40Å 40Z 40Å 40Б
40Б 40Z 40Å 40Z 40Å
40Z 40Б 40Б 40Б 40Z
40Å 100 100 100 100

Что бы в каждом SQL-запросе не указывать COLLATE для поля или в ORDER BY можно указать его для поля при создании таблицы:

CREATE TABLE x
(
  id   SERIAL PRIMARY KEY,
  code VARCHAR(20) COLLATE natural_sort_und,
  name TEXT
);
или для уже существующего поля:
ALTER TABLE y ALTER COLUMN code TYPE VARCHAR(20) COLLATE natural_sort_und;
Учтите, что PostgreSQL не позволит изменить COLLATE поля, если на него завязаны индексы, внешние ключи (foreign keys) или генерируемые поля (GENERATED ALWAYS AS). Сначала их придется удалить.

Итого. Что мы видим? COLLATION в PostgreSQL – это простой в настройке и использовании мощный инструмент в руках разработчика.

Комментариев нет:

Отправить комментарий