Неправильная сортировка строк в PostgeSql

Рейтинг: 0Ответов: 2Опубликовано: 14.04.2023

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

create table ttt (txt varchar(10));
insert into ttt values ('abc');
insert into ttt values ('a a');
insert into ttt values ('a z');
insert into ttt values ('a/a');
insert into ttt values ('a/z');

select * from ttt order by 1;

Результат как ни странно:

```
 txt
-----
 a a
 a/a
 abc
 a z
 a/z
```

Такое впечатление, что символы ' ' и '/' пропускаются при сортировке. Замечу, что ascii у них меньше чем у букв (32 и 47), т.е. по идее они должны идти раньше. Сначала я это замечал только относительно пробела, но сейчас возникла необходимость сортировать поля path для файлов (строить дерево папок) и проблема, что называется, встала ребром. Кодировка в базе UTF8.

Может кто-нибудь подсказать, как бороться?

Ответы

▲ 5

нельзя просто так взять и отсортировать unicode

Замечу, что ascii у них меньше чем у букв (32 и 47), т.е. по идее они должны идти раньше.

Мысль замечательная, но оно так не работает. При том, это решает и делает вовсе не postgresql. Ваша операционная система эти строки будет сортировать точно так же:

melkij@melkij:~$ echo -e "a/z\nabc\na/b" | sort
a/b
abc
a/z

Потому что postgresql пользуется как раз системными правилами сортировки. Во множественном числе, да. Есть целая группа стандартов одного только unicode, предписывающая как сортировать строки. Откуда процитирую,

Collation is not code point (binary) order.

И что самое интересное, правила сортировки могут и действительно меняются со временем. Одни и те же строки в разных операционный системах или версиях libc могут сортироваться по-разному (что даже даёт проблемы для postgresql)

И далее уже продолжается поддержка разных collate со стороны postgresql: https://www.postgresql.org/docs/current/collation.html

В частности, можно попросить использовать наиболее простую форму collate:

melkij=> select * from ttt order by txt collate "C";
 txt 
-----
 a a
 a z
 a/a
 a/z
 abc
▲ 0

Добавлю, чтобы внести окончательную ясность в вопрос Все-таки, см. https://simply.name/ru/pg-lc-collate.html "linux просто игнорирует символы пробела, - и _ при сортировке" и, видимо, еще некоторые другие не буквенно-цифровые символы. Это видно на примере запроса (последние две строки):

select * from ttt order by txt;
 txt
-----
 /
 e
 r/
 re
 req
 r/z

Моя сортировка path сломалась именно на символе '/'. Но с collate "С" все работает правильно - результат

select * from ttt order by txt collate "C";
 txt
-----
 /
 e
 r/
 r/z
 re
 req