检查触发器是不是存在

Posted

技术标签:

【中文标题】检查触发器是不是存在【英文标题】: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 );

【问题讨论】:

How to check if trigger exists in PostgreSQL? 【参考方案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$;
How to check if a table exists in a given schema Information schema vs. system catalogs Select rows which are not present in other table

更多解释的相关答案:

EXECUTE of SELECT ... INTO is not implemented Loop on tables with PL/pgSQL in Postgres 9.0+ Postgres: check disk space taken by materialized view?

【讨论】:

@Avon:修复了一个错误:仅循环通过常规表。

以上是关于检查触发器是不是存在的主要内容,如果未能解决你的问题,请参考以下文章

检查 SQL Server 中是不是存在触发器的最便携方法是啥?

使用谷歌表格公式检查appsscript项目触发器是不是存在

什么更好:检查表是不是存在或总是触发创建语句 [关闭]

如何编写一个触发器来检查一个值是不是已经在表中?

检查 postgresql 的返回查询中是不是存在值

核心数据 - 如何在不触发错误的情况下检查对象关系是不是存在