Буквенно-цифровая сортировка с помощью PostgreSQL

в базе данных, у меня есть различные буквенно-числовые строки в следующем формате:

10_asdaasda
100_inkskabsjd
11_kancaascjas
45_aksndsialcn
22_dsdaskjca
100_skdnascbka

Я хочу, чтобы они по существу были отсортированы по номеру перед строкой, а затем само имя строки, но, конечно, символы сравниваются один за другим, и поэтому результат порядка по имени производит:

10_asdaasda
100_inkskabsjd
100_skdnascbka
11_kancaascjas
22_dsdaskjca
45_aksndsialcn

вместо заказа я бы предпочел:

10_asdaasda
11_kancaascjas
22_dsdaskjca
45_aksndsialcn
100_inkskabsjd
100_skdnascbka

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

4 ответов


идеальным способом было бы нормализуют ваши данные и разделить два компонента столбца на два отдельных столбца. Один из типа integer, одно text.

С текущей таблицей, вы можете сделать что-то, как показано здесь:

WITH x(t) AS (
    VALUES
     ('10_asdaasda')
    ,('100_inkskabsjd')
    ,('11_kancaascjas')
    ,('45_aksndsialcn')
    ,('22_dsdaskjca')
    ,('100_skdnascbka')
    )
SELECT t
FROM   x
ORDER  BY (substring(t, '^[0-9]+'))::int     -- cast to integer
          ,substring(t, '[^0-9_].*$')        -- works as text

то же самое substring() выражения можно использовать для разделения столбца.

регулярные выражения несколько отказоустойчивость:

  • первый regex выбирает самую длинную числовую строку слева,NULL если цифры не найдены, поэтому приведение к integer не ошибетесь.

  • второе регулярное выражение выбирает остальную часть строки из первого символа, который не является цифрой или"_".

если подчеркивание однозначно как разделитель в любом случае,split_part() быстрее:

ORDER  BY (split_part(t, '_', 1)::int
          ,split_part(t, '_', 2)

ответ на ваш пример

SELECT name
FROM   nametable
ORDER  BY (split_part(name, '_', 1)::int
          ,split_part(name, '_', 2)

есть способ сделать это с показателем за выражение. Это не было бы моим предпочтительным решением (я бы пошел на Брэда), но вы можете создать индекс на следующем выражении (есть больше способов сделать это):

CREATE INDEX idx_name ON table (CAST(SPLIT_PART(columname, '_', 1) AS integer));  

тогда вы можете искать и заказывать по CAST(SPLIT_PART(columname, '_', 1) AS integer) каждый раз, когда вам нужно число перед символом подчеркивания, например:

SELECT * FROM table ORDER BY CAST(SPLIT_PART(columname, '_', 1) AS integer);  

вы можете сделать то же самое со Строковой частью, создав индекс на SPLIT_PART(columname, '_', 2), а затем отсортировать соответственно как что ж.
Однако, как я уже сказал, Я нахожу это решение очень уродливым. Я бы определенно пошел с двумя другими столбцами (один для числа и один для строки), а затем, возможно, даже удалил столбец, который вы упоминаете здесь.


вы можете использовать регулярные выражения с подстроки

   order by substring(column, '^[0-9]+')::int, substring(column, '[^0-9]*$')

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

затем вы можете создать индекс на правильно введенный числовой столбец для сортировки.