web-dev-qa-db-fra.com

Casting d'un tableau de textes dans un tableau d'UUID

Comment convertir un tableau de texts en un tableau de UUIDs?

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

5
Sig

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.

Correction du problème d'origine

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:

Points fins

  • 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:

3
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

3
McNets