检查触发器是否存在

Avo*_*nes 5 postgresql dynamic-sql plpgsql database-trigger

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

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;
Run Code Online (Sandbox Code Playgroud)

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

SELECT tgname
from pg_trigger
where not tgisinternal AND tgname='randomname'
Run Code Online (Sandbox Code Playgroud)

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

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 );
Run Code Online (Sandbox Code Playgroud)

小智 5

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

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
$$;
Run Code Online (Sandbox Code Playgroud)

我希望这可以帮助你。


Erw*_*ter 0

您可以使用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$
Run Code Online (Sandbox Code Playgroud)

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

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$;
Run Code Online (Sandbox Code Playgroud)

我使用系统目录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$;
Run Code Online (Sandbox Code Playgroud)

相关答案及更多解释: