簡體   English   中英

如何消除ON CONFLICT子句中plpgsql變量名的歧義?

[英]How to disambiguate a plpgsql variable name in a ON CONFLICT clause?

鑒於此表:

create table test (
    name text primary key
);

我需要編寫一個plpgsql函數,其變量名與主鍵名on conflict ,我必須在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是表名加_pkey 我不知道這有多可靠。 我還是想指定列名。

首先, name是變量和屬性的錯誤名稱。 當你有兩者時,代碼看起來不會很好。 考慮到這一點,您可以使用帶標記的塊“加”前綴變量(在<<fn>>``), and set下面的示例中<<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

默認情況下,如果SQL語句中的名稱可以引用變量或表列,PL / pgSQL將報告錯誤。 您可以通過重命名變量或列,或通過限定模糊引用,或通過告訴PL / pgSQL更喜歡哪種解釋來解決此類問題。

而且 - 基本上整個鏈接都是關於它的。

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

最簡單的解決方案是重命名變量或列。 常見的編碼規則是對PL / pgSQL變量使用與用於列名稱不同的命名約定。 例如,如果您始終將函數變量命名為v_something,而沒有任何列名以v_開頭,則不會發生沖突。

ON CONFLICT...語法( 如此處所述 )使用唯一約束來確定行是否沖突。 您可以通過列出它包含的列(此時Postgres“推斷”要使用的正確索引)或直接命名約束來指定此唯一約束。

在您的情況下,使用的唯一約束是在CREATE TABLE語句中隱式創建的主鍵約束。 這將由DBMS給出一個名稱,除非您直接指定一個; 因此,您需要查找DBMS提供的名稱(並注意稍后重新創建模式時可能會更改),或者在使用語法CONSTRAINT pk_some_name PRIMARY KEY創建表時明確命名。

然后,您可以將該子句指定為ON CONFLICT ON CONSTRAINT pk_some_name DO ... (注意約束名稱周圍沒有括號)。

(或者,當然,您可以更改函數以使用明確的參數名稱;就個人而言,我認為使用像p_in_這樣的前綴而不是in_處理沖突是一種好習慣。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM