PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Humanisierte oder natürliche Zahlensortierung von gemischten Wort-und-Zahlen-Strings

Aufbauend auf Ihren Testdaten funktioniert dies aber mit beliebigen Daten. Dies funktioniert mit einer beliebigen Anzahl von Elementen im String.

Registrieren Sie einen zusammengesetzten Typ, der aus einem text besteht und eine integer Wert einmal pro Datenbank. Ich nenne es ai :

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

Der Trick besteht darin, ein Array von ai zu bilden von jedem Wert in der Spalte.

regexp_matches() mit dem Muster (\D*)(\d*) und das g Die Option gibt eine Zeile für jede Kombination aus Buchstaben und Zahlen zurück. Plus eine irrelevante lose Zeile mit zwei leeren Zeichenfolgen '{"",""}' Das Filtern oder Unterdrücken würde nur zusätzliche Kosten verursachen. Fassen Sie dies in einem Array zusammen, nachdem Sie leere Zeichenfolgen ersetzt haben ('' ) mit 0 in der integer Komponente (als '' kann nicht in integer umgewandelt werden ).

NULL Werte zuerst sortieren - oder Sie müssen sie in Sonderfällen behandeln - oder den ganzen Kram in einem STRICT verwenden funktionieren wie @Craig vorschlägt.

Postgres 9.4 oder höher

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<>hier fummeln

Postgres 9.1 (ursprüngliche Antwort)

Getestet mit PostgreSQL 9.1.5, wobei regexp_replace() hatte ein etwas anderes Verhalten.

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

Fügen Sie regexp_replace (left(data, 1), '[1-9]', '0') hinzu als erstes ORDER BY item, um sich um führende Ziffern und leere Zeichenfolgen zu kümmern.

Falls Sonderzeichen wie {}()"', auftreten können, müssten diese entsprechend maskiert werden.
@Craigs Vorschlag, eine ROW zu verwenden Ausdruck kümmert sich darum.

Übrigens, dies wird nicht in sqlfiddle ausgeführt, aber in meinem db-Cluster. JDBC ist dem nicht gewachsen. sqlfiddle beschwert sich:

Die Methode org.postgresql.jdbc3.Jdbc3Array.getArrayImpl(long,int,Map) ist noch nicht implementiert.

Dies wurde inzwischen behoben:http://sqlfiddle.com/#!17/fad6e/1