PostgreSQL触发器以避免jsonb中的重复值

时间:2018-04-23 06:26:31

标签: postgresql triggers jsonb

我需要检查jsonb中的值是否已存在于数组中。我试图通过触发器实现这一点,但我是这种语言的新手,我不知道如何编写查询。

    CREATE TABLE merchants (
      key uuid PRIMARY KEY,
      data jsonb NOT NULL
    )

这是触发器。我认为NEW.data.ids部分是错误的。

CREATE FUNCTION validate_id_constraint() returns trigger as $$
    DECLARE merchants_count int;
  BEGIN
    merchants_count := (SELECT count(*) FROM merchants WHERE data->'ids' @> NEW.data.ids);
    IF (merchants_count != 0) THEN
      RAISE EXCEPTION 'Duplicate id';
    END IF;
    RETURN NEW;
  END;
  $$ language plpgsql;

  CREATE TRIGGER validate_id_constraint_trigger BEFORE INSERT OR UPDATE ON merchants
    FOR EACH ROW EXECUTE PROCEDURE validate_id_constraint();

当我插入表格时,我收到此错误消息

ERROR:  missing FROM-clause entry for table "data"
LINE 1: ...LECT count(*) FROM merchants WHERE data->'ids' @> NEW.data.i...
                                                            ^ 

我已经在触发器外完成了查询,并且工作正常

   SELECT count(*) FROM merchants WHERE data->'ids' @> '["11176", "11363"]'

1 个答案:

答案 0 :(得分:1)

您收到该错误是因为您使用.而不是->来提取表达式ids中的NEW.data.ids数组。

但是你的触发器无论如何都不会起作用,因为你并没有试图避免遏制,而是在阵列中重叠。

您可以编写触发器功能的一种方法是:

CREATE OR REPLACE FUNCTION validate_id_constraint() RETURNS trigger
   LANGUAGE plpgsql AS
$$DECLARE
   j jsonb;
BEGIN
   FOR j IN
      SELECT jsonb_array_elements(NEW.data->'ids')
   LOOP
      IF EXISTS
         (SELECT 1 FROM merchants WHERE j <@ (data->'ids'))
      THEN
         RAISE EXCEPTION 'Duplicate IDs';
      END IF;
   END LOOP;
   RETURN NEW;
END;$$;

您必须循环,因为jsonb数组上没有“重叠”运算符。

由于你的桌面设计,这一切都很慢而且很麻烦。

注1:如果只在jsonb中存储不需要在数据库中操作的数据,那么 会更好。特别是,您应该将ids数组存储为表中的字段。然后,您可以使用“重叠”运算符&&,并使用gin索引加快速度。

如果对表结构进行规范化并将单个数组条目存储在单独的表中,那么你会更快。然后是常规的唯一约束。

注2:触发器启用的任何约束都会遇到竞争条件:如果两个并发的INSERT相互冲突,则触发器函数将看不到并发的值INSERT,您可能会得到不一致的数据。