PostgreSQL: colonnes générées

16

PostgreSQL prend-il en charge les colonnes générées ? Aussi connu sous le nom de colonnes virtuelles . Je ne parle pas de IDENTITYcolonnes .

Je ne trouve aucune information sur cette fonctionnalité remarquable mais je sais qu'elle est disponible sur SQL Server et dans les dernières versions de MariaDB & MySQL.

Cette fonctionnalité est mentionnée dans la norme SQL: 2003 , et il y a eu des discussions sur les forums PostgreSQL vers 2006, mais je ne trouve rien de substantiel à ce sujet.

Il y a une discussion sur SO, mais elle est assez ancienne maintenant, donc elle pourrait bien être obsolète.

Manngo
la source
2
Cette réponse connexe de 2012 sur SO peut être utile: stackoverflow.com/questions/11165450/… Toujours valide.
Erwin Brandstetter
@ErwinBrandstetter Désolé d'avoir raté ce commentaire. C'est une astuce utile. Merci.
Manngo

Réponses:

17

Je ne sais pas si c'est ce que vous voulez, mais la notation d'attribut row.full_nameet la notation de fonction full_name(row)sont équivalentes dans postgresql.

Cela signifie que vous prenez une table

CREATE TABLE people (
  first_name text,
  last_name text
);

et une fonction:

CREATE FUNCTION full_name(people) RETURNS text AS $$
  SELECT $1.first_name || ' ' || $1.last_name;
$$ LANGUAGE SQL;

et appelez-le comme ceci:

select full_name from people

Est-ce ce dont vous avez besoin?

Pour accélérer les choses, vous pouvez créer un index d'expression:

CREATE INDEX people_full_name_idx ON people
USING GIN (to_tsvector('english', full_name(people)));

Ou tout stocker dans une vue matérialisée.

Exemple tiré d'ici: http://bernardoamc.github.io/sql/2015/05/11/postgres-virtual-columns/

Fabian Zeindl
la source
2
Ceci est la bonne réponse. Voir, par exemple, comment Postgrest se réfère à ce comportement en tant que "colonnes calculées".
fiatjaf
Typo, je pense - la sélection devrait être de select people.full_name from peopleou select full_name(people) from people?
Barguast
Non ça marche comme ça. Le préfixe dans "select people.full_name from people" peut être omis comme dans le SQL standard.
Fabian Zeindl
J'ai raté cette réponse, venant, pour ainsi dire, longtemps après que j'avais abandonné. Merci pour la suggestion.
Manngo
1
Pourriez-vous alors modifier la réponse acceptée?
Fabian Zeindl
6

Non, ceci n'est actuellement pas pris en charge (à partir de Postgres 9.6).

La seule solution consiste à utiliser un déclencheur ou une vue s'il s'agit d'un simple calcul que vous n'avez pas besoin d'indexer.

un cheval sans nom
la source
Les rats. Je suppose que je pourrais opter pour une vue matérialisée si j'ai besoin de la performance. J'ai ajouté une demande pour la fonctionnalité, car elle est déjà disponible dans la compétition.
Manngo
1
Pas besoin de MVIEW. Une colonne avec un déclencheur vous permettra également d'indexer le contenu de la colonne
a_horse_with_no_name
J'ai un problème philosophique avec le stockage de vraies colonnes supplémentaires qui sont essentiellement une répétition des autres données. Il dénormalise la table.
Manngo
5
Eh bien, une colonne calculée est exactement cela: stocker des données dénormalisées. La façon dont la valeur de la colonne calculée est générée n'a pas d'importance. Je ne vois pas de différence conceptuelle entre une "vraie" colonne calculée et celle qui est générée via un déclencheur
a_horse_with_no_name
Une autre solution (dans certains cas) consiste à indexer une expression.
ypercubeᵀᴹ
5

Oui: GENERATED ALWAYS AS … STORED

Postgres 12 ajoute la fonctionnalité pour les colonnes générées, comme mentionné dans la norme SQL: 2003 .

La valeur est générée au moment d'un INSERTou UPDATE, puis stockée avec la ligne comme toute autre valeur.

Un élément généré doit être basé sur une colonne de base de la même table ou sur une fonction immuable .

La syntaxe est simple, une clause sur CREATE TABLE:

GENERATED ALWAYS AS ( generation_expr ) STORED 

Exemple:

CREATE TABLE people (
    ...,
    height_cm NUMERIC,
    height_in NUMERIC GENERATED ALWAYS AS ( height_cm / 2.54 ) STORED
);

Fonctionnalités:

  • Peut être indexé.
  • Fait partie de la norme SQL.

Mises en garde:

  • Basé sur les colonnes de la même table (tables non liées)
  • Non autorisé pour le partitionnement (ne peut pas faire partie d'une clé de partition)
  • Les données sont toujours écrites sur la ligne, prenant de l'espace de stockage
    • La future fonctionnalité pourrait offrir VIRTUAL pour les valeurs calculées à la volée sans stockage
  • Génération profonde unique (utilisez la colonne de base, pas une autre colonne générée)
  • Il n'y a pas de GÉNÉRÉ PAR DÉFAUT (vous ne pouvez pas remplacer la valeur)
  • Impossible d'accéder à gen-col dans le déclencheur AVANT (valeur non encore déterminée)
  • Les fonctions doivent être immuables

Voir:

Basil Bourque
la source
Merci pour cette information. Je vois que la version 12 n'est pas encore complètement publiée, mais je l'attends avec impatience. Je note que PostgreSQL utilise la syntaxe la plus standard, mais est par ailleurs la même que MSSQL. J'ai trouvé les spécifications SQL2003 ici: sigmodrecord.org/publications/sigmodRecord/0403/… . J'ai toujours dit que SQL est une norme évoluant très lentement et que les implémentations de SGBD sont même lentes.
Manngo
0

Selon votre cas d'utilisation, vous pouvez obtenir ce type de comportement en déclarant une nouvelle colonne et en la remplissant avec un déclencheur lors de l'insertion / mise à jour.

J'utiliserais les réponses ci-dessus si possible pour éviter de dupliquer les données qui pourraient être dérivées de ce que vous avez déjà, mais cela fait l'affaire et pourrait être utile pour les champs dérivés à forte intensité de calcul que vous souhaitez calculer une fois et enregistrer.

J'ai considéré cette approche pour résoudre un problème où je n'avais parfois que 15 chiffres d'une clé à 18 chiffres (les 3 derniers chiffres ne sont qu'une somme de contrôle), mais je voulais pouvoir imposer une relation de clé étrangère.

Documents PG sur les déclencheurs: https://www.postgresql.org/docs/9.6/sql-createtrigger.html

Exemple W3: https://www.w3resource.com/PostgreSQL/postgresql-triggers.php

Allen Phelps
la source