電子メールの値がテーブルに存在しない場合に電子メールを挿入し、行のemail_id
を返す関数を作成したいと思います。これどうやってするの?
また、メールが挿入されておらず、DBに既に存在する場合、IDを返すにはどうすればよいですか?別のSELECT
を実行する必要がありますか?
BEGIN;
LOCK TABLE mailing_list IN SHARE ROW EXCLUSIVE MODE;
INSERT INTO mailing_list (email)
SELECT 'email'
WHERE NOT EXISTS (
SELECT * FROM mailing_list WHERE email='email'
);
COMMIT;
returning id
を追加しようとしましたが、機能しません。私は得ました:
クエリには結果データの宛先がありません
@ a_horseすでに説明済み 表示されたエラーメッセージを回避する方法。
以下は、これまで言及してきた関連バージョンの簡単なバリアントです。
CREATE OR REPLACE FUNCTION f_email_insel(_email text, OUT email_id int) AS
$func$
BEGIN
LOOP
BEGIN -- start inner block inside loop to handle possible exception
SELECT INTO email_id m.email_id FROM mailing_list m WHERE m.email = _email;
IF NOT FOUND THEN
INSERT INTO mailing_list (email) VALUES (_email)
RETURNING mailing_list.email_id INTO email_id;
END IF;
EXCEPTION WHEN UNIQUE_VIOLATION THEN -- inserted in concurrent session.
RAISE NOTICE 'It actually happened!'; -- hardly ever happens
END;
EXIT WHEN email_id IS NOT NULL; -- else keep looping
END LOOP;
END
$func$ LANGUAGE plpgsql;
考えられる競合状態を処理するために必要なループは次のとおりです。同時トランザクションがemail
とSELECT
の間に同じINSERT
値を書き込むと、一意の違反が発生します-ここで適切に処理されます。これは、明らかにUNIQUE
に対するUNIQUE
制約(またはemail
インデックス)を想定しています。
CTEの代替 はoneSQLステートメントとして実行されます。したがって、ここではオーバーヘッドはわずかに小さくなっていますが(単純なクエリ)、競合状態の時間枠はわずかに大きくなっています。特に、行がすでに頻繁に存在する場合、これは少し高速です。
あちこちで詳細な説明を読み、ユースケースに最適なアプローチを選択してください。
返す値をどこかに保存する必要があります。
CREATE OR REPLACE FUNCTION f_get(ikey text)
returns integer
AS
$func$
DECLARE
l_id integer;
BEGIN
LOCK TABLE foo IN SHARE ROW EXCLUSIVE MODE;
INSERT INTO foo (type)
SELECT ikey
WHERE NOT EXISTS (
SELECT * FROM foo WHERE type=ikey
)
returning id into l_id; --< store the returned ID in local variable
return l_id; --< return this variable
END
$func$ LANGUAGE plpgsql;
元の関数とOUT
パラメータを使用してそれを行うこともできます。その場合は、returning id into out_key;