Comment convertir un tableau de text
s en un tableau de UUID
s?
Je dois faire un join
entre deux tables: users
et projects
.
La table users
possède un champ de tableau nommé project_ids
contenant les ID de projet sous forme de texte.
La table projects
avait un champ UUID nommé id
.
Mon idée initiale était qu'une requête ressemble à ceci:
SELECT * FROM projects
JOIN users ON
projects.id = ANY(users.project_ids)
Mais cela ne fonctionne pas depuis users.project_ids
ne sont pas UUID
s donc j'ai essayé:
projects.id = ANY(users.project_ids::uuid[])
et même:
projects.id = ANY(ARRAY[users.project_ids]::uuid[])
mais ni l'un ni l'autre ne fonctionne:
ERROR: invalid input syntax for type uuid: ""
MISE À JOUR
@a_horse_with_no_name a définitivement raison. La meilleure option devrait être d'utiliser un tableau d'UUID.
La question est maintenant de savoir comment puis-je modifier un tableau de text
en un tableau de uuid
?
La table users
est actuellement vide (0 enregistrement).
J'ai essayé
ALTER TABLE "users" ALTER COLUMN "project_ids" SET DATA TYPE UUID USING "project_ids"::uuid[];
qui génère
ERREUR: le résultat de la clause USING pour la colonne "product_ids" ne peut pas être converti automatiquement en type uuid. CONSEIL: vous devrez peut-être ajouter une conversion explicite.
ALTER TABLE "utilisateurs" ALTER COLUMN "product_ids" FIXER LE TYPE DE DONNÉES UUID À L'AIDE DE "product_ids" :: UUID;
J'ai aussi essayé
ALTER TABLE "users" ALTER COLUMN "project_ids" SET DATA TYPE UUID[] USING "project_ids"::uuid[];
qui génère
ERREUR: la valeur par défaut de la colonne "project_ids" ne peut pas être convertie automatiquement en type uuid []
La colonne est définie sur un tableau vide par défaut.
J'utilise PG version 10.4 et project_ids
est actuellement text[] nullable
.
Comme cela a été commenté, la colonne project_ids
Devrait être uuid[]
, Ce qui éviterait le problème. Ce serait également plus efficace.
Pour changer (sans données illégales dans la colonne comme vous l'avez affirmé):
ALTER TABLE users ALTER COLUMN project_ids DROP DEFAULT;
ALTER TABLE users
ALTER COLUMN project_ids SET DATA TYPE uuid[] USING project_ids::uuid[];
Vous aviez uuid
au lieu de uuid[]
Par erreur.
Et ça:
ERROR: default for column "product_ids" cannot be cast automatically to type uuid[]
.. signifie que vous avez défini une valeur par défaut pour la colonne. Cette expression ne peut pas être transformée automatiquement. Retirez-le avant de modifier le type. Vous pouvez ajouter un nouveau DEFAULT
plus tard.
La solution efficace dans votre situation d'origine est de supprimer les chaînes vides du tableau avec array_remove()
avant le casting (nécessite Postgres 9.3+):
SELECT *
FROM users u
JOIN projects p ON p.id = ANY(array_remove(u.project_ids, '')::uuid[]);
... après recherche pourquoi il peut y avoir des piqûres vides dans cette colonne text[]
.
En relation:
Le [INNER] JOIN
Dans votre requête supprime les utilisateurs sans projets valides dans projects_ids
Du résultat. En règle générale, vous voudriez garder ceux-là aussi: utilisez plutôt LEFT [OUTER] JOIN
(Avec users
en premier).
Le JOIN
replie les entrées en double dans les deux sens, qui peuvent ou non être comme vous le souhaitez. Si vous souhaitez représenter des entrées en double, désactivez-les avant la jointure.
Et si votre objectif est simplement de résoudre le tableau d'ID en un tableau de noms de projet, vous souhaiterez également conserver l'ordre d'origine des éléments du tableau:
SELECT *
FROM users u
LEFT JOIN LATERAL (
SELECT ARRAY(
SELECT project_name -- use the actual column(s) of your case
FROM unnest (array_remove(u.project_ids, '')::uuid[]) WITH ORDINALITY AS p(id, ord)
JOIN projects USING (id)
ORDER BY ord
)
) p(projects) ON true;
db <> violon ici (librement basé sur violon de McNets )
En relation:
create table users (user_id int, projects text[]); insert into users values (1, (array['a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', 'b0eebc99-9c0b-4ef8-bb6d-cbb9bd380a11'])::text[]); insert into users values (2, (array['a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', 'b0eebc99-9c0b-4ef8-bb6d-cbb9bd380a11', 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'])::text[]); insert into users values (3, (array['f0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', '', 'e0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'])::text[]);
create table projects (project_id uuid); insert into projects values ('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid), ('d0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid), ('e0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid);
Comme a_horse_with_no_name l'a souligné, il échoue si un élément du tableau n'a pas de valeur. Il ne peut pas être converti.
select user_id, project_id from projects join users on project_id = any(projects::uuid[])
ERREUR: syntaxe d'entrée non valide pour le type uuid: ""
Cependant, vous pouvez essayer de caster project_id
comme texte de cette façon:
select user_id, project_id from projects join users on project_id::text = any(projects);
id_utilisateur | project_id ------: | : ----------------------------------- 1 | a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 2 | a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 3 | e0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11
db <> violon ici
Ou vous pouvez étendre le tableau (unnest) et éviter les valeurs vides/nulles:
with usr as ( select user_id, unnest(projects) as project_id from users ) select user_id, projects.project_id from projects join usr on coalesce(usr.project_id, '') <> '' and usr.project_id::uuid = projects.project_id;
id_utilisateur | project_id ------: | : ----------------------------------- 2 | a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 1 | a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 3 | e0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11
db <> violon ici