sql >> Databáze >  >> RDS >> PostgreSQL

Humanizované nebo přirozené třídění smíšených slovních a číselných řetězců

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



  1. Převod datového typu datetime2 na datový typ datetime má za následek hodnotu mimo rozsah

  2. Nasaďte více výpočetních instancí Oracle pomocí fondu instancí a terraformu

  3. Jak nastavit hodnotu pole složené proměnné pomocí dynamického SQL

  4. Proč dostávám parametr 2 nemohu předat chybou odkazu, když používám bindParam s konstantní hodnotou?