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 / 144197
Accepted
beldaz
beldaz
Asked: 2016-07-19 05:54:58 +0800 CST2016-07-19 05:54:58 +0800 CST 2016-07-19 05:54:58 +0800 CST

O PostgreSQL usa NEW na consulta para o gatilho INSTEAD OF

  • 772

Estou tendo problemas para fazer um gatilho INSTEAD OF funcionar corretamente e acho que não entendi como usar NEW. Considere o seguinte cenário simplificado:

CREATE TABLE Product (
  product_id SERIAL PRIMARY KEY,
  product_name VARCHAR
);
CREATE TABLE Purchase (
  purchase_id SERIAL PRIMARY KEY,
  product_id INT REFERENCES Product,
  when_bought DATE
);

CREATE VIEW PurchaseView AS
SELECT purchase_id, product_name, when_bought
FROM Purchase LEFT JOIN Product USING (product_id);

Eu gostaria de poder criar INSTEAD OFgatilhos para permitir que eu insira diretamente no PurchaseView, por exemplo:

INSERT INTO Product(product_name) VALUES ('foo');
INSERT INTO PurchaseView(product_name, when_bought) VALUES ('foo', NOW());

O que eu tinha em mente era algo como:

CREATE OR REPLACE FUNCTION insert_purchaseview_func()
  RETURNS trigger AS
$BODY$
BEGIN
  INSERT INTO Purchase(product_id, when_bought)
  SELECT product_id, when_bought
  FROM NEW 
  LEFT JOIN Product USING (product_name)
  RETURNING * INTO NEW;
END;
$BODY$
LANGUAGE plpgsql;

CREATE TRIGGER insert_productview_trig
  INSTEAD OF INSERT
  ON PurchaseView
  FOR EACH ROW
  EXECUTE PROCEDURE insert_purchaseview_func();

No entanto, a função de gatilho acima apresenta erros ( relation "new" does not exist) quando executada. Sei que posso escrever consultas que usam explicitamente atributos de NEWcláusulas WHEREe SELECT, mas às vezes seria conveniente poder incluir NEWem uma junção. Existe uma maneira de fazer isso?

Solução atual (insatisfatória)

O mais próximo que posso chegar do que quero é

CREATE OR REPLACE FUNCTION insert_purchaseview_func()
  RETURNS trigger AS
$BODY$
DECLARE
tmp RECORD;
BEGIN
    WITH input (product_name, when_bought) as (
       values (NEW.product_name, NEW.when_bought)
    ) 
    INSERT INTO Purchase(product_id, when_bought)
    SELECT product_id, when_bought
    FROM input
    LEFT JOIN Product USING (product_name)
    RETURNING * INTO tmp;
    RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;

Isso é um pouco insatisfatório por vários motivos:

  1. Preciso escrever explicitamente todos os atributos NEWem CTE WITH query, o que para visualizações grandes (especialmente aquelas cujos atributos são determinados automaticamente com SELECT *) fica pesado;

  2. O resultado retornado não tem o SERIALtipo product_idatualizado, então você não obtém o resultado esperado para:

    INSERT INTO PurchaseView(product_name, when_bought) 
    VALUES ('foo', NOW())
    RETURNING *;
    
postgresql trigger
  • 2 2 respostas
  • 19790 Views

2 respostas

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2019-01-23T15:07:51+08:002019-01-23T15:07:51+08:00

    NEWé um registro , não uma tabela . Fundamentos:

    • Use NEW na cláusula FROM no gatilho do Postgres?

    Configuração ligeiramente modificada

    CREATE TABLE product (
      product_id serial PRIMARY KEY,
      product_name text UNIQUE NOT NULL  -- must be UNIQUE
    );
    
    CREATE TABLE purchase (
      purchase_id serial PRIMARY KEY,
      product_id  int REFERENCES product,
      when_bought date
    );
    
    CREATE VIEW purchaseview AS
    SELECT pu.purchase_id, pr.product_name, pu.when_bought
    FROM   purchase     pu
    LEFT   JOIN product pr USING (product_id);
    
    INSERT INTO product(product_name) VALUES ('foo');
    

    product_nametem que ser UNIQUE, ou a pesquisa nesta coluna poderia encontrar várias linhas, o que levaria a todos os tipos de confusão.

    1. Solução simples

    Para o seu exemplo simples, procurando apenas a coluna única product_id, uma subconsulta correlacionada é mais simples e rápida:

    CREATE OR REPLACE FUNCTION insert_purchaseview_func()
      RETURNS trigger AS
    $func$
    BEGIN
       INSERT INTO purchase(product_id, when_bought)
       SELECT (SELECT product_id FROM product WHERE product_name = NEW.product_name), NEW.when_bought
       RETURNING purchase_id
       INTO   NEW.purchase_id;  -- generated serial ID for RETURNING - if needed
    
       RETURN NEW;
    END
    $func$  LANGUAGE plpgsql;
    
    CREATE TRIGGER insert_productview_trig
    INSTEAD OF INSERT ON purchaseview
    FOR EACH ROW EXECUTE PROCEDURE insert_purchaseview_func();
    

    Sem variáveis ​​adicionais. Sem CTE (só acrescentaria custo e ruído). As colunas de NEWsão escritas apenas uma vez (seu ponto 1 ).

    O anexo RETURNING purchase_id INTO NEW.purchase_idcuida do seu ponto 2 : agora, a linha retornada inclui o recém-geradopurchase_id .

    Caso o produto não seja encontrado ( NEW.product_namenão existe na tabela product), a compra ainda é inserida e product_idéNULL . Isso pode ou não ser desejável.

    2.

    Para pular a linha (e possivelmente aumentar um WARNING/ EXCEPTION):

    CREATE OR REPLACE FUNCTION insert_purchaseview_func()
      RETURNS trigger AS
    $func$
    BEGIN
       INSERT INTO purchase AS pu
                (product_id,     when_bought)
       SELECT pr.product_id, NEW.when_bought
       FROM   product pr
       WHERE  pr.product_name = NEW.product_name
       RETURNING pu.purchase_id
       INTO   NEW.purchase_id;  -- generated serial ID for RETURNING - if needed
    
       IF NOT FOUND THEN  -- insert was canceled for missing product
          RAISE WARNING 'product_name % not found! Skipping INSERT.', quote_literal(NEW.product_name);
       END IF;
    
       RETURN NEW;
    END
    $func$  LANGUAGE plpgsql;
    

    Isso pega carona NEWnas colunas para SELECT .. FROM product. Se o produto for encontrado, tudo ocorre normalmente. Se não, nenhuma linha é retornada do SELECTe não INSERTacontece. A variável especial PL/pgSQL FOUNDsó é verdadeira se a última consulta SQL processou pelo menos uma linha.

    Pode ser EXCEPTIONem vez de WARNINGgerar um erro e reverter a transação. Mas prefiro declarar purchase.product_id NOT NULLe inserir incondicionalmente (consulta 1 ou similar), para o mesmo efeito: gera uma exceção se product_idfor NULL. Mais simples, mais barato.

    3. Para várias pesquisas

    CREATE OR REPLACE FUNCTION insert_purchaseview_func()
      RETURNS trigger AS
    $func$
    BEGIN
       INSERT INTO purchase AS pu
                (product_id,   when_bought)     -- more columns?
       SELECT pr.product_id, i.when_bought      -- more columns?
       FROM  (SELECT NEW.*) i                   -- see below
       LEFT   JOIN product  pr USING (product_name)
    -- LEFT   JOIN tbl2     t2 USING (t2_name)  -- more lookups?
       RETURNING pu.purchase_id                 -- more columns?
       INTO   NEW.purchase_id;                  -- more columns?
    
       RETURN NEW;
    END
    $func$  LANGUAGE plpgsql;
    

    Os LEFT JOINs tornam o INSERTincondicional novamente. Em vez disso, use JOINpara pular se não for encontrado.

    FROM (SELECT NEW.*) itransforma o registro NEW em uma tabela derivada com uma única linha , que pode ser usada como qualquer tabela noFROM cláusula - o que você procurava, inicialmente.

    db<>mexa aqui

    • 11
  2. beldaz
    2016-07-19T16:54:15+08:002016-07-19T16:54:15+08:00

    Conforme sugerido nos comentários, parece que o mais próximo que posso fazer do que quero é (corrigindo minha abordagem original na pergunta):

    CREATE OR REPLACE FUNCTION insert_purchaseview_func()
      RETURNS trigger AS
    $BODY$
    DECLARE
    tmp RECORD;
    BEGIN
        WITH input  (product_name, when_bought) as (
           values (NEW.product_name, NEW.when_bought)
        ) 
        INSERT INTO Purchase(product_id, when_bought)
        SELECT product_id, when_bought
        FROM input
        LEFT JOIN Product USING (product_name)
        RETURNING purchase_id INTO tmp;
        NEW.purchase_id = tmp.purchase_id;
        RETURN NEW;
    END;
    $BODY$
    LANGUAGE plpgsql;
    

    Isso pelo menos faz com que a RETURNINGcláusula funcione corretamente. Parece que os atributos de NEWdevem ser explicitamente declarados. A seguir:

    -- Using NEW.* in CTE doesn't work
    CREATE OR REPLACE FUNCTION insert_purchaseview_func()
      RETURNS trigger AS
    $BODY$
    DECLARE
    tmp RECORD;
    BEGIN
        WITH input  as (
           values (NEW.*)
        ) 
        INSERT INTO Purchase(product_id, when_bought)
        SELECT product_id, when_bought
        FROM input
        LEFT JOIN Product USING (product_name)
        RETURNING purchase_id INTO tmp;
        NEW.purchase_id = tmp.purchase_id;
        RETURN NEW;
    END;
    $BODY$
    LANGUAGE plpgsql;
    

    resulta emERROR: column "product_name" specified in USING clause does not exist in left table quando o gatilho é disparado.

    • 4

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