sql >> データベース >  >> RDS >> PostgreSQL

動的SQLを使用して複合変数フィールドの値を設定する方法

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


    1. 非同期I/Oを多用するコードは、非非同期よりも実行速度が遅いのはなぜですか?

    2. SQL Server SHOWPLAN_TEXT

    3. GROUP_CONCAT ORDER BY

    4. 既存の権限を持つユーザーをすばやく削除する方法