在 Postgres 触发器的 FROM 子句中使用 NEW?

Bry*_*ley 2 postgresql trigger subquery postgresql-9.4

我正在尝试编写 Postgres 触发器以在插入或更新新行之前取消嵌套数组字段。例如

SELECT
unnest(something)
FROM NEW
Run Code Online (Sandbox Code Playgroud)

但是,这似乎会导致错误:

关系“新”不存在

如何使用触发器函数内的 NEW 行,以允许我取消嵌套数组字段以进行进一步处理?

创建表语句

以下是表结构的示例:

CREATE TABLE parent_table (
    id uuid NOT NULL,
    jsonb_array_field jsonb[] NOT NULL DEFAULT '{}'::jsonb[],
    CONSTRAINT "parent_table_pkey" PRIMARY KEY (id),
);
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE many_to_one_table (
  id serial primary key,
  parent_table_id uuid references parent_table(id),
  subfield_a text,
  subfield_a text
);
Run Code Online (Sandbox Code Playgroud)

触发功能

下面是 TRIGGER 函数的本质:

CREATE OR REPLACE FUNCTION unnest_and_normalize_json() RETURNS TRIGGER AS 
$body$
begin
    if NEW.jsonb_array_field is null then
        raise exception 'jsonb_array_field is null';
    else
        insert into 
            many_to_one_table(subfield_a, subfield_b)
            select
                parent_table_id, -- this is to be the ForeignKey
                json_data->>'subfieldA' as subfield_a,
                json_data->>'subfieldB' as subfield_b
            from (
                select 
                    id, -- need ID for ForeignKey relationship
                    unnest(jsonb_array_field) as json_data
                    from new
            ) as unnested_json;
    end if;
    RETURN new;
end;
$body$ 
LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

触发语句

触发器语句可以在 INSERT 和 UPDATE 之前或之后运行,只要数据在“迁移”表中进行了镜像。

CREATE TRIGGER unnest_and_normalize_json_on_insert AFTER INSERT ON parent_table
FOR EACH ROW EXECUTE PROCEDURE unnest_and_normalize_json();

CREATE TRIGGER unnest_and_normalize_json_on_update AFTER UPDATE ON parent_table
FOR EACH ROW EXECUTE PROCEDURE unnest_and_normalize_json();
Run Code Online (Sandbox Code Playgroud)

ForeignKey 澄清 我们正在尝试将我们数据模型的这一部分转换为使用 ForeignKey 关系而不是 JSON 字段。normalized_table当我们从旧记录回填/迁移数据时,触发器旨在作为确保数据向前发展的临时步骤。

Erw*_*ter 5

NEWOLD在触发功能是记录-每只定义适用。(例如,OLDINSERT触发器中没有。)您可以对它们执行ROW操作。使用点表示法(如表限定的列名)引用嵌套列,就像列表中包含的FROM表一样。NEW并且OLD在触发器函数的 SQL DML 语句中几乎随处可见。(带有EXECUTE. 的动态 SQL 除外)

要将其NEW视为实际表,您必须先对其进行转换 - 这通常是没有必要的。喜欢:(SELECT NEW.*)。有关有意义的用例,请参阅此相关答案中的第3章:

您的基本触发功能会分解为:

CREATE OR REPLACE FUNCTION unnest_and_normalize_json()
  RETURNS trigger AS 
$func$
BEGIN
   INSERT INTO many_to_one_table (parent_table_id, subfield_a, subfield_b)
   SELECT NEW.id
        , ja->>'subfieldA' AS subfield_a  -- column alias only for documentation
        , ja->>'subfieldB'
   FROM   unnest(NEW.jsonb_array_field) ja;  -- produces a derived table

   RETURN NEW; -- optional for AFTER trigger
END
$func$  LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

这只是噪音:

if NEW.jsonb_array_field is null then ...
Run Code Online (Sandbox Code Playgroud)

该列jsonb_array_field定义为:

jsonb_array_field jsonb[] NOT NULL DEFAULT '{}'::jsonb[],
Run Code Online (Sandbox Code Playgroud)

这是一个AFTER触发器,该NOT NULL约束将已经提出了一个例外NULLjsonb_array_field在这一点上。

您可以将两个触发器合并为一个:

CREATE TRIGGER unnest_and_normalize_json_on_insert
AFTER INSERT OR UPDATE ON parent_table  -- !
FOR EACH ROW EXECUTE PROCEDURE unnest_and_normalize_json();
Run Code Online (Sandbox Code Playgroud)

这就是说,你必须做更多的事情来盖UPDATEDELETE正确。类似于这个(黑暗面一章):