【问题标题】:Syntax error in UPSERT test codeUPSERT 测试代码中的语法错误
【发布时间】:2016-02-14 15:06:09
【问题描述】:

我正在尝试使用以下测试代码测试新的 PostgreSQL upsert syntax,但出现语法错误:

test=> CREATE TABLE test1 (
test(>         key1 integer PRIMARY KEY check (key1 > 0),
test(>         key2 integer check (key2 > 0)
test(> );
CREATE TABLE

test=> CREATE OR REPLACE FUNCTION upsert(IN in_json_array jsonb)
test->         RETURNS void AS
test-> $func$
test$>         UPDATE test1 t SET     
test$>         t.key1 = (obj->>'key1')::int,
test$>         t.key2 = (obj->>'key2')::int
test$>         FROM JSONB_ARRAY_ELEMENTS(in_json_array) obj
test$>         WHERE  t.key1 = obj->'key1'
test$>         ON CONFLICT DO UPDATE SET
test$>         key1 = excluded.key1,
test$>         key2 = excluded.key2;
test$> 
test$> $func$ LANGUAGE sql;

ERROR:  syntax error at or near "ON"
LINE 9:         ON CONFLICT DO UPDATE SET
                ^

请问上面的代码为什么会失败?

另外,test1 表有几个约束(非负值和唯一主键)。如何解决唯一性约束?

更新 2:我已从 UPDATE 切换到 INSERT(抱歉犯了愚蠢的错误!),但仍在为语法苦苦挣扎:

test=> CREATE OR REPLACE FUNCTION upsert(IN in_json_array jsonb)
test->         RETURNS void AS
test-> $func$
test$>         INSERT into test1 AS t (t.key1, t.key2)
test$>         VALUES ((obj->>'key1')::int, (obj->>'key2')::int)
test$>         FROM JSONB_ARRAY_ELEMENTS(in_json_array) obj
test$>         WHERE t.key1 = obj->'key1'
test$>         ON CONFLICT DO UPDATE SET
test$>         t.key1 = excluded.key1,
test$>         t.key2 = excluded.key2;
test$> $func$ LANGUAGE sql;

ERROR:  syntax error at or near "FROM"
LINE 6:         FROM JSONB_ARRAY_ELEMENTS(in_json_array) obj
                ^

我也尝试将 JSON 行更改为:

SELECT obj FROM JSONB_ARRAY_ELEMENTS(in_json_array)

但这也失败了......

为了您的方便,这是我的测试代码:

select upsert('[{"key1":1,"key2":2},{"key1":3,"key2":4}]'::jsonb);
select upsert('[{"key1":1,"key2":2},{"key1":1,"key2":4}]'::jsonb);

【问题讨论】:

  • language plpgsql 需要 begin ... end 块。你可能想要language sql
  • 谢谢,但这没有帮助(BEGIN / ENDLANGUAGE SQL 都没有)
  • PostgreSQL 9.5.1 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16), 64-bit 来自用于 CentOS 6.7 Linux 的 postgresql95-server-9.5.1-1PGDG.rhel6.x86_64 软件包。

标签: postgresql insert upsert postgresql-9.5 postgresql-json


【解决方案1】:

因为这是错误的最高 Google 结果:

ON CONFLICT DO UPDATE command cannot affect row a second time

我会补充一点,这可能是由重复冲突VALUES引起的,例如

INSERT INTO distributors (did, dname)
VALUES 
    (5, 'Gizmo Transglobal'), 
    (5, 'Associated Computing, Inc')
ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname;

在这种情况下,我们尝试插入两个值,其中dim 设置为5。由于dim 是索引,它不会在查询本身中发生冲突。

我在实现微服务和处理请求时遇到了这个错误,其中一些有重复记录。

【讨论】:

  • 噢噢噢噢。谢谢!
【解决方案2】:

如果您使用的是from 子句,则不能使用values。可以是insert into .. values (..) insert into .. select ... from ...,但不能同时使用。

您也不能在提供插入的select 子句中从插入引用目标表。我不确定您要达到什么目的。

您还需要通过指定应处理的约束的 PK 列或 名称 来限定应捕获的“冲突”。在您的情况下,它应该是 pk 列:

所有这些放在一起,函数应该如下所示:

CREATE OR REPLACE FUNCTION upsert(IN in_json_array jsonb)
        RETURNS void AS
$func$
    INSERT into test1 (key1, key2)
    select distinct on ((obj->>'key1')::int)
             (obj->>'key1')::int, 
             (obj->>'key2')::int
    FROM JSONB_ARRAY_ELEMENTS(in_json_array) obj
    ON CONFLICT (key1) DO 
      UPDATE SET key1 = excluded.key1,
                 key2 = excluded.key2;
$func$ 
LANGUAGE sql;

distinct on () 将确保 select 只返回一个 key1 的值,以避免“ON CONFLICT DO UPDATE 命令不能再次影响行”错误。请注意,如果选择中没有order by,则选择哪个键基本上是“随机的”。如果你想选择一个特定的,你需要在select中添加一个order by

【讨论】:

  • 当调用select upsert('[{"key1":1,"key2":2},{"key1":1,"key2":4}]'::jsonb);(即JSON数组中的相同PK)时,不幸的是ERROR: ON CONFLICT DO UPDATE command cannot affect row a second time
  • @AlexanderFarber:好吧,您将相同的密钥传递了两次。显然on conflict 子句无法处理这个问题。您需要调整 select 语句以仅从 json 数组中检索唯一键
  • 好的,谢谢,我会坚持LOOPUPDATE - NOT FOUND - INSERT: stackoverflow.com/a/35231171/165071
  • @AlexanderFarber:应该使用这两个键中的哪一个?您可以使用distinct on () 来确保您只获得每个key1 一次。
  • @AlexanderFarber:如果 PK 实际上是(key1, key2),那么您必须使用ON CONFLICT (key1, key2)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-26
  • 2015-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多