Opierając się na danych testowych, ale działa to z dowolnymi danymi. Działa to z dowolną liczbą elementów w ciągu.
Zarejestruj typ złożony składający się z jednego text i jedna integer wartość raz na bazę danych. Nazywam to ai :
CREATE TYPE ai AS (a text, i int);
Sztuczka polega na utworzeniu tablicy ai z każdej wartości w kolumnie.
regexp_matches() ze wzorem (\D*)(\d*) i g opcja zwraca jeden wiersz dla każdej kombinacji liter i cyfr. Plus jeden nieistotny wiszący wiersz z dwoma pustymi ciągami '{"",""}' Filtrowanie lub tłumienie tylko zwiększałoby koszty. Zagreguj to do tablicy, po zastąpieniu pustych ciągów ('' ) z 0 w integer komponent (jako '' nie można rzutować na integer ).
NULL wartości najpierw posortuj - lub musisz umieścić je w specjalnej wielkości - lub użyj całego zbioru w STRICT funkcjonować tak, jak proponuje @Craig.
Postgres 9.4 lub nowszy
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<>graj tutaj
Postgres 9.1 (oryginalna odpowiedź)
Testowane z PostgreSQL 9.1.5, gdzie regexp_replace() miał nieco inne zachowanie.
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
Dodaj regexp_replace (left(data, 1), '[1-9]', '0') jako pierwszy ORDER BY element do obsługi wiodących cyfr i pustych ciągów.
Jeśli znaki specjalne, takie jak {}()"', może wystąpić, musisz odpowiednio uciec przed nimi.
Sugestia @Craiga, aby użyć ROW express o to zadba.
BTW, to nie zostanie wykonane w sqlfiddle, ale działa w moim klastrze db. JDBC nie jest do tego zdolne. sqlfiddle narzeka:
Metoda org.postgresql.jdbc3.Jdbc3Array.getArrayImpl(long,int,Map) nie została jeszcze zaimplementowana.
Zostało to naprawione:https://sqlfiddle.com/#!17/fad6e/1