如何在 ON CONFLICT 子句中消除 plpgsql 变量名称的歧义?

Tob*_*bia 6 sql postgresql upsert plpgsql name-conflict

鉴于此表:

create table test (
    name text primary key
);
Run Code Online (Sandbox Code Playgroud)

我需要编写一个变量名与主键名冲突的 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; 
$$;
Run Code Online (Sandbox Code Playgroud)

这会编译,但会抛出一个不明确的列引用:

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
Run Code Online (Sandbox Code Playgroud)

我尝试将完整的列名指定为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; 
$$;
Run Code Online (Sandbox Code Playgroud)

但它也失败了:

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
Run Code Online (Sandbox Code Playgroud)

有解决办法吗?


编辑:我找到了一个解决方法:

on conflict on constraint test_pkey do update
Run Code Online (Sandbox Code Playgroud)

test_pkey表名加在哪里_pkey。我不知道这有多可靠。我仍然想指定列名。

Vao*_*sun 9

首先,name对于变量和属性来说,这都是一个不好的名字。当两者都存在时,代码看起来不会很好。考虑到这一点,您可以使用带标签的块“前缀”变量(在下面的示例中<<fn>>),并设置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)
Run Code Online (Sandbox Code Playgroud)

https://www.postgresql.org/docs/current/static/plpgsql-implementation.html#PLPGSQL-VAR-SUBST

默认情况下,如果 SQL 语句中的名称可以引用变量或表列,PL/pgSQL 将报告错误。您可以通过重命名变量或列、限定不明确的引用或告诉 PL/pgSQL 更喜欢哪种解释来解决此类问题。

更进一步-基本上整个链接都是关于它的。

然而,在演示了如何使用 plpgsql 轻松完成特定任务之后,我仍然引用 namual:

最简单的解决方案是重命名变量或列。一个常见的编码规则是对 PL/pgSQL 变量使用与列名不同的命名约定。例如,如果您始终将函数变量命名为 v_something,而没有任何列名称以 v_ 开头,则不会发生冲突。