sql >> Database >  >> RDS >> PostgreSQL

Hoe wijzig ik velden in het nieuwe PostgreSQL JSON-gegevenstype?

Bijwerken :Met PostgreSQL 9.5 zijn er enkele jsonb manipulatiefunctionaliteit binnen PostgreSQL zelf (maar geen voor json; casts zijn vereist om json te manipuleren waarden).

2 (of meer) JSON-objecten samenvoegen (of arrays aaneenschakelen):

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]'

Dus, een eenvoudige sleutel instellen kan worden gedaan met:

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

Waar <key> moet string zijn, en <value> kan van elk type zijn to_jsonb() accepteert.

Voor het instellen van een waarde diep in een JSON-hiërarchie , de jsonb_set() functie kan worden gebruikt:

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

Volledige parameterlijst van jsonb_set() :

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

path kan ook JSON-array-indexen bevatten en negatieve gehele getallen die daar verschijnen, tellen vanaf het einde van JSON-arrays. Een niet-bestaande, maar positieve JSON-array-index zal het element echter aan het einde van de array toevoegen:

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

Voor invoegen in JSON-array (met behoud van alle oorspronkelijke waarden) , de jsonb_insert() functie kan worden gebruikt (in 9.6+; alleen deze functie, in deze sectie ):

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]}]}'

Volledige parameterlijst van jsonb_insert() :

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

Nogmaals, negatieve gehele getallen die verschijnen in path tel vanaf het einde van JSON-arrays.

Dus bijv. toevoegen aan een einde van een JSON-array kan met:

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

Deze functie werkt echter iets anders (dan jsonb_set() ) wanneer het path in target is de sleutel van een JSON-object. In dat geval wordt er alleen een nieuw sleutel-waardepaar voor het JSON-object toegevoegd als de sleutel niet wordt gebruikt. Als het wordt gebruikt, geeft het een foutmelding:

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

Een sleutel (of een index) verwijderen van een JSON-object (of, van een array) kan worden gedaan met de - operator:

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

Verwijderen, diep in een JSON-hiërarchie kan worden gedaan met de #- operator:

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

Voor 9,4 , kunt u een aangepaste versie van het oorspronkelijke antwoord (hieronder) gebruiken, maar in plaats van een JSON-tekenreeks samen te voegen, kunt u rechtstreeks met json_object_agg() aggregeren tot een json-object .

Oorspronkelijk antwoord :Het is ook mogelijk (zonder plpython of plv8) in pure SQL (maar heeft 9.3+ nodig, werkt niet met 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

Bewerken :

Een versie die meerdere sleutels en waarden instelt:

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

Bewerk 2 :zoals @ErwinBrandstetter opmerkte, werken deze functies hierboven als een zogenaamde UPSERT (werkt een veld bij als het bestaat, voegt in als het niet bestaat). Hier is een variant, die alleen 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$;

Bewerk 3 :Hier is een recursieve variant, die kan worden ingesteld (UPSERT ) een bladwaarde (en gebruikt de eerste functie van dit antwoord), gelegen op een sleutelpad (waar sleutels alleen kunnen verwijzen naar binnenobjecten, binnenarrays niet ondersteund):

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

Bijgewerkt:functie toegevoegd voor het vervangen van de sleutel van een bestaand json-veld door een andere gegeven sleutel. Kan handig zijn voor het bijwerken van gegevenstypen bij migraties of andere scenario's, zoals het wijzigen van de gegevensstructuur.

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;

Bijwerken :functies zijn nu gecomprimeerd.



  1. Hoe u een lijst met ingeschakelde / uitgeschakelde controlebeperkingen in SQL Server-database kunt krijgen - SQL Server / TSQL-zelfstudie, deel 86

  2. Echte escape-tekenreeks en PDO

  3. ReadyCloud ReadyShipper X

  4. Een telefoonnummer opmaken in SQL Server (T-SQL)