Come conservare l'ordine originale degli elementi in un array non testato?


19

Data la stringa:

'Penso che PostgreSQL sia elegante'

Vorrei operare sulle singole parole trovate all'interno di quella stringa. In sostanza, ho un separato da cui posso ottenere i dettagli delle parole e vorrei unire un array non desiderato di quella stringa su questo dizionario.

Finora ho:

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;

Ciò realizza i fondamenti di ciò che speravo di fare, ma non conserva l'ordine delle parole originale.

Domanda correlata:
PostgreSQL unnest () con numero di elemento


Vuoi elaborare una stringa o un'intera tabella di stringhe ? In tal caso, la tabella ha una chiave primaria?
Erwin Brandstetter,

@ErwinBrandstetter una stringa in una tabella (che ha una chiave primaria)
swasheck

Risposte:


24

WITH ORDINALITY in Postgres 9.4 o successivo

La nuova funzionalità semplifica questa classe di problemi. La query sopra può ora essere semplicemente:

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

Oppure, applicato a una tabella:

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

Dettagli:

Informazioni sul LATERALjoin implicito :

Postgres 9.3 o precedente - e spiegazione più generale

Per una singola stringa

È possibile applicare la funzione finestra row_number()per ricordare l'ordine degli elementi. Tuttavia, con il solito row_number() OVER (ORDER BY col)ottieni numeri in base all'ordinamento , non alla posizione originale nella stringa.

Si potrebbe semplicemente omettere ORDER BYdi ottenere la posizione "così com'è":

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

Prestazioni di regexp_split_to_table()degrado con corde lunghe. unnest(string_to_array(...))scala meglio:

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

Tuttavia, mentre normalmente funziona e non l'ho mai visto interrompere in semplici query, Postgres non afferma nulla sull'ordine delle righe senza un esplicito ORDER BY.

Per garantire numeri ordinali di elementi nella stringa originale, utilizzaregenerate_subscript() (migliorato con il commento di @deszo):

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

Per una tabella di stringhe

Inserisci PARTITION BY id alla OVERclausola ...

Tabella demo:

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

Uso ctidcome sostituto ad-hoc di una chiave primaria . Se ne hai una (o una colonna unica ), usala invece.

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

Questo funziona senza alcun ID distinti:

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 Fiddle.

Rispondi alla domanda

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;

1
È inoltre possibile sfruttare eccentrico comportamento di Pg SRF-in-SELECT-list: SELECT generate_series(1,array_length(word_array,1)), unnest(word_array) FROM ..... 9.3 LATERALpotrebbe fornire soluzioni migliori a questo problema.
Craig Ringer,

2
Non generate_subscripts(arr, 1)funzionerebbe invece generate_series(1, array_upper(arr, 1))? Preferirei il primo per chiarezza.
dezso

1
@Erwin hai visto questo post CON ORDINALITY da depesz?
Jack Douglas,

1
@JackDouglas: Come succede, venerdì abbiamo avuto una discussione su un argomento correlato , che mi ha portato a una scoperta simile. Ho aggiunto un po 'alla risposta.
Erwin Brandstetter,

1
Il link per "dettagli" si collega solo a questa stessa pagina. È confuso.
Wildcard il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.