PostgreSQL ORDER BY проблема - естественная сортировка - PullRequest
11 голосов
/ 07 февраля 2012

У меня проблема Postgres ORDER BY со следующей таблицей:

em_code  name
EM001    AAA
EM999    BBB
EM1000   CCC

Чтобы вставить новую запись в таблицу,

  1. Я выбираю последнюю записьс SELECT * FROM employees ORDER BY em_code DESC
  2. Уберите алфавиты из em_code, используя reg exp, и сохраните в ec_alpha
  3. Приведите оставшуюся часть к целому числу ec_num
  4. Увеличивайте на единицу ec_num++
  5. Пэд с достаточным количеством нулей и префиксом ec_alpha снова

Когда em_code достигает EM1000, вышеприведенный алгоритм дает сбой.

Первый шаг вернет EM999 вместо EM1000и он снова будет генерировать EM1000 как новый em_code, нарушая ограничение уникального ключа.

Любая идея, как выбрать EM1000?

Ответы [ 6 ]

9 голосов
/ 07 февраля 2012

Причина в том, что строка сортируется в алфавитном порядке (вместо того, чтобы численно, как вы хотели бы) и 1 сортирует до 9. Вы можете решить это так:

SELECT * FROM employees ORDER BY substring(em_code, 3)::int DESC

Было бы эффективнее удалить избыточный 'EM' из вашего em_code - если вы можете - и сохранить целое число для начала.


Дополнительный ответ на вопрос в комментарии

Чтобы удалить все и не все цифры из строки:

SELECT regexp_replace(em_code, E'\\D','','g')
FROM employees

\D - это регулярное выражение Сокращение класса для "не цифр".
'g' в качестве 4-го параметра - это глобальный переключатель, который применяет замену к каждому вхождению в строке, а не только к первому.

Таким образом, я заменяю каждую нецифровую пустую строку, выделяя только цифры из строки.

5 голосов
/ 15 февраля 2018

Один из подходов, который вы можете использовать, - создать для этого naturalsort функцию. Вот пример, написанный легендой Postgres RhodiumToad .

create or replace function naturalsort(text)
    returns bytea language sql immutable strict as $f$
    select string_agg(convert_to(coalesce(r[2], length(length(r[1])::text) || length(r[1])::text || r[1]), 'SQL_ASCII'),'\x00')
    from regexp_matches($1, '0*([0-9]+)|([^0-9]+)', 'g') r;
$f$;

Источник: http://www.rhodiumtoad.org.uk/junk/naturalsort.sql

Чтобы использовать его, просто вызовите функцию в вашем заказе:

SELECT * FROM employees ORDER BY naturalsort(em_code) DESC
4 голосов
/ 25 августа 2016

Это всегда возникает в вопросах и в моем собственном развитии, и я, наконец, устал от хитрых способов сделать это.Я наконец сломался и реализовал его как расширение PostgreSQL:

https://github.com/Bjond/pg_natural_sort_order

Это бесплатно для использования, лицензия MIT.

В основном это просто нормализует числовые значения (ноль до-буквенные числа) внутри строк, так что вы можете создать индексный столбец для полной скорости сортировки естественным образом.Readme объясняет.

Преимущество в том, что вы можете иметь триггер, а не код вашего приложения.Он будет рассчитан на скорости компьютера на сервере PostgreSQL, и миграции с добавлением столбцов станут простыми и быстрыми.

3 голосов
/ 27 августа 2014

вы можете использовать только эту строку "ORDER BY length (подстрока (em_code FROM '[0-9] +')), em_code"

1 голос
/ 20 июля 2013

Я подробно писал об этом в этом связанном вопросе:

Гуманизированная или натуральная числовая сортировка смешанных строк из слов и цифр

(я пишуэтот ответ только в качестве полезной перекрестной ссылки, так что это вики сообщества).

0 голосов
/ 14 декабря 2017

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

https://stackoverflow.com/a/47522040/935122

Я также поместил его на GitHub

https://github.com/ccsalway/dbNaturalSort

...