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:http://sqlfiddle.com/#!17/fad6e/1