Colunas computadas / calculadas / virtuais / derivadas no PostgreSQL

113

O PostgreSQL oferece suporte a colunas computadas / calculadas, como o MS SQL Server? Não consigo encontrar nada na documentação, mas como esse recurso está incluído em muitos outros DBMSs, pensei que poderia estar faltando alguma coisa.

Por exemplo: http://msdn.microsoft.com/en-us/library/ms191250.aspx

Mike Chamberlain
fonte
Usando a expressão de subconsulta lateral (recurso Postgres), você pode adicionar facilmente mais colunas a cada linha.
Victor

Respostas:

139

Até Postgres 11 colunas geradas não são suportadas - conforme definido no padrão SQL e implementado por alguns RDBMS incluindo DB2, MySQL e Oracle. Nem as semelhantes "colunas computadas" do SQL Server.

STOREDcolunas geradas são introduzidas com Postgres 12 . Exemplo trivial:

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

db <> fiddle aqui

VIRTUALcolunas geradas podem vir com uma das próximas iterações. (Não no Postgres 13, ainda).

Relacionado:


Até então , você pode emularVIRTUAL colunas geradas com uma função usando notação de atributo ( tbl.col) que parece e funciona muito como uma coluna gerada virtual . Essa é uma peculiaridade de sintaxe que existe no Postgres por razões históricas e que se encaixa no caso. Esta resposta relacionada tem exemplos de código :

A expressão (parecida com uma coluna) não está incluída em a SELECT * FROM tbl, no entanto. Você sempre tem que listar explicitamente.

Também pode ser compatível com um índice de expressão correspondente - desde que a função seja IMMUTABLE. Gostar:

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

Alternativas

Como alternativa, você pode implementar uma funcionalidade semelhante com um VIEW, opcionalmente acoplado a índices de expressão. Em seguida, SELECT *pode incluir a coluna gerada.

As STOREDcolunas computadas "Persisted" ( ) podem ser implementadas com gatilhos de maneira funcionalmente idêntica.

As visualizações materializadas são um conceito intimamente relacionado, implementado desde o Postgres 9.3 .
Em versões anteriores, pode-se gerenciar MVs manualmente.

Erwin Brandstetter
fonte
Dependendo da quantidade de dados que você está carregando de uma vez, o gatilho pode desacelerar as coisas drasticamente. Em vez disso, pode querer considerar atualizações.
sam yi
1
Essas soluções são praticamente inúteis (sem grandes mudanças no código para uma base de código sem casos de teste) ao migrar do oracle para o postgres. Existe alguma solução do ponto de vista da migração?
happybuddha
@happybuddha: Faça sua pergunta como pergunta. Os comentários não são o lugar. Você sempre pode criar um link para esta questão para contexto (e adicionar um comentário aqui para chamar minha atenção e conectar-se à questão relacionada).
Erwin Brandstetter,
4
A funcionalidade está em desenvolvimento agora: commitfest.postgresql.org/16/1443
r90t
1
@cryanbhu: Depende dos detalhes de sua configuração e requisitos. Você pode fazer uma nova pergunta com as informações necessárias.
Erwin Brandstetter,
32

Sim você pode!! A solução deve ser fácil, segura e eficiente ...

Eu sou novo no postgresql, mas parece que você pode criar colunas computadas usando um índice de expressão , emparelhado com uma visão (a visão é opcional, mas torna a vida um pouco mais fácil).

Suponha que meu cálculo seja md5(some_string_field), então eu crio o índice como:

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

Agora, qualquer consulta que atue MD5(some_string_field)usará o índice em vez de computá-lo do zero. Por exemplo:

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

Você pode verificar isso com o explain .

Entretanto, neste ponto, você está contando com os usuários da tabela que sabem exatamente como construir a coluna. Para tornar a vida mais fácil, você pode criar um VIEWem uma versão aumentada da tabela original, adicionando o valor calculado como uma nova coluna:

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

Agora, qualquer consulta usando some_table_augmentedpoderá usar some_string_field_md5sem se preocupar com como funciona ... eles apenas obtêm um bom desempenho. O modo de exibição não copia nenhum dado da tabela original, por isso é bom em termos de memória e também de desempenho. Observe, no entanto, que você não pode atualizar / inserir em uma exibição, apenas na tabela de origem, mas se você realmente quiser, acredito que você pode redirecionar inserções e atualizações para a tabela de origem usando regras (posso estar errado nesse último ponto, pois Nunca tentei sozinho).

Editar: parece que se a consulta envolver índices concorrentes, o motor do planejador pode às vezes nem usar o índice de expressão. A escolha parece ser dependente dos dados.

dan-man
fonte
1
Você poderia explicar ou dar um exemplo de if the query involves competing indices?
dvtan
17

Uma maneira de fazer isso é com um gatilho!

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();

O gatilho é disparado antes que a linha seja atualizada ou inserida. Ele altera o campo que queremos calcular de NEWregistro e, em seguida, retorna esse registro.

Elmer
fonte
Quando o gatilho dispara de forma aguda? Corri acima e fiz isso insert into computed values(1, 2); insert into computed values(4, 8); commit; select * from computed;e acabou de retornar: 1 2 e 4 8
happybuddha
2
tente insert into computed(one) values(1); insert into computed(one) values(4); commit; select * from computed;o valor da twocoluna será calculado automagicamente!
Elmer
8

PostgreSQL 12 suporta colunas geradas:

PostgreSQL 12 Beta 1 lançado!

Colunas Geradas

O PostgreSQL 12 permite a criação de colunas geradas que calculam seus valores com uma expressão usando o conteúdo de outras colunas. Este recurso fornece colunas geradas armazenadas, que são calculadas em inserções e atualizações e são salvas no disco. As colunas geradas virtuais, que são calculadas apenas quando uma coluna é lida como parte de uma consulta, ainda não foram implementadas.


Colunas Geradas

Uma coluna gerada é uma coluna especial que sempre é calculada a partir de outras colunas. Portanto, é para colunas o que uma visão é para tabelas.

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

db <> demonstração de violino

Lukasz Szozda
fonte
1

Bem, não tenho certeza se é isso que você quer dizer, mas Posgres normalmente suporta sintaxe ETL "fictícia". Eu criei uma coluna vazia na tabela e então precisei preenchê-la com registros calculados dependendo dos valores na linha.

UPDATE table01
SET column03 = column01*column02; /*e.g. for multiplication of 2 values*/
  1. É tão fictício que suspeito que não seja o que Você está procurando.
  2. Obviamente, não é dinâmico, você o executa uma vez. Mas nenhum obstáculo para acioná-lo.
ďobo
fonte
0

Eu tenho um código que funciona e uso o termo calculado, não estou no postgresSQL puro embora rodemos no PADB

aqui está como é usado

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    ;
Wired604
fonte
O que é exatamente PADB?
Gherman
O banco de dados analítico do ParAccel é antigo, mas bom ... en.wikipedia.org/wiki/ParAccel
Wired604
Mas como isso se relaciona com uma pergunta sobre Postgres? Claro que há muitos bancos de dados com suporte para colunas computadas.
Gherman
ah desculpe, eu não perdi tempo para voltar ao contexto ... PADB é baseado em postgress!
Wired604
-6

Uma solução leve com restrição de verificação:

CREATE TABLE example (
    discriminator INTEGER DEFAULT 0 NOT NULL CHECK (discriminator = 0)
);
cinereo
fonte
6
Como isso está relacionado ao conceito de coluna calculada? Você se importaria de explicar?
Erwin Brandstetter
4
Concordo, não está diretamente relacionado. Mas é uma substituição para um caso simples quando você só precisa fazer algo parecido field as 1 persisted.
cinereo
2
Uma descrição realmente teria sido boa. Acho que esta resposta é que se o cálculo pode ser feito com a cláusula padrão, você pode usar um padrão e uma restrição de verificação para evitar que alguém altere o valor.
Ross Bradbury
@Ross Bradbury: Concordo, mas isso só funciona para inserção. Não funcionaria se uma coluna dependente fosse atualizada.
Stefan Steiger