これはさらに簡素化および改善できます:
CREATE OR REPLACE FUNCTION some_f(_tbl regclass, OUT result integer)
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('SELECT (EXISTS (SELECT FROM %s WHERE id = 1))::int', _tbl)
INTO result;
END
$func$;
スキーマ修飾名で呼び出す(以下を参照):
SELECT some_f('myschema.mytable'); -- would fail with quote_ident()
または:
SELECT some_f('"my very uncommon table name"');
主なポイント
OUTを使用する パラメータ 機能を簡素化するため。動的SQLの結果を直接選択して実行できます。追加の変数やコードは必要ありません。
EXISTS まさにあなたが望むことをします。 trueを取得します 行が存在する場合、またはfalse そうでなければ。これを行うにはさまざまな方法があります。EXISTS 通常、最も効率的です。
整数が必要なようです 戻ったので、booleanをキャストしました EXISTSの結果 integer 、それはあなたが持っていたものを正確に生み出します。 ブール値を返します 代わりに。
オブジェクト識別子タイプregclassを使用しています _tblの入力タイプとして 。これですべてのquote_ident(_tbl) またはformat('%I', _tbl) できますが、より良い理由は次のとおりです。
-
..SQLインジェクションを防ぎます 同様に。
-
..テーブル名が無効であるか、存在しないか、現在のユーザーに表示されていない場合は、すぐに失敗し、より適切に失敗します。 (
regclassパラメータは既存にのみ適用されます テーブル。) -
..スキーマ修飾されたテーブル名で機能します。ここでプレーンな
quote_ident(_tbl)またはformat(%I)あいまいさを解決できないため、失敗します。スキーマ名とテーブル名を別々に渡したりエスケープしたりする必要があります。
既存でのみ機能します 明らかに、テーブル。
私はまだformat()を使用しています 、構文を単純化するため(そして、その使用方法を示すため)、ただし%s %Iの代わりに 。通常、クエリはより複雑なので、format() もっと役立ちます。簡単な例では、連結することもできます:
EXECUTE 'SELECT (EXISTS (SELECT FROM ' || _tbl || ' WHERE id = 1))::int'
idをテーブル修飾する必要はありません FROMにはテーブルが1つしかないのに列 リスト。この例では、あいまいさはあり得ません。 (動的)EXECUTE内のSQLコマンド 個別のスコープがある 、関数本体のプレーンSQLコマンドとは対照的に、関数変数またはパラメーターは表示されません。
これがあなたが常にする理由です 動的SQLのユーザー入力を適切にエスケープします:
db<>ここでフィドル SQLインジェクションのデモンストレーション
古いsqlfiddle