S'appuyant sur vos données de test, mais cela fonctionne avec des données arbitraires. Cela fonctionne avec n'importe quel nombre d'éléments dans la chaîne.
Enregistrez un type composé composé d'un text
et un integer
valeur une fois par base de données. Je l'appelle ai
:
CREATE TYPE ai AS (a text, i int);
L'astuce consiste à former un tableau de ai
de chaque valeur de la colonne.
regexp_matches()
avec le motif (\D*)(\d*)
et le g
L'option renvoie une ligne pour chaque combinaison de lettres et de chiffres. Plus une ligne pendante non pertinente avec deux chaînes vides '{"",""}'
Le filtrer ou le supprimer ne ferait qu'augmenter les coûts. Agrégez ceci dans un tableau, après avoir remplacé les chaînes vides (''
) avec 0
dans le integer
composant (comme ''
ne peut pas être converti en integer
).
NULL
les valeurs sont triées en premier - ou vous devez les caser spécialement - ou utilisez tout le shebang dans un STRICT
fonction comme le propose @Craig.
Postgres 9.4 ou version ultérieure
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<>jouez ici
Postgres 9.1 (réponse originale)
Testé avec PostgreSQL 9.1.5, où regexp_replace()
avait un comportement légèrement différent.
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
Ajouter regexp_replace (left(data, 1), '[1-9]', '0')
comme premier ORDER BY
élément pour prendre soin des premiers chiffres et des chaînes vides.
Si des caractères spéciaux comme {}()"',
peuvent se produire, vous devrez les échapper en conséquence.
Suggestion de @Craig d'utiliser un ROW
l'expression s'en charge.
BTW, cela ne s'exécutera pas dans sqlfiddle, mais c'est le cas dans mon cluster db. JDBC n'est pas à la hauteur. sqlfiddle se plaint :
La méthode org.postgresql.jdbc3.Jdbc3Array.getArrayImpl(long,int,Map) n'est pas encore implémentée.
Cela a depuis été corrigé :http://sqlfiddle.com/#!17/fad6e/1