web-dev-qa-db-fra.com

Combinez deux colonnes et ajoutez-les dans une nouvelle colonne

Dans PostgreSQL, je veux utiliser une instruction SQL pour combiner deux colonnes et en créer une nouvelle.

Je pense à utiliser concat(...), mais y a-t-il une meilleure façon?
Quelle est la meilleure façon de procéder?

30
Rock

En général, je suis d'accord avec les conseils de @ kgrittn . Fonce.

Mais pour répondre à votre question de base sur concat() : La nouvelle fonction concat() est utile si vous avez besoin de traiter les valeurs nulles - et null n'a été exclu dans votre question ni dans celle à laquelle vous vous référez.

Si vous pouvez exclure les valeurs nulles, le bon ancien opérateur de concaténation (standard SQL) || est toujours le meilleur choix, et réponse de @ luis est très bien:

SELECT col_a || col_b;

Si l'une ou l'autre de vos colonnes peut être nulle, le résultat serait nul dans ce cas. Vous pouvez défendre avec COALESCE:

SELECT COALESCE(col_a, '') || COALESCE(col_b, '');

Mais cela devient vite fastidieux avec plus d'arguments. C'est là que concat() entre en jeu, ce qui jamais renvoie null, pas même si tous les arguments sont null. Par documentation :

Les arguments NULL sont ignorés.

SELECT concat(col_a, col_b);

Le cas de coin restant pour les deux alternatives est où tous les colonnes d'entrée sont nulles auquel cas nous obtenons toujours une chaîne vide '', mais on pourrait vouloir null à la place (du moins je le ferais). Une façon possible:

SELECT CASE
          WHEN col_a IS NULL THEN col_b
          WHEN col_b IS NULL THEN col_a
          ELSE col_a || col_b
       END

Cela devient plus complexe avec plus de colonnes rapidement. Encore une fois, utilisez concat() mais ajoutez une vérification pour la condition spéciale:

SELECT CASE WHEN (col_a, col_b) IS NULL THEN NULL
            ELSE concat(col_a, col_b) END;

Comment ça marche?
(col_a, col_b) Est une notation abrégée pour une expression de type ligne ROW (col_a, col_b). Et un type de ligne n'est nul si tous les colonnes sont nulles. Explication détaillée:

Utilisez également concat_ws() pour ajouter des séparateurs entre les éléments (_ws .. " avec séparateur ").


Une expression comme celle de la réponse de Kevin:

SELECT $1.zipcode || ' - ' || $1.city || ', ' || $1.state;

est fastidieux à préparer pour les valeurs nulles dans PostgreSQL 8.3 (sans concat()). Un moyen (parmi tant d'autres):

SELECT COALESCE(
         CASE
          WHEN $1.zipcode IS NULL THEN $1.city
          WHEN $1.city IS NULL THEN $1.zipcode
          ELSE $1.zipcode || ' - ' || $1.city
         END, '')
       || COALESCE(', ' || $1.state, '');

La volatilité de la fonction est seulement STABLE

Notez cependant que concat() et concat_ws() sont des fonctions STABLE, pas IMMUTABLE car elles peuvent appeler des fonctions de sortie de type de données (comme timestamptz_out) qui dépendent des paramètres régionaux. Explication de Tom Lane.

Cela interdit leur utilisation directe dans les expressions d'index. Si vous savez que le résultat est réellement immuable dans votre cas, vous pouvez contourner cela avec un wrapper de fonction IMMUTABLE. Exemple ici:

66
Erwin Brandstetter

Vous n'avez pas besoin de stocker la colonne pour la référencer de cette façon. Essaye ça:

Installer:

CREATE TABLE tbl
  (zipcode text NOT NULL, city text NOT NULL, state text NOT NULL);
INSERT INTO tbl VALUES ('10954', 'Nanuet', 'NY');

Nous pouvons voir que nous avons "les bonnes choses":

\pset border 2
SELECT * FROM tbl;
 + --------- + -------- + ------- + 
 | code postal | ville | état | 
 + --------- + -------- + ------- + 
 | 10954 | Nanuet | NY | 
 + --------- + -------- + ------- + 

Ajoutez maintenant une fonction avec le "nom de colonne" souhaité qui prend le type d'enregistrement de la table comme seul paramètre:

CREATE FUNCTION combined(rec tbl)
  RETURNS text
  LANGUAGE SQL
AS $$
  SELECT $1.zipcode || ' - ' || $1.city || ', ' || $1.state;
$$;

Cela crée une fonction qui peut être utilisée comme s'il s'agissait d'une colonne de la table, tant que le nom ou l'alias de la table est spécifié, comme ceci:

SELECT *, tbl.combined FROM tbl;

Qui s'affiche comme ceci:

 + --------- + -------- + ------- + ----------------- --- + 
 | code postal | ville | état | combiné | 
 + --------- + -------- + ------- + --------------- ----- + 
 | 10954 | Nanuet | NY | 10954 - Nanuet, NY | 
 + --------- + -------- + ------- + ----------- --------- + 

Cela fonctionne parce que PostgreSQL recherche d'abord une colonne réelle, mais si aucune n'est trouvée et que l'identifiant est qualifié avec un nom de relation ou un alias, il recherche une fonction comme la précédente et l'exécute avec la ligne comme argument, retournant le résultat comme s'il s'agissait d'une colonne. Vous pouvez même indexer sur une telle "colonne générée" si vous le souhaitez.

Étant donné que vous n'utilisez pas d'espace supplémentaire dans chaque ligne pour les données dupliquées ou que vous ne déclenchez pas de déclencheurs sur toutes les insertions et mises à jour, cela peut souvent être plus rapide que les alternatives.

15
kgrittn

Avez-vous vérifié la fonction de concaténation de chaînes? Quelque chose comme:

update table_c set column_a = column_b || column_c 

devrait marcher. Plus ici

13
luis