Wie kann die ursprüngliche Reihenfolge der Elemente in einem nicht verschachtelten Array beibehalten werden?

17

Angesichts der Zeichenfolge:

"Ich denke, dass PostgreSQL geschickt ist"

Ich würde gerne die einzelnen Wörter in dieser Zeichenfolge bearbeiten. Im Wesentlichen habe ich einen separaten Eintrag, aus dem ich Wortdetails abrufen kann, und möchte ein nicht verschachteltes Array dieser Zeichenfolge in diesem Wörterbuch zusammenfügen.

Bisher habe ich:

select word, meaning, partofspeech
from unnest(string_to_array('I think that PostgreSQL is nifty',' ')) as word
from table t
join dictionary d
on t.word = d.wordname;

Dies erfüllt die Grundlagen dessen, was ich mir erhofft hatte, aber es bewahrt nicht die ursprüngliche Wortreihenfolge.

Verwandte Frage:
PostgreSQL unnest () mit Elementnummer

Swasheck
quelle
Möchten Sie eine Zeichenfolge oder eine ganze Tabelle von Zeichenfolgen verarbeiten ? Wenn ja, hat die Tabelle einen Primärschlüssel?
Erwin Brandstetter
@ErwinBrandstetter eine Zeichenfolge in einer Tabelle (die einen Primärschlüssel hat)
Swasheck

Antworten:

22

WITH ORDINALITY in Postgres 9.4 oder später

Das neue Feature vereinfacht diese Art von Problemen. Die obige Abfrage kann nun einfach sein:

SELECT *
FROM   regexp_split_to_table('I think postgres is nifty', ' ') WITH ORDINALITY x(word, rn);

Oder auf eine Tabelle angewendet:

SELECT *
FROM   tbl t, regexp_split_to_table(t.my_column, ' ') WITH ORDINALITY x(word, rn);

Einzelheiten:

Über die implizite LATERALVerknüpfung:

Postgres 9.3 oder älter - und allgemeinere Erklärung

Für eine einzelne Zeichenfolge

Sie können die Fensterfunktion anwenden row_number(), um die Reihenfolge der Elemente zu speichern. Mit dem üblichen row_number() OVER (ORDER BY col)Verfahren erhalten Sie jedoch Zahlen in der Reihenfolge der Sortierung , nicht die ursprüngliche Position in der Zeichenfolge.

Sie könnten versuchen, einfach das wegzulassen ORDER BY, um die Position "wie sie ist" zu erhalten:

SELECT *, row_number() OVER () AS rn
FROM  (
   SELECT regexp_split_to_table('I think postgres is nifty', ' ') AS word
   ) x;

Leistung regexp_split_to_table()degradiert mit langen Strings. unnest(string_to_array(...))skaliert besser:

SELECT *, row_number() OVER () AS rn
FROM  (
   SELECT unnest(string_to_array('I think postgres is nifty', ' ')) AS word
   ) x;

Während dies normalerweise funktioniert und ich noch nie bei einfachen Abfragen einen Bruch gesehen habe, behauptet PostgreSQL nichts in Bezug auf die Reihenfolge der Zeilen ohne einen expliziten ORDER BY.

Um die Ordnungszahl der Elemente in der Originalzeichenfolge zu gewährleisten , verwenden Sie generate_subscript()(verbessert mit dem Kommentar von @deszo):

SELECT arr[rn] AS word, rn
FROM   (
   SELECT *, generate_subscripts(arr, 1) AS rn
   FROM  (
      SELECT string_to_array('I think postgres is nifty', ' ') AS arr
      ) x
   ) y;

Für eine Streichertabelle

In PARTITION BY idauf die OVERKlausel ...

Demo-Tabelle:

CREATE TEMP TABLE strings(string text);
INSERT INTO strings VALUES
  ('I think postgres is nifty')
 ,('And it keeps getting better');

Ich benutze ctidals Ad-hoc-Ersatz für einen Primärschlüssel . Wenn Sie eine (oder eine eindeutige) Spalte haben, verwenden Sie diese stattdessen.

SELECT *, row_number() OVER (PARTITION BY ctid) AS rn
FROM  (
   SELECT ctid, unnest(string_to_array(string, ' ')) AS word
   FROM   strings
   ) x;

Dies funktioniert ohne eindeutige ID:

SELECT arr[rn] AS word, rn
FROM  (
   SELECT *, generate_subscripts(arr, 1) AS rn
   FROM  (
      SELECT string_to_array(string, ' ') AS arr
      FROM   strings
      ) x
   ) y;

SQL-Geige.

Antwort auf Frage

SELECT z.arr, z.rn, z.word, d.meaning   -- , partofspeech -- ?
FROM  (
   SELECT *, arr[rn] AS word
   FROM  (
      SELECT *, generate_subscripts(arr, 1) AS rn
      FROM  (
         SELECT string_to_array(string, ' ') AS arr
         FROM   strings
         ) x
      ) y
   ) z
JOIN   dictionary d ON d.wordname = z.word
ORDER  BY z.arr, z.rn;
Erwin Brandstetter
quelle
1
Sie können auch ausnutzen Pg schrulligen SRF-in-SELECT-Liste Verhalten: SELECT generate_series(1,array_length(word_array,1)), unnest(word_array) FROM ..... 9.3 LATERALbietet möglicherweise bessere Lösungen für dieses Problem.
Craig Ringer
2
Würde das nicht generate_subscripts(arr, 1)funktionieren generate_series(1, array_upper(arr, 1))? Ich würde das erstere aus Gründen der Klarheit vorziehen.
Dezso
@dezso: Guter Punkt. Vereinfacht die Abfrage noch etwas. Ich habe die Antwort entsprechend geändert.
Erwin Brandstetter
1
@Erwin hast du diesen WITH ORDINALITY-Beitrag von depesz gesehen?
Jack Douglas
1
@JackDouglas: Zufällig hatten wir am Freitag eine Diskussion über ein verwandtes Thema , die mich zu einer ähnlichen Entdeckung führte. Ich habe ein bisschen zur Antwort hinzugefügt.
Erwin Brandstetter,