sql >> Base de Datos >  >> RDS >> PostgreSQL

Cómo cambiar la estructura de los datos jsonb almacenados en Postgres

Puede lograr esto con algunas de las funciones json disponible en postgresql.

En el ejemplo con db-fiddle en funcionamiento a continuación, incluí algunos datos de prueba adicionales.

Esquema (PostgreSQL v13)

CREATE TABLE my_table (
  "dest" json
);

INSERT INTO my_table
  ("dest")
VALUES
  ('{"DestinationLists": [{"name": "TV3/TVNZ/CHOICE", "destinations": [183, 165]}]}'),
  ('{"DestinationLists": [{"name": "SecondTest", "destinations": [103, 105]},{"name": "ThirdTest", "destinations": [3, 5]}]}');

Consulta #1

WITH expanded_data AS (
    SELECT
        dest::text, 
        json_build_object(
            'name',
             dl->>'name',
             'destinations',
             json_agg(
               json_build_object('Id',dld::text::int)
             )
        ) as dest_list_item
    FROM
        my_table, 
        json_array_elements(dest->'DestinationLists') dl,
        json_array_elements(dl->'destinations') dld
    GROUP BY
        dest::text,dl->>'name'
)
SELECT
    json_build_object(
        'DestinationLists',
        json_agg(dest_list_item)
    ) as new_dest
FROM
    expanded_data
GROUP BY
    dest::text;
nuevo_destino
{"DestinationLists":[{"name":"ThirdTest","destinations":[{"Id":3},{"Id":5}]},{"name ":"Segunda Prueba","destinos":[{"Id":103},{"Id":105}]}]}
{"DestinationLists":[{"name":"TV3/TVNZ/CHOICE","destinations":[{"Id":183},{"Id":165}]}]}

Ver en DB Fiddle

Editar 1

En respuesta a su edición, el código a continuación se puede usar como una actualización de la declaración. NÓTESE BIEN. El CTE también se puede reescribir como una subconsulta. Consulte el siguiente ejemplo:

Esquema (PostgreSQL v13)

CREATE TABLE my_table (
  id bigserial,
  "dest" jsonb
);

INSERT INTO my_table
  ("dest")
VALUES
  ('{"DestinationLists": [{"name": "TV3/TVNZ/CHOICE", "destinations": [183, 165]}]}'),
  ('{"DestinationLists": [{"name": "SecondTest", "destinations": [103, 105]},{"name": "ThirdTest", "destinations": [3, 5]}]}');
WITH expanded_data AS (
    SELECT
        id, 
        json_build_object(
            'name',
             dl->>'name',
             'destinations',
             json_agg(
               json_build_object('Id',dld::text::int)
             )
        ) as dest_list_item
    FROM
        my_table, 
        jsonb_array_elements(dest->'DestinationLists') dl,
        jsonb_array_elements(dl->'destinations') dld
    GROUP BY
        id,dl->>'name'
), 
new_json AS (
    SELECT
        id,
        json_build_object(
            'DestinationLists',
            json_agg(dest_list_item)
        ) as new_dest
    FROM
        expanded_data
    GROUP BY
        id
)
UPDATE my_table
SET dest = new_json.new_dest
FROM new_json
WHERE my_table.id = new_json.id;

Después

SELECT * FROM my_table;
id destino
1 {"DestinationLists":[{"name":"TV3/TVNZ/CHOICE","destinations":[{"Id":183},{"Id":165}]}]}
2 {"DestinationLists":[{"name":"SecondTest","destinations":[{"Id":103},{"Id":105}]},{"name":"ThirdTest", "destinos":[{"Id":3},{"Id":5}]}]}

Ver en DB Fiddle

Editar 2

Esta edición responde a los casos extremos en los que algunos destinos pueden no tener destinos y, por lo tanto, es posible que no se actualicen.

Para probar, se agregaron dos registros adicionales, el registro de muestra provisto donde hay dos listas de destinos con nombre pero solo uno tiene destinos y otro donde hay una lista de destinos con nombre sin destinos.

La actualización garantiza que, si no hay cambios, es decir, hay listas de destinos con nombre sin destinos que siguen siendo los mismos. Lo asegura comprobando si el número de elementos de la lista de destinos con nombre es el mismo que el número de elementos de la lista de destinos vacíos. Dado que todos están vacíos, filtra este registro de la actualización y reduce la cantidad de actualizaciones requeridas en la base de datos. Un ejemplo de esto es el número de registro 4

La consulta inicial se modificó para acomodar estas listas vacías a medida que se filtraban con el enfoque anterior.

Esquema (PostgreSQL v13)

CREATE TABLE my_table (
  id bigserial,
  "dest" jsonb
);

INSERT INTO my_table
  ("dest")
VALUES
  ('{"DestinationLists": [{"name": "TV3/TVNZ/CHOICE", "destinations": [183, 165]}]}'),
  ('{"DestinationLists": [{"name": "SecondTest", "destinations": [103, 105]},{"name": "ThirdTest", "destinations": [3, 5]}]}'),
  ('{"DestinationLists": [{"name": "TVNZ, Mediaworks, Choice", "destinations": []}, {"name": "TVNZ, Discovery", "destinations": [165, 183, 4155]}]}'),
  ('{"DestinationLists": [{"name": "Fourth Test", "destinations": []}]}');

Consulta #1

SELECT '------ BEFORE -----';
?columna?
------ ANTES -----

Consulta #2

SELECT * FROM my_table;
id destino
1 {"DestinationLists":[{"name":"TV3/TVNZ/CHOICE","destinations":[183,165]}]}
2 {"DestinationLists":[{"name":"SecondTest","destinations":[103,105]},{"name":"ThirdTest","destinations":[3,5]}]}
3 {"DestinationLists":[{"name":"TVNZ, Mediaworks, Choice","destinations":[]},{"name":"TVNZ, Discovery","destinations":[165,183,4155] }]}
4 {"DestinationLists":[{"name":"Cuarta prueba","destinos":[]}]}

Consulta #3

WITH expanded_data AS (
    SELECT
        id, 
        CASE
            WHEN COUNT(dld)=0 THEN 1
            ELSE 0
        END as name_has_empty_list_item,
        json_build_object(
            'name',
             dl->>'name',
             'destinations',
              CASE 
                  WHEN 
                       COUNT(dld)=0 
                  THEN 
                       to_json(array[]::json[])
                  ELSE
                       json_agg(
                           json_build_object('Id',dld::text::int )
                       )
              END
        ) as dest_list_item
    FROM
        my_table, 
        jsonb_array_elements(dest->'DestinationLists') dl
    LEFT JOIN
        jsonb_array_elements(dl->'destinations') dld ON 1=1
    GROUP BY
        id,dl->>'name'
), 
new_json AS (
    SELECT
        id,
        COUNT(dest_list_item) as no_list_item,
        SUM(name_has_empty_list_item) as no_empty_list_item,
        json_build_object(
            'DestinationLists',
            json_agg(dest_list_item)
        ) as new_dest
    FROM
        expanded_data
    GROUP BY
        id
    HAVING
        SUM(name_has_empty_list_item) <> COUNT(dest_list_item)
    
)
SELECT * FROM new_json;
id no_list_item no_empty_list_item nuevo_destino
1 1 0 {"DestinationLists":[{"name":"TV3/TVNZ/CHOICE","destinations":[{"Id":183},{"Id":165}]}]}
2 2 0 {"DestinationLists":[{"name":"SecondTest","destinations":[{"Id":103},{"Id":105}]},{"name":"ThirdTest", "destinos":[{"Id":3},{"Id":5}]}]}
3 2 1 {"DestinationLists":[{"name":"TVNZ, Discovery","destinations":[{"Id":165},{"Id":183},{"Id":4155}]} ,{"nombre":"TVNZ, Mediaworks, Choice","destinos":[]}]}

Consulta #4

WITH expanded_data AS (
    SELECT
        id, 
        CASE
            WHEN COUNT(dld)=0 THEN 1
            ELSE 0
        END as name_has_empty_list_item,
        json_build_object(
            'name',
             dl->>'name',
             'destinations',
              CASE 
                  WHEN 
                       COUNT(dld)=0 
                  THEN 
                       to_json(array[]::json[])
                  ELSE
                       json_agg(
                           json_build_object('Id',dld::text::int )
                       )
              END
        ) as dest_list_item
    FROM
        my_table, 
        jsonb_array_elements(dest->'DestinationLists') dl
    LEFT JOIN
        jsonb_array_elements(dl->'destinations') dld ON 1=1
    GROUP BY
        id,dl->>'name'
), 
new_json AS (
    SELECT
        id,
        json_build_object(
            'DestinationLists',
            json_agg(dest_list_item)
        ) as new_dest
    FROM
        expanded_data
    GROUP BY
        id
    HAVING
        SUM(name_has_empty_list_item) <> COUNT(dest_list_item)
)
UPDATE my_table
SET dest = new_json.new_dest
FROM new_json
WHERE my_table.id = new_json.id;

No hay resultados para mostrar.

Consulta #5

SELECT '------ AFTER -----';
?columna?
------ DESPUÉS -----

Consulta #6

SELECT * FROM my_table;
id destino
4 {"DestinationLists":[{"name":"Cuarta prueba","destinos":[]}]}
1 {"DestinationLists":[{"name":"TV3/TVNZ/CHOICE","destinations":[{"Id":183},{"Id":165}]}]}
2 {"DestinationLists":[{"name":"SecondTest","destinations":[{"Id":103},{"Id":105}]},{"name":"ThirdTest", "destinos":[{"Id":3},{"Id":5}]}]}
3 {"DestinationLists":[{"name":"TVNZ, Discovery","destinations":[{"Id":165},{"Id":183},{"Id":4155}]} ,{"nombre":"TVNZ, Mediaworks, Choice","destinos":[]}]}

Ver en DB Fiddle

Avíseme si esto funciona para usted.