【问题标题】:Accessing fields of records in triggers by column name passed as trigger arg通过作为触发器 arg 传递的列名访问触发器中的记录字段
【发布时间】:2013-12-05 12:40:59
【问题描述】:

我有一组表,它们使用鉴别器列来区分实体类型。这些表中的某些表描述了定义的实体组之间的关系或包含特定类型实体的更多信息。我想对表中的数据进行完整性检查。最初这些是作为检查编写的,但 postgres 不能很好地处理备份中的表排序,所以我想将检查重写为触发器,以便在恢复数据库时可以关闭触发器。

由于实体的实际列名会从一个表更改为另一个表,并且我想以 DRY 方式进行,因此我尝试编写一个将实体的 id 作为参数并验证它的触发函数是正确的类型。

我正在尝试调用一个带参数的函数触发器

c.is_earth_based_measurement_tg(bigint).

实际的触发器是这样写的:

CREATE TRIGGER earth_based_measurement_locations_tg1
  BEFORE INSERT OR UPDATE
  ON measurements.earth_based_measurement_locations
  FOR EACH ROW
    EXECUTE PROCEDURE c.is_earth_based_measurement_tg(NEW.fk_measurement);

当我尝试保存触发器时,我收到“错误:“。”或附近的语法错误,指的是 NEW.fk_measurement。编写此触发器的正确​​方法是什么?

谢谢。

【问题讨论】:

    标签: sql postgresql triggers hstore


    【解决方案1】:

    您不需要将NEW.anything 作为参数传递;它作为变量NEW 自动提供给触发函数。因此,只要tg_opINSERTUPDATENEW 未设置为DELETETRUNCATE 触发器),您的触发器就可以从其中的任何位置访问NEW.fk_measurement

    触发器可以接受参数,但它们出现在TG_ARGV 中(如 zero323 注释)。它们不需要用于列值,实际上不能引用列值;它们的用途是用于参数化触发器、具有可选功能的触发器等。

    在重新阅读您的问题时,您确实需要一个用于感兴趣的列名的参数。不幸的是,在 NEW 元组中通过动态名称访问列非常尴尬。

    您可以将触发器创建为:

    CREATE TRIGGER earth_based_measurement_locations_tg1
      BEFORE INSERT OR UPDATE
      ON measurements.earth_based_measurement_locations
      FOR EACH ROW
        EXECUTE PROCEDURE c.is_earth_based_measurement_tg('fk_measurement');
    

    即将 colname 作为字符串文字传递。

    触发过程将被定义为:

    CREATE OR REPLACE FUNCTION is_earth_based_measurement_tg() RETURNS trigger AS $$
    BEGIN
      IF tg_op = 'INSERT' OR tg_op = 'UPDATE' THEN
        PERFORM my_func(hstore(NEW) -> TG_ARGV[0]);
      END IF;
    END;
    $$ LANGUAGE plpgsql;
    

    ... 或者您想对从列中提取的值执行任何其他操作。

    不能在当前 PostgreSQL 版本(包括 9.4)中通过动态列名设置 NEW 中列的值。

    请注意,我们将 NEW 元组转换为 hstore,然后按列名取消引用它。

    【讨论】:

    • 在我的问题中,我注意到列名会因表而异。如果我触发参数无法引用列值,那么看起来我无法做我想做的事情,并且必须为我拥有的每个不同的列名编写一个触发函数。
    • @rozkosz 明白了。已更新。
    • 有效,谢谢。必须使用您的 answer 并确保将变量强制转换为 bigint。
    【解决方案2】:

    触发器函数不能用参数声明。 OLDNEW 和几个与触发器相关的变量默认使用所谓的TG_ARGV 传递,并且可以在触发器函数中访问,无需额外步骤。您可以查看official documentation 了解详细信息,但通常应该类似于:

    CREATE OR REPLACE FUNCTION is_earth_based_measurement_tg()
    RETURNS TRIGGER AS $earth_based_measurement_locations_tg1$
        BEGIN
           IF (NEW.fk_measurement = 'foo') THEN
               -- Do something
           END IF;
        END;
    $earth_based_measurement_locations_tg1$ LANGUAGE plpgsql;
    

    【讨论】:

    • 你究竟是如何传递默认的 TG_ARGV 的?我在您的链接引用的页面上没有看到这样的示例。
    猜你喜欢
    • 2022-12-06
    • 2020-05-20
    • 2012-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多