Un documento JSONB casi nunca es definitivo: claves sueltas, banderas temporales y campos con secretos deben desaparecer antes de que la fila salga hacia el exterior. PostgreSQL trae dos operadores de borrado — - para el nivel superior y #- para una ruta anidada. Ambos devuelven un documento nuevo y nunca modifican el original.
El operador -: una clave o un indice de array
El operando izquierdo es jsonb; el derecho indica que quitar. Una cadena elimina una clave de nivel superior; un entero elimina un elemento del array por su indice, donde un indice negativo cuenta desde el final.
SELECT '{"name": "Ann", "temp": true}'::jsonb - 'temp';
SELECT '["a", "b", "c"]'::jsonb - 1;
SELECT '["a", "b", "c"]'::jsonb - -1;
Supongamos que users tiene una columna profile jsonb con ajustes. Quitemos una clave temporal en todas las filas:
UPDATE users
SET profile = profile - 'onboarding_temp'
WHERE profile ? 'onboarding_temp';
Borrar varias claves a la vez
Cuando el operando derecho es un array text[], - elimina todas las claves de nivel superior listadas en una sola pasada. Resulta comodo para sanear un documento antes de serializarlo en una respuesta de API.
SELECT '{"a": 1, "b": 2, "c": 3}'::jsonb - '{a,c}'::text[];
SELECT id, email,
profile - '{password_hash,internal_notes,ssn}'::text[] AS public_profile
FROM users
WHERE id = 42;
Atencion: la forma con array solo afecta al nivel superior. No alcanza los campos anidados — para eso necesitas #-.
El operador #-: un valor en una ruta anidada
#- recibe a la derecha una ruta como text[] y elimina lo que haya al final de esa ruta. Los elementos de la ruta son nombres de clave e indices de array, en orden de anidamiento.
SELECT '{"user": {"name": "Ann", "secret": "x"}}'::jsonb #- '{user,secret}';
SELECT '{"orders": [{"id": 1, "card": "4111"}]}'::jsonb #- '{orders,0,card}';
En la practica #- suele limpiar datos de registro en orders.meta jsonb antes de una exportacion:
UPDATE orders
SET meta = meta #- '{payment,raw_card_number}'
WHERE status = 'paid'
AND meta #> '{payment,raw_card_number}' IS NOT NULL;
Trampas y combinacion con jsonb_set
- Una clave o ruta inexistente no es un error: el operador devuelve el documento sin cambios. Filtra con
? o #> para no lanzar UPDATEs vacios.
- y #- siempre devuelven un nuevo jsonb. Para persistir el cambio necesitas UPDATE ... SET col = col - ....
- Si un indice GIN cubre la columna JSONB, un
UPDATE masivo lo reconstruye — en tablas grandes eso no es nada barato.
- En una ruta de
#- un numero se interpreta como indice de array; asegurate de que el nodo sea realmente un array, o el elemento no se encontrara.
El borrado suele ir junto a jsonb_set: una expresion descarta la parte no deseada, la otra escribe un valor nuevo.
UPDATE orders
SET meta = jsonb_set(meta #- '{payment,cvv}', '{redacted}', 'true'::jsonb)
WHERE id = 100;
En MySQL el equivalente es JSON_REMOVE(doc, '$.path'), que acepta una o varias rutas. En ClickHouse las columnas JSON suelen ser inmutables, asi que un borrado se expresa con un INSERT del valor reconstruido en vez de un operador puntual.
Un documento JSONB casi nunca es definitivo: claves sueltas, banderas temporales y campos con secretos deben desaparecer antes de que la fila salga hacia el exterior. PostgreSQL trae dos operadores de borrado —
-para el nivel superior y#-para una ruta anidada. Ambos devuelven un documento nuevo y nunca modifican el original.El operador
-: una clave o un indice de arrayEl operando izquierdo es
jsonb; el derecho indica que quitar. Una cadena elimina una clave de nivel superior; un entero elimina un elemento del array por su indice, donde un indice negativo cuenta desde el final.-- drop a single top-level key SELECT '{"name": "Ann", "temp": true}'::jsonb - 'temp'; -- => {"name": "Ann"} -- remove an array element by index (zero-based) SELECT '["a", "b", "c"]'::jsonb - 1; -- => ["a", "c"] -- negative index counts from the end SELECT '["a", "b", "c"]'::jsonb - -1; -- => ["a", "b"]Supongamos que
userstiene una columnaprofile jsonbcon ajustes. Quitemos una clave temporal en todas las filas:UPDATE users SET profile = profile - 'onboarding_temp' WHERE profile ? 'onboarding_temp';Borrar varias claves a la vez
Cuando el operando derecho es un array
text[],-elimina todas las claves de nivel superior listadas en una sola pasada. Resulta comodo para sanear un documento antes de serializarlo en una respuesta de API.-- strip several keys at once SELECT '{"a": 1, "b": 2, "c": 3}'::jsonb - '{a,c}'::text[]; -- => {"b": 2} -- hide sensitive fields before returning a user row SELECT id, email, profile - '{password_hash,internal_notes,ssn}'::text[] AS public_profile FROM users WHERE id = 42;Atencion: la forma con array solo afecta al nivel superior. No alcanza los campos anidados — para eso necesitas
#-.El operador
#-: un valor en una ruta anidada#-recibe a la derecha una ruta comotext[]y elimina lo que haya al final de esa ruta. Los elementos de la ruta son nombres de clave e indices de array, en orden de anidamiento.-- remove a nested key SELECT '{"user": {"name": "Ann", "secret": "x"}}'::jsonb #- '{user,secret}'; -- => {"user": {"name": "Ann"}} -- remove a deep array element: orders[0].card SELECT '{"orders": [{"id": 1, "card": "4111"}]}'::jsonb #- '{orders,0,card}'; -- => {"orders": [{"id": 1}]}En la practica
#-suele limpiar datos de registro enorders.meta jsonbantes de una exportacion:UPDATE orders SET meta = meta #- '{payment,raw_card_number}' WHERE status = 'paid' AND meta #> '{payment,raw_card_number}' IS NOT NULL;Trampas y combinacion con
jsonb_set?o#>para no lanzarUPDATEs vacios.-y#-siempre devuelven un nuevojsonb. Para persistir el cambio necesitasUPDATE ... SET col = col - ....UPDATEmasivo lo reconstruye — en tablas grandes eso no es nada barato.#-un numero se interpreta como indice de array; asegurate de que el nodo sea realmente un array, o el elemento no se encontrara.El borrado suele ir junto a
jsonb_set: una expresion descarta la parte no deseada, la otra escribe un valor nuevo.-- drop a secret and set a redaction flag in one statement UPDATE orders SET meta = jsonb_set(meta #- '{payment,cvv}', '{redacted}', 'true'::jsonb) WHERE id = 100;En MySQL el equivalente es
JSON_REMOVE(doc, '$.path'), que acepta una o varias rutas. En ClickHouse las columnas JSON suelen ser inmutables, asi que un borrado se expresa con unINSERTdel valor reconstruido en vez de un operador puntual.