update postgres jsonb_set example column postgresql sql-update crud postgresql-9.4 jsonb

jsonb_set - postgresql jsonb extract



Cómo realizar operaciones de actualización en columnas de tipo JSONB en Postgres 9.4 (9)

Mirando a través de la documentación del tipo de datos Postgres 9.4 JSONB, no es inmediatamente obvio para mí cómo hacer actualizaciones en las columnas JSONB.

Documentación para tipos y funciones JSONB:

http://www.postgresql.org/docs/9.4/static/functions-json.html http://www.postgresql.org/docs/9.4/static/datatype-json.html

Como ejemplos, tengo esta estructura de tabla básica:

CREATE TABLE test(id serial, data jsonb);

Insertar es fácil, como en:

INSERT INTO test(data) values (''{"name": "my-name", "tags": ["tag1", "tag2"]}'');

Ahora, ¿cómo actualizaría la columna ''datos''? Esta es una sintaxis no válida:

UPDATE test SET data->''name'' = ''my-other-name'' WHERE id = 1;

¿Está esto documentado en algún lugar obvio que me perdí? Gracias.


Escribí una pequeña función para mí que funciona de forma recursiva en Postgres 9.4. Tuve el mismo problema (bueno, resolvieron parte de este dolor de cabeza en Postgres 9.5). De todos modos, aquí está la función (espero que funcione bien para usted):

CREATE OR REPLACE FUNCTION jsonb_update(val1 JSONB,val2 JSONB) RETURNS JSONB AS $$ DECLARE result JSONB; v RECORD; BEGIN IF jsonb_typeof(val2) = ''null'' THEN RETURN val1; END IF; result = val1; FOR v IN SELECT key, value FROM jsonb_each(val2) LOOP IF jsonb_typeof(val2->v.key) = ''object'' THEN result = result || jsonb_build_object(v.key, jsonb_update(val1->v.key, val2->v.key)); ELSE result = result || jsonb_build_object(v.key, v.value); END IF; END LOOP; RETURN result; END; $$ LANGUAGE plpgsql;

Aquí está el uso de muestra:

select jsonb_update(''{"a":{"b":{"c":{"d":5,"dd":6},"cc":1}},"aaa":5}''::jsonb, ''{"a":{"b":{"c":{"d":15}}},"aa":9}''::jsonb); jsonb_update --------------------------------------------------------------------- {"a": {"b": {"c": {"d": 15, "dd": 6}, "cc": 1}}, "aa": 9, "aaa": 5} (1 row)

Como puede ver, analice en profundidad y actualice / agregue valores donde sea necesario.


Esta pregunta se hizo en el contexto de postgres 9.4, sin embargo, los nuevos lectores que lleguen a esta pregunta deben tener en cuenta que en postgres 9.5, las operaciones de creación / actualización / eliminación de subdocumentos en campos JSONB son compatibles de forma nativa con la base de datos, sin necesidad de extensión funciones

Ver: JSONB modificando operadores y funciones



Idealmente, no utiliza documentos JSON para datos estructurados y regulares que desea manipular dentro de una base de datos relacional. Utilice un diseño relacional normalizado en su lugar.

JSON está destinado principalmente a almacenar documentos completos que no necesitan ser manipulados dentro del RDBMS. Relacionado:

Actualizar una fila en Postgres siempre escribe una nueva versión de toda la fila. Ese es el principio básico del modelo MVCC de Postgres . Desde una perspectiva de rendimiento, apenas importa si cambia una sola pieza de datos dentro de un objeto JSON o todo: debe escribirse una nueva versión de la fila.

Por lo tanto, el consejo en el manual :

Los datos JSON están sujetos a las mismas consideraciones de control de concurrencia que cualquier otro tipo de datos cuando se almacenan en una tabla. Aunque es factible almacenar documentos grandes, tenga en cuenta que cualquier actualización adquiere un bloqueo de nivel de fila en toda la fila. Considere limitar los documentos JSON a un tamaño manejable para disminuir la contención de bloqueo entre las transacciones de actualización. Idealmente, los documentos JSON deben representar cada uno un dato atómico que las reglas de negocio dictan, no se puede subdividir razonablemente en datums más pequeños que se puedan modificar de forma independiente.

La esencia de esto: para modificar cualquier cosa dentro de un objeto JSON, debe asignar un objeto modificado a la columna. Postgres proporciona medios limitados para construir y manipular datos json además de sus capacidades de almacenamiento. El arsenal de herramientas ha crecido sustancialmente con cada nueva versión desde la versión 9.2. Pero el principio permanece: siempre debe asignar un objeto modificado completo a la columna y Postgres siempre escribe una nueva versión de fila para cualquier actualización.

Algunas técnicas para trabajar con las herramientas de Postgres 9.3 o posterior:

  • ¿Cómo modifico los campos dentro del nuevo tipo de datos PostgreSQL JSON?

Esta respuesta ha atraído tantos votos negativos como todas mis otras respuestas en SO juntas . A la gente no parece gustarle la idea: un diseño normalizado es superior para los datos no dinámicos. Esta excelente publicación de blog de Craig Ringer explica con más detalle:



Para aquellos que se encuentran con este problema y quieren una solución muy rápida (y están atascados en 9.4.5 o anterior), esto es lo que hice:

Creación de tabla de prueba.

CREATE TABLE test(id serial, data jsonb); INSERT INTO test(data) values (''{"name": "my-name", "tags": ["tag1", "tag2"]}'');

Actualización de la declaración para cambiar el nombre de la propiedad jsonb

UPDATE test SET data = replace(data::TEXT,''"name":'',''"my-other-name":'')::jsonb WHERE id = 1;

En última instancia, la respuesta aceptada es correcta porque no puede modificar una pieza individual de un objeto jsonb (en 9.4.5 o anterior); sin embargo, puede convertir el objeto jsonb en una cadena (:: TEXT) y luego manipular la cadena y volver al objeto jsonb (:: jsonb).

Hay dos advertencias importantes.

  1. esto reemplazará todas las propiedades llamadas "nombre" en el json (en el caso de que tenga varias propiedades con el mismo nombre)
  2. esto no es tan eficiente como lo sería jsonb_set si usa 9.5

Dicho esto, me encontré con una situación en la que tenía que actualizar el esquema para el contenido de los objetos jsonb y esta era la forma más sencilla de lograr exactamente lo que pedía el póster original.


Quizás: ACTUALIZAR prueba SET data = ''"my-other-name"'' :: json WHERE id = 1;

Funcionó con mi caso, donde los datos son de tipo json


Si puede actualizar a Postgresql 9.5, el comando jsonb_set está disponible, como han mencionado otros.

En cada una de las siguientes declaraciones SQL, he omitido la cláusula where por brevedad; obviamente, querrás volver a agregar eso.

Nombre de actualización:

UPDATE test SET data = jsonb_set(data, ''{name}'', ''"my-other-name"'');

Reemplace las etiquetas (en lugar de agregar o quitar etiquetas):

UPDATE test SET data = jsonb_set(data, ''{tags}'', ''["tag3", "tag4"]'');

Sustitución de la segunda etiqueta (indexada a 0):

UPDATE test SET data = jsonb_set(data, ''{tags,1}'', ''"tag5"'');

Agregue una etiqueta ( esto funcionará siempre que haya menos de 999 etiquetas; cambiar el argumento 999 a 1000 o superior genera un error . Este ya no parece ser el caso en Postgres 9.5.3; se puede usar un índice mucho mayor) :

UPDATE test SET data = jsonb_set(data, ''{tags,999999999}'', ''"tag6"'', true);

Eliminar la última etiqueta:

UPDATE test SET data = data #- ''{tags,-1}''

Actualización compleja (elimine la última etiqueta, inserte una nueva etiqueta y cambie el nombre):

UPDATE test SET data = jsonb_set( jsonb_set(data #- ''{tags,-1}'', ''{tags,999999999}'', ''"tag3"'', true), ''{name}'', ''"my-other-name"'');

Es importante tener en cuenta que en cada uno de estos ejemplos, en realidad no está actualizando un solo campo de los datos JSON. En cambio, está creando una versión temporal y modificada de los datos, y asigna esa versión modificada de nuevo a la columna. En la práctica, el resultado debería ser el mismo, pero teniendo esto en cuenta debería hacer actualizaciones complejas, como el último ejemplo, más comprensibles.

En el ejemplo complejo, hay tres transformaciones y tres versiones temporales: Primero, se elimina la última etiqueta. Luego, esa versión se transforma al agregar una nueva etiqueta. A continuación, la segunda versión se transforma cambiando el campo de name . El valor en la columna de data se reemplaza con la versión final.


actualizar el atributo ''nombre'':

UPDATE test SET data=data||''{"name":"my-other-name"}'' WHERE id = 1;

y si desea eliminar, por ejemplo, los atributos ''nombre'' y ''etiquetas'':

UPDATE test SET data=data-''{"name","tags"}''::text[] WHERE id = 1;