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

JSON値として行を返すPostgres関数

    2つの大きな問題があります:
    1。 UPDATEを置くことはできません サブクエリにまったく data-modifyingで解決できます。 CTE パトリックが示す のように 、しかし、それは目前のケースに必要なものよりも高価で冗長です。
    2。 潜在的に危険な名前の競合があります 、それはまだ対処されていません。

    より良いクエリ/機能

    SQL関数ラッパーは今のところ脇に置いておきます(これに戻ります)。簡単なUPDATEを使用できます RETURNINGを使用 条項:

    UPDATE tbl
    SET    value1 = 'something_new'
    WHERE  id = 123
    RETURNING row_to_json(ROW(value1, value2));
    

    RETURNING 句は、更新された行の列を含む任意の式を許可します。これは、データ変更CTEよりも短く安価です。

    残りの問題:行コンストラクター ROW(...) 列名は保持されないため(これは既知の弱点です)、JSON値で汎用キーを取得します:

    row_to_json
    {"f1":"something_new","f2":"what ever is in value2"}
    

    Postgres 9.3では、最初のステップをカプセル化するためのCTE別の関数、または明確に定義された行タイプへのキャストが必要になります。詳細:

    Postgresでは9.4 <を使用するだけです。 code> json_build_object() またはjson_object()

    UPDATE tbl
    SET    value1 = 'something_new'
    WHERE  id = 123
    RETURNING json_build_object('value1', value1, 'value2', value2);

    または:

    ...
    RETURNING json_object('{value1, value2}', ARRAY[value1, value2]);

    これで、元の列名またはキー名として選択したものを取得できます:

    row_to_json
    {"value1":"something_new","value2":"what ever is in value2"}
    

    これを関数でラップするのは簡単です。これにより、2番目の問題が発生します...

    名前の競合

    元の関数では、関数パラメーターと列名に同じ名前を使用します。これは一般的に非常に悪い考えです 。どの識別子がどのスコープで最初に来るかをよく理解する必要があります。

    手元の場合、結果はまったくナンセンスです:

    Create Or Replace Function ExampleTable_Update (id bigint, value1 text) Returns 
    ...
        Update ExampleTable
        Set Value1 = value1
        Where id = id
        Returning Value1, Value2;
    ...
    $$ Language SQL;

    idの2番目のインスタンスを期待しているようですが 関数パラメータを参照しますが、参照しません。列名はSQLステートメントのスコープ内で最初に来て、2番目のインスタンスは列を参照します。その結果、常に trueの式になります idのNULL値を除く 。したがって、すべての行を更新します。 データの壊滅的な損失につながる可能性があります さらに悪いことに、SQL関数は 1つを返すため、後で理解することさえできないかもしれません。 RETURNINGで定義されている任意の行 関数の句( oneを返します 行のセットではなく、行)。

    この特定のケースでは、 value1 =value1 もあるため、「ラッキー」になります。 、これは列を既存の値で上書きし、効果的に..非常に高価な方法で何もしません(トリガーが何かをしない限り)。 value1が変更されていない任意の行を取得することに戸惑うかもしれません。 結果として。

    だから、しないでください。

    何をしているのかを正確に理解していない限り、このような潜在的な名前の競合は避けてください(明らかにそうではありません)。私が好きな規則の1つは、関数のパラメーター名と変数名の前にアンダースコアを付けることですが、列名はアンダースコアで始まることはありません。多くの場合、位置参照を使用して明確にすることができます: $ 1 $ 2 、...、しかしそれは問題の半分だけを回避します。 名前の競合を回避する限り、どの方法でも問題ありません。 。私が提案するもの:

    CREATE OR REPLACE FUNCTION foo (_id bigint, _value1 text)
       RETURNS json AS
    $func$
    UPDATE tbl
    SET    value1 = _value1
    WHERE  id     = _id
    RETURNING json_build_object('value1', value1, 'value2', value2);
    $func$  LANGUAGE sql;
    

    また、これは実際の列の値を返すことにも注意してください value1 UPDATEの後 、入力パラメータ _value1と同じ場合と同じでない場合があります 。データベースルールまたはトリガーが干渉している可能性があります...



    1. Hibernate(EntityManager)またはJPAを使用してOracle関数またはプロシージャを呼び出す方法

    2. ツールボックスから取り出す非推奨の機能–パート2

    3. SQLServerフェールオーバークラスターのインストール-2

    4. mysqlで友達リストを設定する