PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Ordinamento dei numeri umani o naturali di stringhe miste di parole e numeri

Basandosi sui dati del test, ma funziona con dati arbitrari. Funziona con qualsiasi numero di elementi nella stringa.

Registra un tipo composto composto da un text e un integer valore una volta per database. Lo chiamo ai :

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

Il trucco è formare un array di ai da ogni valore nella colonna.

regexp_matches() con lo schema (\D*)(\d*) e il g l'opzione restituisce una riga per ogni combinazione di lettere e numeri. Più una riga penzolante irrilevante con due stringhe vuote '{"",""}' Filtrarlo o sopprimerlo aggiungerebbe solo dei costi. Aggregalo in un array, dopo aver sostituito le stringhe vuote ('' ) con 0 nel integer componente (come '' non può essere trasmesso a integer ).

NULL i valori ordinano per primi - o devi metterli in un caso speciale - o usa l'intero shebang in un STRICT funzione come propone @Craig.

Postgres 9.4 o successivo

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<>gioca qui

Postgres 9.1 (risposta originale)

Testato con PostgreSQL 9.1.5, dove regexp_replace() aveva un comportamento leggermente diverso.

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

Aggiungi regexp_replace (left(data, 1), '[1-9]', '0') come primo ORDER BY elemento per occuparsi delle cifre iniziali e delle stringhe vuote.

Se caratteri speciali come {}()"', può verificarsi, dovresti evitarli di conseguenza.
Suggerimento di @Craig per utilizzare una ROW l'espressione si occupa di questo.

BTW, questo non verrà eseguito in sqlfiddle, ma lo fa nel mio cluster db. JDBC non è all'altezza. sqlfiddle si lamenta:

Il metodo org.postgresql.jdbc3.Jdbc3Array.getArrayImpl(long,int,Map) non è ancora implementato.

Da allora è stato risolto:http://sqlfiddle.com/#!17/fad6e/1