É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
WITH ORDINALITY
Dans Postgres 9.4 ou version ultérieureLa 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
:
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;
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;
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;