如何在 ON CONFLICT 子句中消除 plpgsql 变量名的歧义?
How to disambiguate a plpgsql variable name in a ON CONFLICT clause?
鉴于此 table:
create table test (
name text primary key
);
我需要编写一个变量名与主键名冲突的 plpgsql 函数,我必须在 on conflict
子句中使用它:
create or replace function func(
name text -- this variable name...
) returns void language plpgsql as
$$
begin
insert into test (name) values (name)
on conflict (name) do update -- ...conflicts with this line
set name = func.name;
end;
$$;
这会编译,但会抛出一个不明确的列引用:
select * from func('one');
ERROR: column reference "name" is ambiguous
LINE 2: on conflict (name) do update
^
DETAIL: It could refer to either a PL/pgSQL variable or a table column.
QUERY: insert into test (name) values (name)
on conflict (name) do update
set name = func.name
CONTEXT: PL/pgSQL function func(text) line 3 at SQL statement
我尝试将完整的列名称指定为 on conflict (test.name)
,但无法编译,或 ((test.name))
可编译:
create or replace function func(
name text
) returns void language plpgsql as
$$
begin
insert into test (name) values (name)
on conflict ((test.name)) do -- this fails too
update set name = func.name;
end;
$$;
但它也失败了:
select * from func('two');
ERROR: invalid reference to FROM-clause entry for table "test"
LINE 2: on conflict ((test.name)) do
^
HINT: There is an entry for table "test", but it cannot be referenced from this part of the query.
QUERY: insert into test (name) values (name)
on conflict ((test.name)) do
update set name = func.name
CONTEXT: PL/pgSQL function func(text) line 3 at SQL statement
有解决办法吗?
编辑:我找到了解决方法:
on conflict on constraint test_pkey do update
其中 test_pkey
是 table 名称加上 _pkey
。我不知道这有多可靠。我还是想指定列名。
首先,name
对于变量和属性来说都是一个糟糕的名字。当你同时拥有两者时,代码将不会好看。考虑到这一点,您可以 "prefix" 带有标记块的变量(在下面的示例中 <<fn>>``), and set
variable_conflict` 以优先考虑列名,请参见下面的代码:
t=# create or replace function func(
name text
) returns void language plpgsql as
$$
#variable_conflict use_column
<<fn>>
declare name text :='blah';
begin
insert into test (name) values (name)
on conflict (name) do -- this no longer fails
update set name = fn.name;
end;
$$;
t=# insert into test select 'b';
INSERT 0 1
Time: 8.076 ms
t=# select func('b');
func
------
(1 row)
Time: 6.117 ms
t=# select * from test;
name
------
b
blah
(2 rows)
https://www.postgresql.org/docs/current/static/plpgsql-implementation.html#PLPGSQL-VAR-SUBST
By default, PL/pgSQL will report an error if a name in a SQL statement
could refer to either a variable or a table column. You can fix such a
problem by renaming the variable or column, or by qualifying the
ambiguous reference, or by telling PL/pgSQL which interpretation to
prefer.
更进一步 - 基本上整个 link 都是关于它的。
然而 - 在演示了如何使用 plpgsql 轻松完成特定任务之后,我仍然引用 namual:
The simplest solution is to rename the variable or column. A common
coding rule is to use a different naming convention for PL/pgSQL
variables than you use for column names. For example, if you
consistently name function variables v_something while none of your
column names start with v_, no conflicts will occur.
ON CONFLICT...
语法 (as documented here) 使用 唯一约束 来确定行是否冲突。您可以通过列出它包含的列(此时 Postgres "infers" 使用正确的索引)或直接命名约束来指定此唯一约束。
在您的情况下,所使用的唯一约束是在 CREATE TABLE
语句期间隐式创建的主键约束。除非您直接指定一个名称,否则这将由 DBMS 为其指定一个名称;因此您将需要查找 DBMS 给它的名称(请注意,如果您稍后重新创建模式,这可能会改变),或者在创建 table 时使用语法 [=12] 明确命名它=].
然后您可以将子句指定为 ON CONFLICT ON CONSTRAINT pk_some_name DO ...
(注意约束名称两边没有括号)。
(或者,当然,您可以更改函数以使用明确的参数名称;就我个人而言,我认为使用 p_
或 in_
之类的前缀比处理冲突更好视具体情况而定。)
鉴于此 table:
create table test (
name text primary key
);
我需要编写一个变量名与主键名冲突的 plpgsql 函数,我必须在 on conflict
子句中使用它:
create or replace function func(
name text -- this variable name...
) returns void language plpgsql as
$$
begin
insert into test (name) values (name)
on conflict (name) do update -- ...conflicts with this line
set name = func.name;
end;
$$;
这会编译,但会抛出一个不明确的列引用:
select * from func('one');
ERROR: column reference "name" is ambiguous
LINE 2: on conflict (name) do update
^
DETAIL: It could refer to either a PL/pgSQL variable or a table column.
QUERY: insert into test (name) values (name)
on conflict (name) do update
set name = func.name
CONTEXT: PL/pgSQL function func(text) line 3 at SQL statement
我尝试将完整的列名称指定为 on conflict (test.name)
,但无法编译,或 ((test.name))
可编译:
create or replace function func(
name text
) returns void language plpgsql as
$$
begin
insert into test (name) values (name)
on conflict ((test.name)) do -- this fails too
update set name = func.name;
end;
$$;
但它也失败了:
select * from func('two');
ERROR: invalid reference to FROM-clause entry for table "test"
LINE 2: on conflict ((test.name)) do
^
HINT: There is an entry for table "test", but it cannot be referenced from this part of the query.
QUERY: insert into test (name) values (name)
on conflict ((test.name)) do
update set name = func.name
CONTEXT: PL/pgSQL function func(text) line 3 at SQL statement
有解决办法吗?
编辑:我找到了解决方法:
on conflict on constraint test_pkey do update
其中 test_pkey
是 table 名称加上 _pkey
。我不知道这有多可靠。我还是想指定列名。
首先,name
对于变量和属性来说都是一个糟糕的名字。当你同时拥有两者时,代码将不会好看。考虑到这一点,您可以 "prefix" 带有标记块的变量(在下面的示例中 <<fn>>``), and set
variable_conflict` 以优先考虑列名,请参见下面的代码:
t=# create or replace function func(
name text
) returns void language plpgsql as
$$
#variable_conflict use_column
<<fn>>
declare name text :='blah';
begin
insert into test (name) values (name)
on conflict (name) do -- this no longer fails
update set name = fn.name;
end;
$$;
t=# insert into test select 'b';
INSERT 0 1
Time: 8.076 ms
t=# select func('b');
func
------
(1 row)
Time: 6.117 ms
t=# select * from test;
name
------
b
blah
(2 rows)
https://www.postgresql.org/docs/current/static/plpgsql-implementation.html#PLPGSQL-VAR-SUBST
By default, PL/pgSQL will report an error if a name in a SQL statement could refer to either a variable or a table column. You can fix such a problem by renaming the variable or column, or by qualifying the ambiguous reference, or by telling PL/pgSQL which interpretation to prefer.
更进一步 - 基本上整个 link 都是关于它的。
然而 - 在演示了如何使用 plpgsql 轻松完成特定任务之后,我仍然引用 namual:
The simplest solution is to rename the variable or column. A common coding rule is to use a different naming convention for PL/pgSQL variables than you use for column names. For example, if you consistently name function variables v_something while none of your column names start with v_, no conflicts will occur.
ON CONFLICT...
语法 (as documented here) 使用 唯一约束 来确定行是否冲突。您可以通过列出它包含的列(此时 Postgres "infers" 使用正确的索引)或直接命名约束来指定此唯一约束。
在您的情况下,所使用的唯一约束是在 CREATE TABLE
语句期间隐式创建的主键约束。除非您直接指定一个名称,否则这将由 DBMS 为其指定一个名称;因此您将需要查找 DBMS 给它的名称(请注意,如果您稍后重新创建模式,这可能会改变),或者在创建 table 时使用语法 [=12] 明确命名它=].
然后您可以将子句指定为 ON CONFLICT ON CONSTRAINT pk_some_name DO ...
(注意约束名称两边没有括号)。
(或者,当然,您可以更改函数以使用明确的参数名称;就我个人而言,我认为使用 p_
或 in_
之类的前缀比处理冲突更好视具体情况而定。)