AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 217781
Accepted
exhuma
exhuma
Asked: 2018-09-18 04:21:36 +0800 CST2018-09-18 04:21:36 +0800 CST 2018-09-18 04:21:36 +0800 CST

Como posso escrever um gatilho "on-update" que recebe um nome de tabela como argumento?

  • 772

Observação : o exemplo a seguir é um código fortemente simplificado, mas ilustra a tarefa em um exemplo reproduzível. Olhar para este exemplo pode fazer você pensar "por que fazer assim"? Na realidade, a tarefa completa é armazenar uma trilha de auditoria para várias tabelas, mas somente se determinadas condições forem atendidas. A condição é a mesma para cada tabela (cada uma delas compartilha algumas colunas como inserted, updatede assim por diante). Portanto, o código para armazenar uma trilha de auditoria é o mesmo para cada tabela. Mas as colunas reais a serem copiadas são diferentes a cada vez. Eu quis criar um gatilho que lidasse com isso dinamicamente para que eu não precisasse tocá-lo toda vez que o esquema fosse alterado.


Considere o seguinte exemplo de trabalho (pergunta abaixo). Isso demonstra um esquema simples em que cada atualização na tabela datafaz com que os valores antigos sejam movidos para data2:

DROP TABLE IF EXISTS data CASCADE;
DROP TABLE IF EXISTS data2 CASCADE;
CREATE TABLE data (
    id SERIAL,
    name TEXT,
    updated TIMESTAMP WITH TIME ZONE
);
CREATE TABLE data2 (
    id SERIAL,
    name TEXT,
    updated TIMESTAMP WITH TIME ZONE
);

CREATE OR REPLACE FUNCTION update_trigger_func()
    RETURNS TRIGGER AS $$
    BEGIN
        NEW.updated = NOW();
        INSERT INTO data2 VALUES (OLD.*);
        RETURN NEW;
    END;
    $$ language 'plpgsql';

CREATE TRIGGER update_trigger
    BEFORE UPDATE ON data
    FOR EACH ROW
    EXECUTE PROCEDURE update_trigger_func();


SET client_min_messages TO 'debug';
INSERT INTO data (name) VALUES ('foo');
COMMIT;  -- Make sure we get new timestamps from NOW()
SELECT * FROM ONLY data;
SELECT * FROM ONLY data2;

SELECT pg_sleep(1);
UPDATE data SET name = 'bar';
COMMIT;  -- Make sure we get new timestamps from NOW()
SELECT * FROM ONLY data;
SELECT * FROM ONLY data2;

SELECT pg_sleep(1);
UPDATE data SET name = 'baz';
COMMIT;  -- Make sure we get new timestamps from NOW()
SELECT * FROM ONLY data;
SELECT * FROM ONLY data2;

Observe que a função update_trigger_functem o nome da tabela "history" codificado como data2na linha que diz:

INSERT INTO data2 VALUES (OLD.*);

Se data2fosse um argumento, esta função poderia ser reutilizável para outras tabelas também. Mas até agora não consegui encontrar o encantamento certo. Eu tentei as duas versões a seguir até agora:

INSERT INTO TG_ARGV[0] VALUES (OLD.*);

Mas isso causa um erro de sintaxe:

psql:temptable.sql:28: ERROR:  syntax error at or near "VALUES"
LINE 11:         INSERT INTO TG_ARGV[0] VALUES (OLD.*);

Então, alternativamente, tentei com SQL dinâmico:

sql := 'INSERT INTO' || TG_ARGV[0] || 'VALUES (OLD.*)';
EXECUTE sql;

Mas isso falha porque a OLDvariável não está disponível no contexto de execução:

psql:temptable.sql:58: ERROR:  missing FROM-clause entry for table "old"
LINE 1: INSERT INTO data2 VALUES (OLD.*)
                                    ^
QUERY:  INSERT INTO data2 VALUES (OLD.*)
CONTEXT:  PL/pgSQL function versioned_update() line 11 at EXECUTE

Dado que eu gostaria de usar essa função de gatilho em outras tabelas, não posso codificar os nomes das colunas. Como eu poderia conseguir isso?

postgresql trigger
  • 1 1 respostas
  • 471 Views

1 respostas

  • Voted
  1. Best Answer
    exhuma
    2018-09-18T08:20:33+08:002018-09-18T08:20:33+08:00

    Infelizmente, pesquisar no Google foi bastante difícil, pois o tipo de dados OLDé data, e pesquisar por isso no Google não resultou em nada utilizável. Além disso, descobrir que dataé um tipo composto foi fundamental para encontrar a solução.

    A questão é dupla:

    • A tabela não pode ser usada diretamente em uma INSERTinstrução, pois é do tipo text. Portanto, o SQL dinâmico deve ser usado. Então, em vez de escrever diretamente

      INSERT INTO TG_ARGV[0] VALUES OLD;
      

      o seguinte teve que ser usado:

      sql := format(INSERT INTO %I VALUES ...', TG_ARGV[0]);
      
    • A OLDvariável não está disponível diretamente dentro de uma EXECUTEinstrução. Então, construindo a partir do ponto de bala anterior, isso não foi possível:

       sql := format(INSERT INTO %I VALUES OLD', TG_ARGV[0]);
      

      em vez disso, os valores de OLDtiveram que ser passados ​​​​com a USINGpalavra-chave:

       sql := format('INSERT INTO %I VALUES $1.*', TG_ARGV[0]);
       EXECUTE sql USING OLD;
      

    A solução final:

    CREATE OR REPLACE FUNCTION update_trigger_func()
        RETURNS TRIGGER AS $$
        DECLARE
            sql TEXT;
        BEGIN
            NEW.updated = NOW();
            sql := format('INSERT INTO %I VALUES $1.*', TG_ARGV[0]);
            EXECUTE sql USING OLD;
            RETURN NEW;
        END;
        $$ language 'plpgsql';
    

    Isso nos permite escrever um gatilho que lida com eventos na datatabela e grava o histórico na historytabela como:

     CREATE TRIGGER data_versioning_trigger_delete
         BEFORE DELETE ON data
         FOR EACH ROW EXECUTE PROCEDURE update_trigger_func('history');
    
    • 2

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve