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

PostgreSQL unnest() mit Elementnummer

Postgres 9.4 oder höher

Verwenden Sie WITH ORDINALITY für satzrückgebende Funktionen:

Wenn eine Funktion im FROM -Klausel wird durch WITH ORDINALITY angehängt , einbigint Spalte wird an die Ausgabe angehängt, die bei 1 beginnt und für jede Zeile der Ausgabe der Funktion um 1 erhöht wird. Dies ist am nützlichsten im Fall von Set-Rückgabefunktionen wie unnest() .

In Kombination mit LATERAL Funktion in pg 9.3+, und laut diesem Thread zu pgsql-hackers kann die obige Abfrage jetzt geschrieben werden als:

SELECT t.id, a.elem, a.nr
FROM   tbl AS t
LEFT   JOIN LATERAL unnest(string_to_array(t.elements, ','))
                    WITH ORDINALITY AS a(elem, nr) ON TRUE;

LEFT JOIN ... ON TRUE behält alle Zeilen in der linken Tabelle bei, auch wenn der Tabellenausdruck rechts keine Zeilen zurückgibt. Wenn das keine Rolle spielt, können Sie dieses ansonsten äquivalente, weniger ausführliche verwenden Form mit einem impliziten CROSS JOIN LATERAL :

SELECT t.id, a.elem, a.nr
FROM   tbl t, unnest(string_to_array(t.elements, ',')) WITH ORDINALITY a(elem, nr);

Oder einfacher, wenn es auf einem tatsächlichen Array basiert (arr eine Array-Spalte sein):

SELECT t.id, a.elem, a.nr
FROM   tbl t, unnest(t.arr) WITH ORDINALITY a(elem, nr);

Oder sogar mit minimaler Syntax:

SELECT id, a, ordinality
FROM   tbl, unnest(arr) WITH ORDINALITY a;

a ist automatisch Tabelle und Spalten-Alias. Der Standardname der hinzugefügten Ordinalitätsspalte ist ordinality . Aber es ist besser (sicherer, sauberer), explizite Spaltenaliase und Tabellenqualifizierungsspalten hinzuzufügen.

Postgres 8.4–9.3

Mit row_number() OVER (PARTITION BY id ORDER BY elem) Sie erhalten Zahlen gemäß der Sortierreihenfolge, nicht die Ordnungszahl der ursprünglichen Ordnungsposition in der Zeichenfolge.

Sie können ORDER BY einfach weglassen :

SELECT *, row_number() OVER (PARTITION by id) AS nr
FROM  (SELECT id, regexp_split_to_table(elements, ',') AS elem FROM tbl) t;

Während dies normalerweise funktioniert und ich noch nie gesehen habe, dass es bei einfachen Abfragen fehlgeschlagen ist, sagt PostgreSQL nichts über die Reihenfolge der Zeilen ohne ORDER BY aus . Es funktioniert aufgrund eines Implementierungsdetails.

Um Ordnungszahlen zu garantieren von Elementen in der durch Leerzeichen getrennten Zeichenfolge :

SELECT id, arr[nr] AS elem, nr
FROM  (
   SELECT *, generate_subscripts(arr, 1) AS nr
   FROM  (SELECT id, string_to_array(elements, ' ') AS arr FROM tbl) t
   ) sub;

Oder einfacher, wenn es auf einem tatsächlichen Array basiert :

SELECT id, arr[nr] AS elem, nr
FROM  (SELECT *, generate_subscripts(arr, 1) AS nr FROM tbl) t;

Zugehörige Antwort auf dba.SE:

  • Wie bewahrt man die ursprüngliche Reihenfolge der Elemente in einem nicht verschachtelten Array?

Postgres 8.1–8.4

Keine dieser Funktionen ist noch verfügbar:RETURNS TABLE , generate_subscripts() , unnest() , array_length() . Aber das funktioniert:

CREATE FUNCTION f_unnest_ord(anyarray, OUT val anyelement, OUT ordinality integer)
  RETURNS SETOF record
  LANGUAGE sql IMMUTABLE AS
'SELECT $1[i], i - array_lower($1,1) + 1
 FROM   generate_series(array_lower($1,1), array_upper($1,1)) i';

Beachten Sie insbesondere, dass der Array-Index von den Ordinalpositionen der Elemente abweichen kann. Betrachten Sie diese Demo mit erweiterter Funktion :

CREATE FUNCTION f_unnest_ord_idx(anyarray, OUT val anyelement, OUT ordinality int, OUT idx int)
  RETURNS SETOF record
  LANGUAGE sql IMMUTABLE AS
'SELECT $1[i], i - array_lower($1,1) + 1, i
 FROM   generate_series(array_lower($1,1), array_upper($1,1)) i';

SELECT id, arr, (rec).*
FROM  (
   SELECT *, f_unnest_ord_idx(arr) AS rec
   FROM  (VALUES (1, '{a,b,c}'::text[])  --  short for: '[1:3]={a,b,c}'
               , (2, '[5:7]={a,b,c}')
               , (3, '[-9:-7]={a,b,c}')
      ) t(id, arr)
   ) sub;

 id |       arr       | val | ordinality | idx
----+-----------------+-----+------------+-----
  1 | {a,b,c}         | a   |          1 |   1
  1 | {a,b,c}         | b   |          2 |   2
  1 | {a,b,c}         | c   |          3 |   3
  2 | [5:7]={a,b,c}   | a   |          1 |   5
  2 | [5:7]={a,b,c}   | b   |          2 |   6
  2 | [5:7]={a,b,c}   | c   |          3 |   7
  3 | [-9:-7]={a,b,c} | a   |          1 |  -9
  3 | [-9:-7]={a,b,c} | b   |          2 |  -8
  3 | [-9:-7]={a,b,c} | c   |          3 |  -7

Vergleichen Sie:

  • Normalisieren Sie Array-Indizes für eindimensionale Arrays, sodass sie mit 1 beginnen