问题
I've got JSON column storing a document. I want to perform atomic UPDATEs on this column.
Given e.g. value:
[{"substanceId": 182, "text": "substance_name_182"}, {"substanceId": 183, "text": "substance_name_183"}]
and the update
[{"substanceId": 182, "text": "substance_name_182_new"}, {"substanceId": 184, "text": "substance_name_184"}]
I expect to get this:
[{"substanceId": 182, "text": "substance_name_182_new"}, {"substanceId": 183, "text": "substance_name_183"} {"substanceId": 184, "text": "substance_name_184"}}]
None of the JSON_MERGE_PATCH
or JSON_MERGE_PRESERVE
directly allow me to achieve it. The JSON_MERGE_PATCH
is not aware substanceId
being an ID of the document.
Is there any way to achieve it on the MySQL side?
I could do it client-side (fetch value first, modify and update it back) but that's last resort, firstly I have lots of rows to update, where all of them would be covered by UPDATE
with WHERE
clause, so the MySQL way would be more convenient. When doing it client-side and do it safely I would have to LOCK many rows FOR UPDATE.
E.g. query:
SELECT JSON_MERGE_PATCH(
'[{"substanceId": 182, "text": "substance_name_182"}, {"substanceId": 183, "text": "substance_name_183"}]',
'[{"substanceId": 182, "text": "substance_name_182_new"}, {"substanceId": 184, "text": "substance_name_184"}]'
) v;
results
[{"text": "substance_name_182_new", "substanceId": 182}, {"text": "substance_name_184", "substanceId": 184}]
回答1:
Here is an insanely convoluted way of doing this in MySQL, using JSON_TABLE to convert the update and original JSON values into columns, merging the columns using a (simulated) FULL JOIN
and then re-creating the output JSON value using JSON_OBJECT and JSON_ARRAYAGG; finally using that to update the original table:
WITH upd AS (
SELECT *
FROM JSON_TABLE('[{"substanceId": 182, "text": "substance_name_182_new"}, {"substanceId": 184, "text": "substance_name_184"}]',
'$[*]' COLUMNS (
substanceId INT PATH '$.substanceId',
txt VARCHAR (100) PATH '$.text')
) jt
CROSS JOIN (SELECT DISTINCT id
FROM test) t
),
cur AS (
SELECT id, substanceId, txt
FROM test
JOIN JSON_TABLE(test.j,
'$[*]' COLUMNS (
substanceId INT PATH '$.substanceId',
txt VARCHAR (100) PATH '$.text')
) jt
),
allv AS (
SELECT COALESCE(upd.id, cur.id) AS id,
COALESCE(upd.substanceId, cur.substanceId) AS substanceId,
COALESCE(upd.txt, cur.txt) AS txt
FROM upd
LEFT JOIN cur ON cur.substanceId = upd.substanceId
UNION ALL
SELECT COALESCE(upd.id, cur.id) AS id,
COALESCE(upd.substanceId, cur.substanceId) AS substanceId,
COALESCE(upd.txt, cur.txt) AS txt
FROM upd
RIGHT JOIN cur ON cur.substanceId = upd.substanceId
),
obj AS (
SELECT DISTINCT id, JSON_OBJECT('substanceId', substanceId, 'text', txt) AS o
FROM allv
),
arr AS (
SELECT id, JSON_ARRAYAGG(o) AS a
FROM obj
GROUP BY id
)
UPDATE test
JOIN arr ON test.id = arr.id
SET test.j = arr.a
;
SELECT JSON_PRETTY(j)
FROM test
Output:
[
{
"text": "substance_name_183",
"substanceId": 183
},
{
"text": "substance_name_184",
"substanceId": 184
},
{
"text": "substance_name_182_new",
"substanceId": 182
}
]
Demo on dbfiddle
Note this assumes you use a unique id
value to distinguish the rows in your table. If you use something else, you would need to swap that in where id
is used in the above query.
来源:https://stackoverflow.com/questions/60601014/mysql-json-merge-two-json-objects-primary-key-in-json-document