postgresql Postgres 13.9 -如何通过追加另外两个json元素值来更新嵌套的json元素值

watbbzwu  于 2023-10-18  发布在  PostgreSQL
关注(0)|答案(1)|浏览(127)

我们正在使用Postgres 13.9和下面的表和JSON结构。

CREATE TABLE eco_val (
        Client varchar(50) NOT NULL,
        id varchar(50) NOT NULL,
        eco_js json NULL
        CONSTRAINT pk_eco_val PRIMARY KEY (Client, ID)
    );
    
    insert into eco_val (Client,ID,eco_js) values ('testclient','7193497_1',
      '{"ecoOptions": [
        {
          "ecoValue": [
            {
              "name": "A",
              "locale": "en_US"
            }
          ],
          "ecoOptionID": "7193497_1_1",
          "seq": 1,
          "defaultIndicator": false,
          "correctecoIndicator": false
        },
        {
          "ecoValue": [
            {
              "name": "1",
              "locale": "en_US"
            }
          ],
          "ecoOptionID": "7193497_1_2",
          "seq": 2,
          "defaultIndicator": false,
          "correctecoIndicator": true
        },
        {
          "ecoValue": [
            {
              "name": "2",
              "locale": "en_US"
            }
          ],
          "ecoOptionID": "7193497_1_1",
          "seq": 3,
          "defaultIndicator": false,
          "correctecoIndicator": true
        },
        {
          "ecoValue": [
            {
              "name": "5",
              "locale": "en_US"
            }
          ],
          "ecoOptionID": "7193497_1_7",
          "seq": 4,
          "defaultIndicator": false,
          "correctecoIndicator": true
        },
        {
          "ecoValue": [
            {
              "name": "ab",
              "locale": "en_US"
            }
          ],
          "ecoOptionID": "7193497_1_1",
          "seq": 5,
          "defaultIndicator": false,
          "correctecoIndicator": false
        },
        {
          "ecoValue": [
            {
              "name": "ad",
              "locale": "en_US"
            }
          ],
          "ecoOptionID": "7193497_1_2",
          "seq": 6,
          "defaultIndicator": false,
          "correctecoIndicator": false
        }
      ]
    }');

理想情况下,ecoOptionID应存储ID的结果(即7193497_1),并在其正下方添加下划线和seq。由于应用程序中的一个错误,json在创建时使用了错误的ecoOptionID值,如粗体所示(例如,下表中的7193497_1_2应该是7193497_1_6,7193497_1_7应该是7193497_1_4):
| ID|拟生态群落|seq| CNT|最后数字标识|
| --|--|--|--|--|
| 邮编:7193497_1| 7193497_1_1| 1 | 3 | 1 |
| 邮编:7193497_1|7193497_1| 3 | 3 | 1 |
| 邮编:7193497_1|7193497_1| 5 | 3 | 1 |
| 邮编:7193497_1| 7193497_1_2| 2 | 2 | 2 |
| 邮编:7193497_1|7193497_1| 6 | 2 | 2 |
| 邮编:7193497_1|7193497_1_7| 4 | 1 | 7 |

select client,
    id, 
    ab ->>'ecoOptionID' ecoOptionID,
    ab ->>'seq' seq,
    count(*) over (partition by client,id,ab ->>'ecoOptionID' order by ab ->>'ecoOptionID') cnt,
 reverse(SUBSTRING(REVERSE(ab ->>'ecoOptionID') FROM 1 for POSITION('_' IN REVERSE(ab ->>'ecoOptionID')) -1)) last_digit_id
    from
    eco_val a,
    jsonb_array_elements(eco_js::jsonb->'ecoOptions') ab
where
 id = '7193497_1';

我们有一些数据需要更正。
我已经看到jsonb_set可以用于指定索引或路径的地方。不幸的是,我们的JSON中的ecoOptionID的数量没有预定义。
是否有办法更新所有要设置为字符串的ecoOptionID,该字符串是该元素的ID、下划线和seq的串联?
生成的JSON应该看起来像这样:

'{"ecoOptions": [
    {
      "ecoValue": [
        {
          "name": "A",
          "locale": "en_US"
        }
      ],
      "ecoOptionID": "7193497_1_1",
      "seq": 1,
      "defaultIndicator": false,
      "correctecoIndicator": false
    },
    {
      "ecoValue": [
        {
          "name": "1",
          "locale": "en_US"
        }
      ],
      "ecoOptionID": "7193497_1_2",
      "seq": 2,
      "defaultIndicator": false,
      "correctecoIndicator": true
    },
    {
      "ecoValue": [
        {
          "name": "2",
          "locale": "en_US"
        }
      ],
      "ecoOptionID": "7193497_1_3",
      "seq": 3,
      "defaultIndicator": false,
      "correctecoIndicator": true
    },
    {
      "ecoValue": [
        {
          "name": "5",
          "locale": "en_US"
        }
      ],
      "ecoOptionID": "7193497_1_4",
      "seq": 4,
      "defaultIndicator": false,
      "correctecoIndicator": true
    },
    {
      "ecoValue": [
        {
          "name": "ab",
          "locale": "en_US"
        }
      ],
      "ecoOptionID": "7193497_1_5",
      "seq": 5,
      "defaultIndicator": false,
      "correctecoIndicator": false
    },
    {
      "ecoValue": [
        {
          "name": "ad",
          "locale": "en_US"
        }
      ],
      "ecoOptionID": "7193497_1_6",
      "seq": 6,
      "defaultIndicator": false,
      "correctecoIndicator": false
    }
  ]
}');
qco9c6ql

qco9c6ql1#

我想到了一个解决方案:

do $$
declare 
rec RECORD;
v_sql        varchar(3200);
begin for rec in
(select b.* from (select Client, t.rn-1 as ind,
    id, 
    t.account->>'ecoOptionID' ecoOptionID,
    t.account->>'seq' seq,
    count(*) over (partition by Client,id,t.account->>'ecoOptionID' ) cnt
,case when  reverse(SUBSTRING(REVERSE(t.account->>'ecoOptionID') FROM 1 for POSITION('_' IN REVERSE(t.account->>'ecoOptionID')) -1)) != t.account->>'seq' 
then 'mismatch'
when  reverse(SUBSTRING(REVERSE(t.account->>'ecoOptionID') FROM 1 for POSITION('_' IN REVERSE(t.account->>'ecoOptionID')) -1)) = t.account->>'seq' 
then 'match'
else 'N/A'
end seq_id_match 
    from
    eco_val  a,
    jsonb_array_elements(eco_js ::jsonb->'ecoOptions') WITH ORDINALITY AS t (account, rn)
where id = '7193497_1')b where seq_id_match='mismatch')
loop

v_sql :='
update eco_val  set eco_js =
jsonb_set(eco_js ::jsonb,''{ecoOptions,'||rec.ind||',ecoOptionID}'',
''"'||rec.id||'_'||rec.seq||'"'')
where Client='''||rec.Client||''' and id='''||rec.id||'''';
raise info using message := concat('Final query ',v_sql);
execute  v_sql; 
end loop;
end;
$$;

FOR循环将遍历每个在ecoOptionID中不匹配的索引,并将该值替换为ID和SEQ的串联。

相关问题