hstore
を使用した高速化
Postgres 9.0以降 、追加モジュール hstore
データベースにインストールされているのは、 #=
を使用した非常にシンプルで高速なソリューションです。 その演算子...
record
のreplace[s]フィールドhstore
の値が一致している 。
モジュールをインストールするには:
CREATE EXTENSION hstore;
例:
SELECT my_record #= '"field"=>"value"'::hstore; -- with string literal
SELECT my_record #= hstore(field, value); -- with values
値はtext
にキャストする必要があります そして戻って、明らかに。
詳細を含むplpgsql関数の例:
- トリガー機能のエンドレスループ
- PostgresトリガーのキーでNEWに割り当てます
json
で動作するようになりました / jsonb
、また!
json
にも同様のソリューションがあります (9.3ページ以上)または jsonb
(9.4ページ以上)
SELECT json_populate_record (my_record, json_build_object('key', 'new-value');
機能は文書化されていませんが、Postgres 13以降の公式です。マニュアル:
ただし、baseがNULLでない場合、baseに含まれる値は、一致しない列に使用されます。
したがって、既存の行を取得して任意のフィールドに入力できます(その中の行を上書きします)。
json
の主な利点 vs hstore
:
- ストックのPostgresと連携するため、追加のモジュールは必要ありません。
- ネストされた配列および複合型でも機能します。
マイナーな欠点:少し遅い。
詳細については、@Geirの追加回答を参照してください。
hstore
なし およびjson
古いバージョンを使用している場合、または追加のモジュール hstore
をインストールできない場合 またはそれがインストールされていると仮定することはできません、これは私が以前に投稿したものの改善されたバージョンです。 hstore
よりもまだ遅い ただし、演算子:
CREATE OR REPLACE FUNCTION f_setfield(INOUT _comp_val anyelement
, _field text, _val text)
RETURNS anyelement
LANGUAGE plpgsql STABLE AS
$func$
BEGIN
EXECUTE 'SELECT ' || array_to_string(ARRAY(
SELECT CASE WHEN attname = _field
THEN '$2'
ELSE '($1).' || quote_ident(attname)
END AS fld
FROM pg_catalog.pg_attribute
WHERE attrelid = pg_typeof(_comp_val)::text::regclass
AND attnum > 0
AND attisdropped = FALSE
ORDER BY attnum
), ',')
USING _comp_val, _val
INTO _comp_val;
END
$func$;
電話:
CREATE TEMP TABLE t( a int, b text); -- Composite type for testing
SELECT f_setfield(NULL::t, 'a', '1');
メモ
-
値
_val
の明示的なキャスト ターゲットデータ型に合わせる必要はありません。動的クエリの文字列リテラルは自動的に強制変換され、pg_type
のサブクエリが不要になります。 。しかし、私はそれをさらに一歩進めました: -
quote_literal(_val)
を置き換えますUSING
を介して直接値を挿入する 句。 1つの関数呼び出しと2つのキャストを節約し、とにかく安全です。テキストコード> 最新のPostgreSQLでは自動的にターゲットタイプに強制変換されます。 (9.1より前のバージョンではテストしていません。)
-
array_to_string(ARRAY())
string_agg()
よりも高速です 。 -
変数は必要ありません、
DECLARE
。割り当てが少なくなります。 -
動的SQLにサブクエリはありません。
($ 1).field
より高速です。 -
pg_typeof(_comp_val)::text ::regclass
は、
(SELECT typrelid FROM pg_catalog.pg_type WHERE oid =pg_typeof($ 1)::oid)
と同じです。
有効な複合タイプの場合、より高速になります。
この最後の変更は、pg_type.typname
を前提として構築されています。 関連するpg_class.relname
と常に同じです 登録された複合タイプの場合、ダブルキャストでサブクエリを置き換えることができます。このテストを大きなデータベースで実行して確認しましたが、期待どおりに空になりました:
SELECT *
FROM pg_catalog.pg_type t
JOIN pg_namespace n ON n.oid = t.typnamespace
WHERE t.typrelid > 0 -- exclude non-composite types
AND t.typrelid IS DISTINCT FROM
(quote_ident(n.nspname ) || '.' || quote_ident(typname))::regclass
-
INOUT
の使用 パラメータを使用すると、明示的なRETURN
が不要になります。 。これは単なる表記上のショートカットです。 Pavelはそれを気に入らないでしょう、彼は明示的なRETURN
を好みます ステートメント...
すべてをまとめると、これは2倍の速さです。 以前のバージョンと同じです。
元の(時代遅れの)回答:
その結果、〜2.25倍高速のバージョンになります。 。しかし、Pavelの2番目のバージョンに基づいて構築しなければ、おそらくそれを行うことはできなかったでしょう。
さらに、このバージョンはほとんどのキャストを回避します 単一のクエリ内ですべてを実行することでテキストをやり取りするため、エラーが発生しにくくなります。
PostgreSQL9.0および9.1でテスト済み 。
CREATE FUNCTION f_setfield(_comp_val anyelement, _field text, _val text)
RETURNS anyelement
LANGUAGE plpgsql STABLE AS
$func$
DECLARE
_list text;
BEGIN
_list := (
SELECT string_agg(x.fld, ',')
FROM (
SELECT CASE WHEN a.attname = $2
THEN quote_literal($3) || '::'|| (SELECT quote_ident(typname)
FROM pg_catalog.pg_type
WHERE oid = a.atttypid)
ELSE quote_ident(a.attname)
END AS fld
FROM pg_catalog.pg_attribute a
WHERE a.attrelid = (SELECT typrelid
FROM pg_catalog.pg_type
WHERE oid = pg_typeof($1)::oid)
AND a.attnum > 0
AND a.attisdropped = false
ORDER BY a.attnum
) x
);
EXECUTE 'SELECT ' || _list || ' FROM (SELECT $1.*) x'
USING $1
INTO $1;
RETURN $1;
END
$func$;