web-dev-qa-db-fra.com

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

PostgreSQL supporte-t-il 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 quelque chose.

Exemple: http://msdn.Microsoft.com/en-us/library/ms191250.aspx

62
Mike Chamberlain

Jusqu'à Postgres 11 colonnes générées ne sont pas supportés - comme défini dans le standard SQL et implémentés par certains SGBDR, y compris DB2, MySQL et Oracle. Ni les similaires "colonnes calculées" de SQL Server.

La fonctionnalité est en développement pour Postgres 12 , l'auteur principal Peter Eisentraut.

Pour l'instant, vous pouvez émuler avec une fonction en utilisant notation d'attribut (tbl.col) qui ressemble et fonctionne beaucoup comme une colonne générée virtuelle. C'est un peu une bizarrerie syntaxique qui existe dans Postgres pour des raisons historiques et qui convient parfaitement. Cette réponse associée a exemples de code:

L'expression (ressemblant à une colonne) n'est cependant pas incluse dans un 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));

Des alternatives

Sinon, vous pouvez implémenter une fonctionnalité similaire avec un VIEW , éventuellement couplé à des index d'expression. Alors SELECT * peut inclure la colonne générée.

Les colonnes calculées "persistantes" peuvent être implémentées avec triggers de manière fonctionnellement identique.

Les vues matérialisées sont un concept étroitement lié, mis en œuvre depuis Postgres 9.3 .
Dans les versions antérieures, il était possible de gérer les MV de manière manuelle.

80
Erwin Brandstetter

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

Postgresql est nouveau pour moi, mais il semble que vous puissiez créer des colonnes calculées en utilisant un expression index , associé à un view (la vue est facultative, mais rend la vie un peu plus facile).

Supposons que mon calcul est md5(some_string_field), puis je crée l'index comme suit: 

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

Désormais, toutes les requêtes qui agissent sur 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 vous appuyez sur les utilisateurs de la table, sachant exactement comment construire la colonne. Pour vous simplifier la vie, vous pouvez créer une VIEW sur une version augmentée de la table d'origine, en ajoutant à la valeur calculée une 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_augmented pourront utiliser some_string_field_md5 sans se soucier de la façon dont cela fonctionne .. elles obtiennent simplement de bonnes performances. La vue ne copie aucune donnée de la table d'origine. Par conséquent, la mémoire et les performances sont optimales. Notez toutefois que vous ne pouvez pas mettre à jour/insérer dans une vue, mais uniquement dans la table source, mais si vous le souhaitez vraiment, je pense que vous pouvez rediriger les insertions et les mises à jour de la table source à l’aide de rules (je pourrais me tromper ce dernier point que je n’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 l'expression-index du tout. Le choix semble dépendre des données.

21
dan-man

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 activé avant que la ligne ne soit mise à jour ou insérée. Il modifie le champ que nous voulons calculer de l'enregistrement NEW, puis renvoie cet enregistrement.

12
Elmer

PostgreSQL 12 supporte les colonnes générées:

Publication de PostgreSQL 12 Beta 1!

Colonnes Générées

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


colonnes générées

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

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

db <> démo fiddle

5
Lukasz Szozda

J'ai un code qui fonctionne et utilise le terme calculé, je ne suis pas pur sur postgresSQL, mais 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    ;
0
Wired604