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

新しいPostgreSQLJSONデータ型内のフィールドを変更するにはどうすればよいですか?

    更新 :PostgreSQL 9.5には、いくつかのjsonbがあります。 PostgreSQL自体の操作機能(ただし、jsonにはありません; jsonを操作するには、キャストが必要です 値)。

    2つ(またはそれ以上)のJSONオブジェクト(または配列の連結)のマージ:

    SELECT jsonb '{"a":1}' || jsonb '{"b":2}', -- will yield jsonb '{"a":1,"b":2}'
           jsonb '["a",1]' || jsonb '["b",2]'  -- will yield jsonb '["a",1,"b",2]'
    

    したがって、単純なキーを設定する 以下を使用して実行できます:

    SELECT jsonb '{"a":1}' || jsonb_build_object('<key>', '<value>')
    

    <key>の場所 文字列である必要があり、<value> to_jsonb()のタイプは何でもかまいません 受け入れます。

    JSON階層の奥深くに値を設定するjsonb_set() 関数を使用できます:

    SELECT jsonb_set('{"a":[null,{"b":[]}]}', '{a,1,b,0}', jsonb '{"c":3}')
    -- will yield jsonb '{"a":[null,{"b":[{"c":3}]}]}'
    

    jsonb_set()の完全なパラメータリスト :

    jsonb_set(target         jsonb,
              path           text[],
              new_value      jsonb,
              create_missing boolean default true)
    

    path JSON配列インデックスも含めることができ、そこに表示される負の整数はJSON配列の最後から数えます。ただし、存在しないが正のJSON配列インデックスは、配列の最後に要素を追加します。

    SELECT jsonb_set('{"a":[null,{"b":[1,2]}]}', '{a,1,b,1000}', jsonb '3', true)
    -- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}'
    

    JSON配列に挿入する場合(元の値をすべて保持しながら)jsonb_insert() 関数を使用できます( 9.6以降。この関数のみ、このセクションでは ):

    SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b,0}', jsonb '2')
    -- will yield jsonb '{"a":[null,{"b":[2,1]}]}', and
    SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b,0}', jsonb '2', true)
    -- will yield jsonb '{"a":[null,{"b":[1,2]}]}'
    

    jsonb_insert()の完全なパラメータリスト :

    jsonb_insert(target       jsonb,
                 path         text[],
                 new_value    jsonb,
                 insert_after boolean default false)
    

    繰り返しますが、pathに表示される負の整数 JSON配列の最後から数えます。

    だから、f.ex。 JSON配列の末尾への追加は、次の方法で実行できます。

    SELECT jsonb_insert('{"a":[null,{"b":[1,2]}]}', '{a,1,b,-1}', jsonb '3', true)
    -- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}', and
    

    ただし、この関数の動作は少し異なります(jsonb_set()とは異なります )path target JSONオブジェクトのキーです。その場合、キーが使用されていない場合にのみ、JSONオブジェクトの新しいキーと値のペアが追加されます。使用すると、エラーが発生します:

    SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,c}', jsonb '[2]')
    -- will yield jsonb '{"a":[null,{"b":[1],"c":[2]}]}', but
    SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b}', jsonb '[2]')
    -- will raise SQLSTATE 22023 (invalid_parameter_value): cannot replace existing key
    

    キー(またはインデックス)の削除 JSONオブジェクトから(または配列から)-を使用して実行できます 演算子:

    SELECT jsonb '{"a":1,"b":2}' - 'a', -- will yield jsonb '{"b":2}'
           jsonb '["a",1,"b",2]' - 1    -- will yield jsonb '["a","b",2]'
    

    JSON階層の奥深くから削除する #-で実行できます 演算子:

    SELECT '{"a":[null,{"b":[3.14]}]}' #- '{a,1,b,0}'
    -- will yield jsonb '{"a":[null,{"b":[]}]}'
    

    9.4の場合 、元の回答の修正バージョン(下記)を使用できますが、JSON文字列を集約する代わりに、json_object_agg()を使用してjsonオブジェクトに直接集約できます。 。

    元の回答 :純粋なSQLでも(plpythonまたはplv8なしで)可能です(ただし、9.3以降が必要です。9.2では機能しません)

    CREATE OR REPLACE FUNCTION "json_object_set_key"(
      "json"          json,
      "key_to_set"    TEXT,
      "value_to_set"  anyelement
    )
      RETURNS json
      LANGUAGE sql
      IMMUTABLE
      STRICT
    AS $function$
    SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')::json
      FROM (SELECT *
              FROM json_each("json")
             WHERE "key" <> "key_to_set"
             UNION ALL
            SELECT "key_to_set", to_json("value_to_set")) AS "fields"
    $function$;
    

    SQLFiddle

    編集

    複数のキーと値を設定するバージョン:

    CREATE OR REPLACE FUNCTION "json_object_set_keys"(
      "json"          json,
      "keys_to_set"   TEXT[],
      "values_to_set" anyarray
    )
      RETURNS json
      LANGUAGE sql
      IMMUTABLE
      STRICT
    AS $function$
    SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')::json
      FROM (SELECT *
              FROM json_each("json")
             WHERE "key" <> ALL ("keys_to_set")
             UNION ALL
            SELECT DISTINCT ON ("keys_to_set"["index"])
                   "keys_to_set"["index"],
                   CASE
                     WHEN "values_to_set"["index"] IS NULL THEN 'null'::json
                     ELSE to_json("values_to_set"["index"])
                   END
              FROM generate_subscripts("keys_to_set", 1) AS "keys"("index")
              JOIN generate_subscripts("values_to_set", 1) AS "values"("index")
             USING ("index")) AS "fields"
    $function$;
    

    編集2 :@ErwinBrandstetterが指摘したように、上記のこれらの関数は、いわゆるUPSERTのように機能します。 (フィールドが存在する場合は更新し、存在しない場合は挿入します)。これは、UPDATEのみのバリアントです。 :

    CREATE OR REPLACE FUNCTION "json_object_update_key"(
      "json"          json,
      "key_to_set"    TEXT,
      "value_to_set"  anyelement
    )
      RETURNS json
      LANGUAGE sql
      IMMUTABLE
      STRICT
    AS $function$
    SELECT CASE
      WHEN ("json" -> "key_to_set") IS NULL THEN "json"
      ELSE (SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')
              FROM (SELECT *
                      FROM json_each("json")
                     WHERE "key" <> "key_to_set"
                     UNION ALL
                    SELECT "key_to_set", to_json("value_to_set")) AS "fields")::json
    END
    $function$;
    

    編集3 :これは、(UPSERTを設定できる再帰的なバリアントです。 )キーパス(キーは内部オブジェクトのみを参照でき、内部配列はサポートされていません)にあるリーフ値(およびこの回答の最初の関数を使用):

    CREATE OR REPLACE FUNCTION "json_object_set_path"(
      "json"          json,
      "key_path"      TEXT[],
      "value_to_set"  anyelement
    )
      RETURNS json
      LANGUAGE sql
      IMMUTABLE
      STRICT
    AS $function$
    SELECT CASE COALESCE(array_length("key_path", 1), 0)
             WHEN 0 THEN to_json("value_to_set")
             WHEN 1 THEN "json_object_set_key"("json", "key_path"[l], "value_to_set")
             ELSE "json_object_set_key"(
               "json",
               "key_path"[l],
               "json_object_set_path"(
                 COALESCE(NULLIF(("json" -> "key_path"[l])::text, 'null'), '{}')::json,
                 "key_path"[l+1:u],
                 "value_to_set"
               )
             )
           END
      FROM array_lower("key_path", 1) l,
           array_upper("key_path", 1) u
    $function$;
    

    更新:既存のjsonフィールドのキーを別の指定されたキーに置き換える機能が追加されました。移行やデータ構造の修正などの他のシナリオでデータ型を更新する場合に便利です。

    CREATE OR REPLACE FUNCTION json_object_replace_key(
        json_value json,
        existing_key text,
        desired_key text)
      RETURNS json AS
    $BODY$
    SELECT COALESCE(
    (
        SELECT ('{' || string_agg(to_json(key) || ':' || value, ',') || '}')
        FROM (
            SELECT *
            FROM json_each(json_value)
            WHERE key <> existing_key
            UNION ALL
            SELECT desired_key, json_value -> existing_key
        ) AS "fields"
        -- WHERE value IS NOT NULL (Actually not required as the string_agg with value's being null will "discard" that entry)
    
    ),
        '{}'
    )::json
    $BODY$
      LANGUAGE sql IMMUTABLE STRICT
      COST 100;
    

    更新 :関数がコンパクトになりました。



    1. MariaDBがXpandでグローバルスケールを実現する方法

    2. Oracleのプラス(+)サインインを使用した左結合と右結合

    3. Oracle SQL WHERE句で(+)記号はどういう意味ですか?

    4. SQL Serverにリンクサーバーログインを追加する(T-SQLの例)