为什么这个规则不能防止重复密钥违规?

发布于 2024-10-21 08:51:51 字数 332 浏览 1 评论 0原文

(postgresql)我试图将 csv 数据复制到表中,但遇到重复的密钥违规错误,并且无法告诉 COPY 忽略这些错误,因此遵循互联网我尝试添加这条规则:

CREATE OR REPLACE RULE ignore_duplicate_inserts AS
   ON INSERT TO mytable
   WHERE (EXISTS ( SELECT mytable.id
           FROM mytable
          WHERE mytable.id = new.id)) DO NOTHING;

来规避问题,但我仍然遇到这些错误 - 有什么想法吗?

(postgresql) I was trying to COPY csv data into a table but I was getting duplicate key violation errors, and there's no way to tell COPY to ignore those, so following internet wisdom I tried adding this rule:

CREATE OR REPLACE RULE ignore_duplicate_inserts AS
   ON INSERT TO mytable
   WHERE (EXISTS ( SELECT mytable.id
           FROM mytable
          WHERE mytable.id = new.id)) DO NOTHING;

to circumvent the problem, but I still get those errors - any ideas why ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

杀お生予夺 2024-10-28 08:51:52

默认规则将内容添加到当前操作

粗略地说,当执行给定表上的给定命令时,规则会导致执行其他命令。

但是 INSTEAD 规则允许您替换操作:

或者,INSTEAD 规则可以用另一个命令替换给定的命令,或者导致命令根本不执行。

所以,我认为你想要指定 INSTEAD

CREATE OR REPLACE RULE ignore_duplicate_inserts AS
   ON INSERT TO mytable
   WHERE (EXISTS ( SELECT mytable.id
           FROM mytable
          WHERE mytable.id = new.id)) DO INSTEAD NOTHING;

没有 INSTEAD ,当您想说“而不是插入,什么都不做”时,您的规则本质上是说“执行插入,然后什么也不做”,并且据我所知,DO INSTEAD NOTHING会这样做。

我不是 PostgreSQL 规则方面的专家,但我认为添加“INSTEAD”应该可行。

更新:感谢 araqnid 我们知道

COPY FROM 将调用任何触发器并检查目标表上的约束。但是,它不会调用规则

,因此规则在这种情况下不起作用。但是,触发器在 COPY FROM 期间被触发,因此您可以编写 BEFORE INSERT 触发器当检测到重复行时将返回 NULL

可以返回NULL来跳过当前行的操作。这指示执行器不执行调用触发器的行级操作(插入或修改特定表行)。

也就是说,我认为你最好使用 araqnid 的“将其全部加载到临时表中,清理它,并将其复制到最终目的地”对于像你这样的批量加载操作来说​​是一个更明智的解决方案。

Rules by default add things to the current action:

Roughly speaking, a rule causes additional commands to be executed when a given command on a given table is executed.

But an INSTEAD rule allows you to replace the action:

Alternatively, an INSTEAD rule can replace a given command by another, or cause a command not to be executed at all.

So, I think you want to specify INSTEAD:

CREATE OR REPLACE RULE ignore_duplicate_inserts AS
   ON INSERT TO mytable
   WHERE (EXISTS ( SELECT mytable.id
           FROM mytable
          WHERE mytable.id = new.id)) DO INSTEAD NOTHING;

Without the INSTEAD, your rule is essentially saying "do the INSERT and then do nothing" when you want to say "instead of the INSERT, do nothing" and, AFAIK, the DO INSTEAD NOTHING will do that.

I'm not an expert on PostgreSQL rules but I think adding the "INSTEAD" should work.

UPDATE: Thanks to araqnid we know that:

COPY FROM will invoke any triggers and check constraints on the destination table. However, it will not invoke rules

So a rule isn't going to work in this situation. However, triggers are fired during COPY FROM so you could write a BEFORE INSERT trigger that would return NULL when it detected duplicate rows:

It can return NULL to skip the operation for the current row. This instructs the executor to not perform the row-level operation that invoked the trigger (the insertion or modification of a particular table row).

That said, I think you'd be better off with araqnid's "load it all into a temporary table, clean it up, and copy it to the final destination" would be a more sensible solution for a bulk loading operation like you have.

小情绪 2024-10-28 08:51:52

COPY FROM 不会调用规则(http://www.postgresql.org/docs/9.0/interactive/sql-copy.html#AEN58860)

我的方法是将 CSV 数据加载到临时文件中表,然后使用 INSERT...SELECT 语句将数据复制到尚不存在的目标表中。 (如果 CSV 数据本身存在重复项,请先从临时表中删除这些重复项)。像这样的东西:

BEGIN;
CREATE TEMP TABLE stage_data(key_column, data_columns...) ON COMMIT DROP;
\copy stage_data from data.csv with csv header
-- prevent any other updates while we are merging input (omit this if you don't need it)
LOCK target_data IN SHARE ROW EXCLUSIVE MODE;
-- insert into target table
INSERT INTO target_data(key_column, data_columns...)
   SELECT key_column, data_columns...
   FROM stage_data
   WHERE NOT EXISTS (SELECT 1 FROM target_data
                     WHERE target_data.key_column = stage_data.key_column)
END;

COPY FROM will not invoke rules (http://www.postgresql.org/docs/9.0/interactive/sql-copy.html#AEN58860)

My approach would be to load the CSV data into a temp table, then use an INSERT...SELECT statement to copy the data into the target table where it doesn't already exist. (If there are duplicates in the CSV data itself, remove those from the temp table first). Something like:

BEGIN;
CREATE TEMP TABLE stage_data(key_column, data_columns...) ON COMMIT DROP;
\copy stage_data from data.csv with csv header
-- prevent any other updates while we are merging input (omit this if you don't need it)
LOCK target_data IN SHARE ROW EXCLUSIVE MODE;
-- insert into target table
INSERT INTO target_data(key_column, data_columns...)
   SELECT key_column, data_columns...
   FROM stage_data
   WHERE NOT EXISTS (SELECT 1 FROM target_data
                     WHERE target_data.key_column = stage_data.key_column)
END;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文