2014-05-06 10 views
20

Ich muss einige Attribute aus einer JSON-Typ-Spalte entfernen.PostgreSQL: Attribut aus der JSON-Spalte entfernen

Die Tabelle:

CREATE TABLE my_table(id VARCHAR(80), data json); 
INSERT INTO my_table (id, data) VALUES (
    'A', 
    '{"attrA":1,"attrB":true,"attrC":["a", "b", "c"]}' 
); 

Nun, ich brauche attrB aus Spalte data zu entfernen.

Etwas wie wäre nett. Aber ein Weg mit einem temporären Tisch wäre auch genug.

+0

Was ist Ihre Postgres-Version? – pozs

Antwort

32

aktualisieren: für 9.5+, gibt es explizite Operatoren, die Sie mit jsonb verwenden können (wenn Sie eine json getippt Spalte haben, können Sie Abgüsse verwenden, um eine Änderung zu beantragen):

Löschen eines Schlüssels (oder ein index) von einem JSON Objekt (oder aus einem Array) kann mit dem - Führer durchgeführt werden:

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

Löschen, aus der tiefe in einer JSON Hierarchie kann mit dem #- Führer durchgeführt werden:

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

Für 9.4 können Sie eine modifizierte Version der ursprünglichen Antwort (unten) verwenden, sondern stattdessen einen JSON-String zu aggregieren, können Sie in ein json Objekt direkt mit json_object_agg() aggregieren.

Verwandte: andere JSON Manipulationen whithin PostgreSQL:

Ursprüngliche Antwort (gilt für PostgreSQL 9.3):

Wenn Sie mindestens PostgreSQL 9.3 haben, können Sie Teilen Sie Ihr Objekt in Paare mit json_each() und filtern Sie Ihre unerwünschten Felder, dann bauen Sie den JSON wieder manuell auf. Etwas wie:

SELECT data::text::json AS before, 
     ('{' || array_to_string(array_agg(to_json(l.key) || ':' || l.value), ',') || '}')::json AS after 
FROM (VALUES ('{"attrA":1,"attrB":true,"attrC":["a","b","c"]}'::json)) AS v(data), 
LATERAL (SELECT * FROM json_each(data) WHERE "key" <> 'attrB') AS l 
GROUP BY data::text 

Mit 9.2 (oder niedriger) ist es nicht möglich.

bearbeiten:

Eine bequemere Form ist, eine Funktion zu schaffen, die eine beliebige Anzahl von Attributen in einem json Feld entfernen:

Edit 2: string_agg() ist weniger teuer als array_to_string(array_agg())

CREATE OR REPLACE FUNCTION "json_object_delete_keys"("json" json, VARIADIC "keys_to_delete" TEXT[]) 
    RETURNS json 
    LANGUAGE sql 
    IMMUTABLE 
    STRICT 
AS $function$ 
SELECT COALESCE(
    (SELECT ('{' || string_agg(to_json("key") || ':' || "value", ',') || '}') 
    FROM json_each("json") 
    WHERE "key" <> ALL ("keys_to_delete")), 
    '{}' 
)::json 
$function$; 

Mit dieser Funktion müssen Sie nur die Abfrage belo ausführen w:

UPDATE my_table 
SET data = json_object_delete_keys(data, 'attrB'); 
+0

Funktioniert es mit verschachtelten Schlüsseln? Sub-Ebenen –

+0

@RezaS nicht direkt dies, aber Sie können die rekursive Variante (in http://stackoverflow.com/a/23500670/1499698) damit kombinieren (siehe meinen Kommentar dort). – pozs

+0

Benötigen Sie die ', {}' in der Abfrage? Ich denke, dass Sie das auch weglassen können –

1

Eine andere bequeme Möglichkeit, dies zu tun, ist die Verwendung von Hstore-Erweiterung. Auf diese Weise können Sie bequemere Funktionen zum Setzen/Löschen von Schlüsseln in ein JSON-Objekt schreiben.Ich kam mit folgenden Funktion bis zu tun das gleiche:

CREATE OR REPLACE FUNCTION remove_key(json_in json, key_name text) 
RETURNS json AS $$ 
DECLARE item json; 
DECLARE fields hstore; 
BEGIN 
-- Initialize the hstore with desired key being set to NULL 
fields := hstore(key_name,NULL); 

-- Parse through Input Json and push each key into hstore 
FOR item IN SELECT row_to_json(r.*) FROM json_each_text(json_in) AS r 
LOOP 
    --RAISE NOTICE 'Parsing Item % %', item->>'key', item->>'value'; 
    fields := (fields::hstore || hstore(item->>'key', item->>'value')); 
END LOOP; 
--RAISE NOTICE 'Result %', hstore_to_json(fields); 
-- Remove the desired key from store 
fields := fields-key_name; 

RETURN hstore_to_json(fields); 
END; 
$$ LANGUAGE plpgsql 
SECURITY DEFINER 
STRICT; 

Ein einfaches Beispiel für die Verwendung ist:

SELECT remove_key(('{"Name":"My Name", "Items" :[{ "Id" : 1, "Name" : "Name 1"}, { "Id" : 2, "Name 2" : "Item2 Name"}]}')::json, 'Name'); 
-- Result 
"{"Items": "[{ \"Id\" : 1, \"Name\" : \"Name 1\"}, { \"Id\" : 2, \"Name 2\" : \"Item2 Name\"}]"}" 

Ich habe eine andere Funktion, um den set_key Betrieb sowie folgende zu tun:

CREATE OR REPLACE FUNCTION set_key(json_in json, key_name text, key_value text) 
RETURNS json AS $$ 
DECLARE item json; 
DECLARE fields hstore; 
BEGIN 
-- Initialize the hstore with desired key value 
fields := hstore(key_name,key_value); 

-- Parse through Input Json and push each key into hstore 
FOR item IN SELECT row_to_json(r.*) FROM json_each_text(json_in) AS r 
LOOP 
    --RAISE NOTICE 'Parsing Item % %', item->>'key', item->>'value'; 
    fields := (fields::hstore || hstore(item->>'key', item->>'value')); 
END LOOP; 
--RAISE NOTICE 'Result %', hstore_to_json(fields); 
RETURN hstore_to_json(fields); 
END; 
$$ LANGUAGE plpgsql 
SECURITY DEFINER 
STRICT; 

Ich habe dies mehr in meinem blog hier diskutiert.

2

Es ist ein hässlicher Hack, aber wenn attrB ist nicht Ihre erste Schlüssel und es erscheint nur einmal, dann können Sie Folgendes tun:

UPDATE my_table SET data=REPLACE(data::text, ',"attrB":' || (doc->'attrB')::text, '')::json; 
+0

Nicht so schlecht. Sie könnten es mit 'regexp_replace' verbessern - machen Sie das Komma optional und es würde auf jeden Fall funktionieren. –

23

So viel einfacher geworden ist mit PostgreSQL 9.5 JSONB Typs. Siehe JSONB-Operatoren dokumentiert here.

Sie können ein Top-Level-Attribut mit dem Operator "-" entfernen.

Sie können dies innerhalb eines Aktualisierungsaufrufs verwenden, um ein vorhandenes JSONB-Feld zu aktualisieren.

UPDATE my_table SET data = data - 'attrB' 

Sie können den Attributnamen auch dynamisch über Parameter angeben, wenn er in einer Funktion verwendet wird.

CREATE OR REPLACE FUNCTION delete_mytable_data_key(
    _id integer, 
    _key character varying) 
    RETURNS void AS 
$BODY$ 
BEGIN 
    UPDATE my_table SET 
     data = data - _key 
    WHERE id = _id; 
END; 
$BODY$ 
    LANGUAGE plpgsql VOLATILE 
    COST 100; 

Der Reverse-Operator ist das "||", um zwei JSONB-Pakete miteinander zu verketten. Beachten Sie, dass die rechte Verwendung des Attributs alle vorherigen überschreibt.

SELECT '{"a": true, "c": true}'::jsonb || '{"a": false, "b": 2}'::jsonb 
// -> {"a": false, "b": 2, "c": true} 
0

Ich konnte nicht SELECT '{"a": "b"}'::jsonb - 'a'; in 9.5.2 arbeiten. Allerdings hat SELECT '{"a": "b"}'::jsonb #- '{a}'; funktioniert!

0

Während dies in 9.5+ mit den jsonb-Operatoren sicherlich einfacher ist, ist die Funktion, die pozs schrieb, um mehrere Schlüssel zu entfernen, immer noch nützlich. Wenn beispielsweise die zu entfernenden Schlüssel in einer Tabelle gespeichert sind, können Sie sie mit der Funktion entfernen. Hier ist eine aktualisierte Funktion, mit jsonb und postgresql 9.5+:

CREATE FUNCTION remove_multiple_keys(IN object jsonb, 
            variadic keys_to_delete text[], 
            OUT jsonb) 
    IMMUTABLE 
    STRICT 
    LANGUAGE SQL 
AS 
$$ 
    SELECT jsonb_object_agg(key, value) 
    FROM (SELECT key, value 
      FROM jsonb_each("object") 
      WHERE NOT (key = ANY("keys_to_delete")) 
    ) each_subselect 
$$ 
; 

Wenn die Schlüssel entfernt werden sollen, werden in einer Tabelle gespeichert, (zB in der Spalte „Schlüssel“ der Tabelle „table_with_keys“) Sie nennen könnte diese funktion wie folgt:

SELECT remove_multiple_keys(my_json_object, 
          VARIADIC (SELECT array_agg(keys) FROM table_with_keys)); 
Verwandte Themen