PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Classificação numérica humanizada ou natural de strings mistas de palavras e números


Com base em seus dados de teste, mas isso funciona com dados arbitrários. Isso funciona com qualquer número de elementos na string.

Registre um tipo composto composto por um text e um integer valor uma vez por banco de dados. Eu chamo de ai :

CREATE TYPE ai AS (a text, i int);

O truque é formar um array de ai de cada valor na coluna.

regexp_matches() com o padrão (\D*)(\d*) e o g A opção retorna uma linha para cada combinação de letras e números. Mais uma linha pendente irrelevante com duas strings vazias '{"",""}' Filtrar ou suprimir apenas aumentaria o custo. Agregue isso em um array, depois de substituir strings vazias ('' ) com 0 no integer componente (como '' não pode ser convertido em integer ).

NULL os valores são classificados primeiro - ou você precisa criá-los em maiúsculas - ou usar todo o shebang em um STRICT funcionar como @Craig propõe.

Postgres 9.4 ou posterior

SELECT data
FROM   alnum
ORDER  BY ARRAY(SELECT ROW(x[1], CASE x[2] WHEN '' THEN '0' ELSE x[2] END)::ai
                FROM regexp_matches(data, '(\D*)(\d*)', 'g') x)
        , data;

db<>mexa aqui

Postgres 9.1 (resposta original)


Testado com PostgreSQL 9.1.5, onde regexp_replace() teve um comportamento um pouco diferente.
SELECT data
FROM  (
    SELECT ctid, data, regexp_matches(data, '(\D*)(\d*)', 'g') AS x
    FROM   alnum
    ) x
GROUP  BY ctid, data   -- ctid as stand-in for a missing pk
ORDER  BY regexp_replace (left(data, 1), '[0-9]', '0')
        , array_agg(ROW(x[1], CASE x[2] WHEN '' THEN '0' ELSE x[2] END)::ai)
        , data         -- for special case of trailing 0

Adicione regexp_replace (left(data, 1), '[1-9]', '0') como primeiro ORDER BY item para cuidar de dígitos iniciais e strings vazias.

Se caracteres especiais como {}()"', pode ocorrer, você teria que escapar deles de acordo.
Sugestão do @Craig para usar um ROW expressão cuida disso.

BTW, isso não será executado no sqlfiddle, mas no meu cluster de banco de dados. JDBC não está à altura disso. sqlfiddle reclama:

O método org.postgresql.jdbc3.Jdbc3Array.getArrayImpl(long,int,Map) ainda não foi implementado.

Isso já foi corrigido:http://sqlfiddle.com/#!17/fad6e/1