2017-12-06 9 views
3

は、このテーブルを考える:ON CONFLICT句でplpgsql変数名を明確にする方法はありますか?

create table test (
    name text primary key 
); 

私はon conflict句で使用する必要があり、主キーの名前と衝突する変数名とplpgsqlが機能を記述する必要があります。

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 

完全列名をとして指定しようとしましたが

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; 
$$; 

をしかし、それは同様に失敗します:コンパイルされません、または((test.name))コンパイルどのソリューションは

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 

ありますか?


編集:test_pkeyは、テーブル名プラス_pkeyです

on conflict on constraint test_pkey do update 

:私は回避策を見つけました。私はそれがいかに信頼できるか分かりません。私はまだ代わりに列名を指定したいと思います。

答えて

3

から始まるnameは、変数と属性の両方に悪い名前です。両方をお持ちの場合、コードは良く見えません。デフォルトでは

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

、PL:このことを念頭に置いて、あなたは、ラベルされたブロック(<<fn>>``), and set variable_conflict`以下の例では、以下のコードを参照して、列名を優先すると、「接頭辞」変数ことができます/ pgSQLは、SQL文 の名前が変数またはテーブル列のいずれかを参照できる場合、エラーを報告します。 問題を修正するには、変数または列の名前を変更するか、またはあいまいな参照 を修飾するか、 PL/pgSQLと解釈すると が好きです。

さらに基本的にはリンク全体についてです。まだ

そして - これは簡単にplpgsqlがで行うことができますどのように特定のタスクを実証した後、私はまだ引用namual:

最も簡単な解決策は、変数または列の名前を変更することです。一般的な のコーディング規則では、PL/pgSQL 変数に、列名に使用するものとは異なる命名規則を使用します。たとえば、 の関数名にv_somethingという名前を付けた場合、 列名のいずれもv_で始まらない場合、競合は発生しません。

+0

なぜですか?私はOPのコードを編集しました。 –

+0

@IMSoP私はちょっと精巧な答えを出しました - もっと理解できますか? –

+0

素晴らしい!興味のない場合は、 '#variable_conflict'ルールを適用すると、'((name)) 'の余分な括弧が必要ですか?'(name) 'も同様ですか?マニュアルを読むと、余分な角括弧が "インデックスの列名"ではなく "インデックス式"を指定しているように見えますが、これが名前解決に何らかの違いをもたらすのではないかと思います。 – IMSoP

1

ON CONFLICT...構文(as documented here)は、行の競合かどうかを決定するために一意制約を使用します。このユニークな制約は、それに含まれるカラム(Postgresが使用する正しいインデックスを推測するポイント)を列挙するか、または制約に直接名前を付けることによって指定できます。

あなたのケースでは、一意制約は、CREATE TABLEステートメントの間に暗黙的に作成された主キー制約です。これは、直接指定しない限り、DBMSによって与えられた名前を持ちます。 DBMSが指定した名前を参照するか(後でスキーマを再作成すると変更される可能性があることに注意してください)、構文CONSTRAINT pk_some_name PRIMARY KEYを使用してテーブルを作成するときに明示的に名前を付ける必要があります。

次に、句をON CONFLICT ON CONSTRAINT pk_some_name DO ...と指定します(制約名の前後に角括弧はありません)。

(もちろん、明白なパラメータ名を使用するように関数を変更することもできますが、個人的には、ケースごとに競合を処理するのではなく、p_またはin_などの接頭辞を使用することをおすすめします。 )

関連する問題