在冲突时使用插入时出现 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) ...
但这似乎不是个好主意,至少对我而言。,
给定一个 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) ...
但这似乎不是个好主意,至少对我而言。,