sql >> Base de Datos >  >> RDS >> PostgreSQL

Clasificación de números humanizados o naturales de cadenas mixtas de palabras y números

Sobre la base de sus datos de prueba, pero esto funciona con datos arbitrarios. Esto funciona con cualquier número de elementos en la cadena.

Registre un tipo compuesto formado por un text y un integer valor una vez por base de datos. Yo lo llamo ai :

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

El truco es formar una matriz de ai de cada valor en la columna.

regexp_matches() con el patrón (\D*)(\d*) y el g La opción devuelve una fila para cada combinación de letras y números. Más una fila colgante irrelevante con dos cadenas vacías '{"",""}' Filtrarlo o suprimirlo solo agregaría costos. Agregue esto en una matriz, después de reemplazar las cadenas vacías ('' ) con 0 en el integer componente (como '' no se puede convertir a integer ).

NULL los valores se ordenan primero, o tiene que ponerlos en un caso especial, o usar todo el shebang en un STRICT funcionar como propone @Craig.

Postgres 9.4 o 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<>violín aquí

Postgres 9.1 (respuesta original)

Probado con PostgreSQL 9.1.5, donde regexp_replace() tuvo un comportamiento ligeramente 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

Agregue regexp_replace (left(data, 1), '[1-9]', '0') como primer ORDER BY elemento para ocuparse de los dígitos iniciales y las cadenas vacías.

Si caracteres especiales como {}()"', pueden ocurrir, tendrías que escapar de ellos en consecuencia.
La sugerencia de @Craig de usar una ROW expresión se encarga de eso.

Por cierto, esto no se ejecutará en sqlfiddle, pero sí en mi clúster de base de datos. JDBC no está a la altura. sqlfiddle se queja:

El método org.postgresql.jdbc3.Jdbc3Array.getArrayImpl(long,int,Map) aún no está implementado.

Esto ya se solucionó:http://sqlfiddle.com/#!17/fad6e/1