PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Jak zmienić strukturę danych jsonb przechowywanych w Postgresie?

Możesz to osiągnąć za pomocą niektórych funkcji json dostępne w postgresql.

W poniższym przykładzie z działającym db-fiddle zawarłem dodatkowe dane testowe.

Schemat (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]}]}');

Zapytanie nr 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;
new_dest
{"DestinationLists":[{"name":"Test trzeci","destinations":[{"Id":3},{"Id":5}]},{"name ":"SecondTest","destinations":[{"Id":103},{"Id":105}]}]}
{"DestinationLists":[{"name":"TV3/TVNZ/CHOICE","destinations":[{"Id":183},{"Id":165}]}]}

Zobacz na DB Fiddle

Edytuj 1

W odpowiedzi na Twoją zmianę poniższy kod może zostać użyty jako aktualizacja wyciągu. NB. CTE można również przepisać jako podzapytanie. Zobacz poniższy przykład:

Schemat (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;

Po

SELECT * FROM my_table;
id cel
1 {"DestinationLists":[{"name":"TV3/TVNZ/CHOICE","destinations":[{"Id":183},{"Id":163}]}]}
2 {"DestinationLists":[{"name":"SecondTest","destinations":[{"Id":103},{"Id":105}]},{"name":"Test trzeci", "destinations":[{"Id":3},{"Id":5}]}]}

Zobacz na DB Fiddle

Edytuj 2

Ta zmiana odpowiada na skrajne przypadki, w których niektóre miejsca docelowe mogą nie mieć miejsc docelowych i jako takie mogą nie być aktualizowane.

Aby przetestować, dodano dwa dodatkowe rekordy, przykładowy rekord, w którym istnieją dwie nazwane listy miejsc docelowych, ale tylko jedna zawiera miejsca docelowe, a druga, gdy istnieje nazwana lista miejsc docelowych bez miejsc docelowych.

Aktualizacja zapewnia, że ​​jeśli nie ma zmian, to znaczy, że istnieją nazwane listy miejsc docelowych bez miejsc docelowych, które pozostaną takie same. Zapewnia to poprzez sprawdzenie, czy liczba pozycji na liście nazwanych miejsc docelowych jest taka sama, jak liczba pustych elementów listy miejsc docelowych. Ponieważ wszystkie są puste, odfiltrowuje ten rekord z aktualizacji i zmniejsza liczbę wymaganych aktualizacji bazy danych. Przykładem tego jest numer rekordu 4

Początkowe zapytanie zostało zmodyfikowane, aby pomieścić te puste listy, ponieważ były one filtrowane przy użyciu poprzedniego podejścia.

Schemat (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": []}]}');

Zapytanie nr 1

SELECT '------ BEFORE -----';
?kolumna?
------ PRZED -----

Zapytanie nr 2

SELECT * FROM my_table;
id cel
1 {"DestinationLists":[{"name":"TV3/TVNZ/CHOICE","destinations":[183,165]}]}
2 {"DestinationLists":[{"name":"Drugi test","destinations":[103,105]},{"name":"Trzeci test","destinations":[3,5]}]}
3 {"DestinationLists":[{"name":"TVNZ, Media, wybór","destinations":[]},{"name":"TVNZ, odkrycia","destinations":[165,183,4155] }]}
4 {"DestinationLists":[{"name":"Czwarty test","destinations":[]}]}

Zapytanie nr 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 nowe_docelowe
1 1 0 {"DestinationLists":[{"name":"TV3/TVNZ/CHOICE","destinations":[{"Id":183},{"Id":163}]}]}
2 2 0 {"DestinationLists":[{"name":"SecondTest","destinations":[{"Id":103},{"Id":105}]},{"name":"Test trzeci", "destinations":[{"Id":3},{"Id":5}]}]}
3 2 1 {"DestinationLists":[{"name":"TVNZ, Odkrycie","destinations":[{"Id":165},{"Id":183},{"Id":4155}]} ,{"name":"TVNZ, Media, wybór","destinations":[]}]}

Zapytanie nr 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;

Brak wyników do wyświetlenia.

Zapytanie nr 5

SELECT '------ AFTER -----';
?kolumna?
------ PO -----

Zapytanie #6

SELECT * FROM my_table;
id cel
4 {"DestinationLists":[{"name":"Czwarty test","destinations":[]}]}
1 {"DestinationLists":[{"name":"TV3/TVNZ/CHOICE","destinations":[{"Id":183},{"Id":163}]}]}
2 {"DestinationLists":[{"name":"SecondTest","destinations":[{"Id":103},{"Id":105}]},{"name":"Test trzeci", "destinations":[{"Id":3},{"Id":5}]}]}
3 {"DestinationLists":[{"name":"TVNZ, Odkrycie","destinations":[{"Id":165},{"Id":183},{"Id":4155}]} ,{"name":"TVNZ, Media, wybór","destinations":[]}]}

Zobacz na DB Fiddle

Daj mi znać, czy to działa dla Ciebie.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Obliczanie odległości między lokalizacją GPS a wartością geograficzną Postgis za pomocą funkcji?

  2. Kompilacja PL/Proxy z PostgresPlus Advance Server 9.1

  3. SSL dla połączenia PostgreSQL nodejs

  4. Uruchamianie hurtowni danych w PostgreSQL

  5. Nie można połączyć się z PostgreSQL lokalnie przez bazę nodejs, nagle przestał działać na Macu