Vychází z vašich testovacích dat, ale funguje to s libovolnými daty. Funguje to s libovolným počtem prvků v řetězci.
Zaregistrujte složený typ složený z jednoho text a jedno integer hodnotu jednou za databázi. Říkám tomu ai :
CREATE TYPE ai AS (a text, i int);
Trik spočívá ve vytvoření pole ai z každé hodnoty ve sloupci.
regexp_matches() se vzorem (\D*)(\d*) a g volba vrátí jeden řádek pro každou kombinaci písmen a číslic. Plus jeden irelevantní visící řádek se dvěma prázdnými řetězci '{"",""}' Filtrování nebo potlačení by jen zvýšilo náklady. Po nahrazení prázdných řetězců to agregujte do pole ('' ) s 0 v integer komponenta (jako '' nelze přetypovat na integer ).
NULL hodnoty nejprve seřaďte - nebo je musíte zadat speciálním případem - nebo použijte celý shebang v STRICT fungovat jako @Craig navrhuje.
Postgres 9.4 nebo novější
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<>zde hrajte
Postgres 9.1 (původní odpověď)
Testováno s PostgreSQL 9.1.5, kde regexp_replace() měl trochu jiné chování.
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
Přidejte regexp_replace (left(data, 1), '[1-9]', '0') jako první ORDER BY položka, která se postará o úvodní číslice a prázdné řetězce.
Pokud se jedná o speciální znaky jako {}()"', mohou nastat, museli byste jim odpovídajícím způsobem uniknout.
Návrh @Craiga použít ROW výraz se o to postará.
BTW, toto se nespustí v sqlfiddle, ale ano v mém db clusteru. JDBC na to nemá. sqlfiddle si stěžuje:
Metoda org.postgresql.jdbc3.Jdbc3Array.getArrayImpl(long,int,Map) ještě není implementována.
Toto bylo od té doby opraveno:https://sqlfiddle.com/#!17/fad6e/1