【问题标题】:Check if trigger exists检查触发器是否存在
【发布时间】:2016-08-30 13:37:18
【问题描述】:

我对公共架构中所有表的触发器有以下查询:

SELECT 'CREATE TRIGGER ' || tab_name|| '_if_modified_trg INSERT OR UPDATE OR DELETE ON  ' || tab_name|| ' FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func(); ' AS trigger_creation_query
FROM (
   SELECT quote_ident(table_schema) || '.' || quote_ident(table_name) as  tab_name
   FROM information_schema.tables
   WHERE table_schema='public'
   ) AS foo;

而且我知道如何检查触发器是否存在:

SELECT tgname
from pg_trigger
where not tgisinternal AND tgname='randomname'

但是如何在第一个查询中检查是否已经存在同名的触发器 - 并跳过创建它并继续?这是我的解决方案,但它不起作用:

SELECT 'CREATE TRIGGER ' || tab_name|| '_if_modified_trg INSERT OR UPDATE OR DELETE ON  ' || tab_name|| ' FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func(); ' AS trigger_creation_query
FROM (
   SELECT quote_ident(table_schema) || '.' || quote_ident(table_name) as tab_name
   FROM information_schema.tables
   WHERE table_schema='public'    
 ) AS foo
 WHERE tab_name||'if_modified_trg' NOT IN (
    SELECT tgname
    from pg_trigger
    where not tgisinternal );

【问题讨论】:

标签: postgresql plpgsql dynamic-sql database-trigger


【解决方案1】:

使用它,您可以检查触发器是否存在,如果不存在则创建它。不要忘记最后一个“;”。

DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'randomname') THEN
        CREATE TRIGGER randomname  
        AFTER INSERT OR UPDATE OR DELETE ON randomtable
        FOR EACH ROW EXECUTE PROCEDURE randomfunction();
    END IF;
END
$$;

希望对你有帮助。

【讨论】:

    【解决方案2】:

    您可以使用DO 语句或plpgsql 函数有条件地执行触发器创建:

    DO
    $do$
    BEGIN
       IF EXISTS (
           SELECT 1
           FROM   pg_trigger
           WHERE  NOT tgisinternal AND tgname = 'randomname'
           ) THEN
          -- do nothing
       ELSE
          -- create trigger
       END IF;
    END
    $do$
    

    仔细检查后,您的其余代码也存在各种问题。 似乎您正在尝试这样做:

    DO
    $do$
    DECLARE
       _tbl regclass;
       _trg text;
    BEGIN
       FOR _tbl, _trg IN
          SELECT c.oid::regclass, relname || '_if_modified_trg'
          FROM   pg_class c
          JOIN   pg_namespace n ON n.oid = c.relnamespace
          WHERE  n.nspname = 'public'
          AND    c.relkind = 'r'  -- only regular tables
       LOOP
          IF EXISTS (
             SELECT 
             FROM   pg_trigger
             WHERE  tgname  = _trg
             AND    tgrelid = _tbl       -- check only for respective table
             ) THEN
             -- do nothing
          ELSE
             -- create trigger
             EXECUTE format(
                'CREATE TRIGGER %I
                 BEFORE INSERT OR UPDATE OR DELETE ON %s
                 FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func()'
               , _trg, _tbl::text
             );
          END IF;
       END LOOP;
    END
    $do$;
    

    出于多种原因,我使用系统目录 pg_class 而不是 information_schema.tables。最重要的是,它包含了表的oid,这使得对pg_trigger的检查更简单,更不容易出错。

    我们实际上可以进一步简化并检查同一查询中是否存在触发器。明显更快,但:

    DO
    $do$
    DECLARE
       _tbl text;
       _trg text;
    BEGIN
    FOR _tbl, _trg IN
       SELECT c.oid::regclass::text, relname || '_if_modified_trg'
       FROM   pg_class        c
       JOIN   pg_namespace    n ON n.oid = c.relnamespace
       LEFT   JOIN pg_trigger t ON t.tgname = c.relname || '_if_modified_trg'
                               AND t.tgrelid = c.oid  -- check only respective table
       WHERE  n.nspname = 'public'
       AND    c.relkind = 'r'   -- only regular tables
       AND    t.tgrelid IS NULL -- trigger does not exist yet
    LOOP
       EXECUTE format(
          'CREATE TRIGGER %I
           BEFORE INSERT OR UPDATE OR DELETE ON %s
           FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func()'
         , _trg_name, _tbl_oid::text
       );
    END LOOP;
    END
    $do$;
    

    更多解释的相关答案:

    【讨论】:

    • @Avon:修复了一个错误:仅循环通过常规表。
    猜你喜欢
    • 2017-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-07
    • 2010-09-27
    • 2010-09-12
    • 2021-12-31
    • 2023-04-01
    相关资源
    最近更新 更多