在冲突时使用插入时出现 Postgres 语法错误

Postgres syntax error while using insert on conflict

给定一个 table

CREATE TABLE balances
(
    username   varchar(255)     NOT NULL,
    currency   varchar(255)     NOT NULL,
    balance    numeric          NULL,
    CONSTRAINT balances_pkey PRIMARY KEY (username, currency)
);

尝试后

CREATE OR REPLACE FUNCTION merge_balance(username varchar(255), currency varchar(255), to_add numeric) RETURNS void
AS $$ BEGIN
    INSERT INTO balances(username, currency, to_add)
    ON CONFLICT balances_pkey DO UPDATE SET balance = OLD.balance + to_add
END; $$ LANGUAGE plpgsql;

我明白了

ERROR:  syntax error at or near "ON"

我的 PostgreSQL 版本(运行 在容器 postgres:11.5 中)

SELECT version();
                                                             version
----------------------------------------------------------------------------------------------------------------------------------
 PostgreSQL 11.5 (Debian 11.5-3.pgdg90+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516, 64-bit
(1 row)

那些;,$的事情太烦人了,现在我得到了这个=_=
有人解决过这样的问题吗?谢谢!

不应该吗

INSERT INTO balances VALUES(username, currency, to_add)
   ON CONFLICT balances_pkey DO UPDATE SET balance = OLD.balance + to_add

考虑:

create or replace function merge_balance(
    p_username varchar(255), 
    p_currency varchar(255), 
    p_to_add numeric
) returns void
as $$
begin
    insert into balances (username, currency, balance)
    values(p_username, p_currency, p_to_add)
    on conflict (username, currency) do update set balance = old.balance + p_to_add;
end; $$ language plpgsql;

理由:

  • 您需要一个 VALUES() 子句来枚举要插入的值;环绕列出目标列也是一个好习惯 - 为此,您最好不要使用与 table 列

    同名的函数参数
  • 冲突目标必须用括号括起来-你可以使用约束名称,但我发现使用列名更清楚

您尝试时不能使用“old”。您正在做的是引用 DML 语句为触发器生成的伪行 OLD。即使可以引用它也将是 NULL,因为所有旧列在插入时都是空的。这不是您想要的,结果将为空。但是您确实需要为 table 添加别名以避免更新子句出现歧义。也没有必要在函数头上指定限制。尝试:

create or replace 
function merge_balance(
         p_username varchar  
       , p_currency varchar  
       , p_to_add   numeric
       )
  returns  void
  language sql
as $$
    insert into balances as bal (username, currency, balance)
    values (p_username, p_currency, p_to_add)
        on conflict (username, currency) 
        do update 
           set balance = bal.balance + excluded.balance;
$$; 

查看完整示例 here

作为事后的想法,我猜你可以通过使用 old 作为 table 别名来使用 old.balance。

insert into balances as old (username, currency, balance) ...

但这似乎不是个好主意,至少对我而言。,