web-dev-qa-db-fra.com

Comment conserver l'ordre d'origine des éléments dans un tableau non imbriqué?

Étant donné la chaîne:

"Je pense que PostgreSQL est astucieux"

Je voudrais opérer sur les mots individuels trouvés dans cette chaîne. Essentiellement, j'ai un fichier séparé à partir duquel je peux obtenir des détails sur Word et je voudrais rejoindre un tableau non imbriqué de cette chaîne dans ce dictionnaire.

Jusqu'à présent, j'ai:

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;

Cela accomplit les principes fondamentaux de ce que j'espérais faire, mais cela ne préserve pas l'ordre d'origine de Word.

Question connexe:
PostgreSQL unnest () avec le numéro d'élément

19
swasheck

WITH ORDINALITY Dans Postgres 9.4 ou version ultérieure

La nouvelle fonctionnalité simplifie cette classe de problèmes. La requête ci-dessus peut maintenant simplement être:

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

Ou, appliqué à une table:

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

Détails:

À propos de la jointure implicite LATERAL:

Postgres 9.3 ou plus ancien - et explication plus générale

Pour une seule chaîne

Vous pouvez appliquer la fonction de fenêtre row_number() pour mémoriser l'ordre des éléments. Cependant, avec la row_number() OVER (ORDER BY col) habituelle, vous obtenez des nombres selon l'ordre de tri , pas la position d'origine dans la chaîne.

Vous pouvez simplement omettre ORDER BY Pour obtenir la position "en l'état":

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

Les performances de regexp_split_to_table() se dégradent avec de longues chaînes. unnest(string_to_array(...)) évolue mieux:

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

Cependant, alors que cela fonctionne normalement et que je ne l'ai jamais vu casser dans de simples requêtes, Postgres n'affirme rien quant à l'ordre des lignes sans un ORDER BY Explicite.

Pour garantir le nombre ordinal d'éléments dans la chaîne d'origine, utilisez generate_subscript() (amélioré avec le commentaire de @ 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;

Pour une table de cordes

Ajoutez PARTITION BY id À la clause OVER ...

Table de démonstration:

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

J'utilise ctid comme substitut ad hoc pour une clé primaire. Si vous en avez un (ou n'importe quelle colonne unique) utilisez-le à la place.

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

This fonctionne sans identifiant distinct:

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.

Réponse à la question

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;
24
Erwin Brandstetter