Colonnes calculées / calculées / virtuelles / dérivées dans PostgreSQL

113

PostgreSQL prend-il en charge les colonnes calculées / calculées, comme MS SQL Server? Je ne trouve rien dans la documentation, mais comme cette fonctionnalité est incluse dans de nombreux autres SGBD, j'ai pensé qu'il me manquait peut-être quelque chose.

Par exemple: http://msdn.microsoft.com/en-us/library/ms191250.aspx

Mike Chamberlain
la source
En utilisant l'expression de sous-requête latérale (fonction Postgres), vous pouvez facilement ajouter plus de colonnes à chaque ligne.
Victor

Réponses:

139

Les colonnes générées jusqu'à Postgres 11 ne sont pas prises en charge - telles que définies dans la norme SQL et implémentées par certains SGBDR, notamment DB2, MySQL et Oracle. Ni les «colonnes calculées» similaires de SQL Server.

STOREDles colonnes générées sont introduites avec Postgres 12 . Exemple trivial:

CREATE TABLE tbl (
  int1    int
, int2    int
, product bigint GENERATED ALWAYS AS (int1 * int2) STORED
);

db <> violon ici

VIRTUALles colonnes générées peuvent être accompagnées de l'une des prochaines itérations. (Pas encore dans Postgres 13).

En relation:


Jusque-là , vous pouvez émuler les VIRTUALcolonnes générées avec une fonction utilisant la notation d'attribut ( tbl.col) qui ressemble et fonctionne un peu comme une colonne générée virtuelle . C'est un peu une bizarrerie de syntaxe qui existe dans Postgres pour des raisons historiques et qui convient parfaitement. Cette réponse associée contient des exemples de code :

L'expression (qui ressemble à une colonne) n'est cependant pas incluse dans a SELECT * FROM tbl. Vous devez toujours le lister explicitement.

Peut également être pris en charge avec un index d'expression correspondant - à condition que la fonction soit IMMUTABLE. Comme:

CREATE FUNCTION col(tbl) ... AS ...  -- your computed expression here
CREATE INDEX ON tbl(col(tbl));

Alternatives

Vous pouvez également implémenter des fonctionnalités similaires avec un VIEW, éventuellement associé à des index d'expression. Puis SELECT *peut inclure la colonne générée.

Les STOREDcolonnes calculées "persistantes" ( ) peuvent être implémentées avec des déclencheurs d'une manière fonctionnellement identique.

Les vues matérialisées sont un concept étroitement lié, implémenté depuis Postgres 9.3 .
Dans les versions antérieures, il était possible de gérer les MV manuellement.

Erwin Brandstetter
la source
En fonction de la quantité de données que vous chargez à la fois, le déclencheur peut ralentir considérablement les choses. Peut vouloir envisager des mises à jour à la place.
sam yi
1
Ces solutions sont pratiquement inutiles (sans d'énormes changements de code dans une base de code sans cas de test) lors de la migration d'oracle vers postgres. Existe-t-il des solutions du point de vue de la migration?
happybuddha
@happybuddha: Veuillez poser votre question comme question. Les commentaires ne sont pas l'endroit. Vous pouvez toujours créer un lien vers cette question pour le contexte (et ajouter un commentaire ici pour attirer mon attention et créer un lien vers la question associée).
Erwin Brandstetter
4
La fonctionnalité est en cours de développement en ce moment: commitfest.postgresql.org/16/1443
r90t
1
@cryanbhu: dépend des détails de votre configuration et de vos exigences. Vous pourriez poser une nouvelle question avec les informations nécessaires.
Erwin Brandstetter
32

Oui, vous pouvez!! La solution doit être simple, sûre et performante ...

Je suis nouveau dans postgresql, mais il semble que vous puissiez créer des colonnes calculées en utilisant un index d'expression , associé à une vue (la vue est facultative, mais rend la vie un peu plus facile).

Supposons que mon calcul soit md5(some_string_field), alors je crée l'index comme:

CREATE INDEX some_string_field_md5_index ON some_table(MD5(some_string_field));

Désormais, toutes les requêtes qui agissent MD5(some_string_field)utiliseront l'index plutôt que de le calculer à partir de zéro. Par exemple:

SELECT MAX(some_field) FROM some_table GROUP BY MD5(some_string_field);

Vous pouvez vérifier cela avec Expliquer .

Cependant, à ce stade, vous comptez sur les utilisateurs de la table sachant exactement comment construire la colonne. Pour vous simplifier la vie, vous pouvez créer un VIEWsur une version augmentée de la table d'origine, en ajoutant la valeur calculée en tant que nouvelle colonne:

CREATE VIEW some_table_augmented AS 
   SELECT *, MD5(some_string_field) as some_string_field_md5 from some_table;

Désormais, toutes les requêtes utilisant some_table_augmentedpourront être utilisées some_string_field_md5sans se soucier de son fonctionnement .. elles obtiennent juste de bonnes performances. La vue ne copie aucune donnée de la table d'origine, elle est donc bonne en termes de mémoire et de performances. Notez cependant que vous ne pouvez pas mettre à jour / insérer dans une vue, uniquement dans la table source, mais si vous le souhaitez vraiment, je pense que vous pouvez rediriger les insertions et les mises à jour vers la table source en utilisant des règles (je peux me tromper sur ce dernier point car Je ne l'ai jamais essayé moi-même).

Edit: il semble que si la requête implique des index concurrents, le moteur de planification peut parfois ne pas utiliser du tout l'index d'expression. Le choix semble dépendre des données.

dan-man
la source
1
Pourriez-vous s'il vous plaît expliquer ou donner un exemple de if the query involves competing indices?
dvtan
17

Une façon de faire est d'utiliser un déclencheur!

CREATE TABLE computed(
    one SERIAL,
    two INT NOT NULL
);

CREATE OR REPLACE FUNCTION computed_two_trg()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
AS $BODY$
BEGIN
    NEW.two = NEW.one * 2;

    RETURN NEW;
END
$BODY$;

CREATE TRIGGER computed_500
BEFORE INSERT OR UPDATE
ON computed
FOR EACH ROW
EXECUTE PROCEDURE computed_two_trg();

Le déclencheur est déclenché avant que la ligne ne soit mise à jour ou insérée. Il modifie le champ que nous voulons calculer d' NEWenregistrement, puis renvoie cet enregistrement.

Elmer
la source
Quand la gâchette se déclenche-t-elle réellement? J'ai couru ce qui précède et je l'ai fait insert into computed values(1, 2); insert into computed values(4, 8); commit; select * from computed;et il vient de revenir: 1 2 et 4 8
happybuddha
2
essayez insert into computed(one) values(1); insert into computed(one) values(4); commit; select * from computed;la valeur de la twocolonne sera calculée automatiquement!
Elmer
8

PostgreSQL 12 prend en charge les colonnes générées:

PostgreSQL 12 Beta 1 est sorti!

Colonnes générées

PostgreSQL 12 permet la création de colonnes générées qui calculent leurs valeurs avec une expression utilisant le contenu d'autres colonnes. Cette fonctionnalité fournit des colonnes générées stockées, qui sont calculées sur les insertions et les mises à jour et sont enregistrées sur le disque. Les colonnes générées virtuelles, qui sont calculées uniquement lorsqu'une colonne est lue dans le cadre d'une requête, ne sont pas encore implémentées.


Colonnes générées

Une colonne générée est une colonne spéciale qui est toujours calculée à partir d'autres colonnes. Ainsi, c'est pour les colonnes ce qu'est une vue pour les tables.

CREATE TABLE people (
    ...,
    height_cm numeric,
    height_in numeric GENERATED ALWAYS AS (height_cm * 2.54) STORED
);

démo db <> fiddle

Lukasz Szozda
la source
1

Eh bien, je ne sais pas si c'est ce que vous voulez dire, mais Posgres supporte normalement la syntaxe ETL "factice". J'ai créé une colonne vide dans le tableau, puis j'ai dû la remplir par des enregistrements calculés en fonction des valeurs de la ligne.

UPDATE table01
SET column03 = column01*column02; /*e.g. for multiplication of 2 values*/
  1. C'est tellement factice que je soupçonne que ce n'est pas ce que vous cherchez.
  2. Evidemment ce n'est pas dynamique, vous l'exécutez une fois. Mais aucun obstacle pour le déclencher.
ďobo
la source
0

J'ai un code qui fonctionne et utilise le terme calculé, je ne suis pas sur postgresSQL pur bien que nous courions sur PADB

voici comment il est utilisé

create table some_table as
    select  category, 
            txn_type,
            indiv_id, 
            accum_trip_flag,
            max(first_true_origin) as true_origin,
            max(first_true_dest ) as true_destination,
            max(id) as id,
            count(id) as tkts_cnt,
            (case when calculated tkts_cnt=1 then 1 else 0 end) as one_way
    from some_rando_table
    group by 1,2,3,4    ;
Filaire604
la source
Qu'est-ce que PADB exactement?
Gherman
Base de données analytique ParAccel c'est vieux mais sympa ... fr.wikipedia.org/wiki/ParAccel
Wired604
Mais comment cela se rapporte-t-il à une question sur Postgres? Bien sûr, il y a beaucoup de bases de données prenant en charge les colonnes calculées.
Gherman
ah désolé je n'ai pas pris le temps de revenir dans le contexte .... PADB est basé sur postgress!
Wired604
-6

Une solution légère avec contrainte Check:

CREATE TABLE example (
    discriminator INTEGER DEFAULT 0 NOT NULL CHECK (discriminator = 0)
);
cinereo
la source
6
Comment cela est-il lié au concept de colonne calculée? Voudriez-vous expliquer?
Erwin Brandstetter
4
D'accord, ce n'est pas directement lié. Mais est une substitution pour un cas simple où vous avez juste besoin de faire quelque chose comme field as 1 persisted.
cinereo
2
Une description aurait en effet été sympa. Je pense que cette réponse est que si le calcul peut être effectué avec la clause par défaut, vous pouvez utiliser une contrainte par défaut et une contrainte de contrôle pour empêcher quiconque de modifier la valeur.
Ross Bradbury
@ Ross Bradbury: D'accord, mais cela ne fonctionne que pour l'insert. Cela ne fonctionnerait pas si une colonne dépendante était mise à jour.
Stefan Steiger