patternsqlMinor
Is it possible to remove a JSON array element by value in MySQL?
Viewed 0 times
arrayvalueremovepossiblemysqlelementjson
Problem
In MySQL, it's possible to remove an array element by index:
Is it possible to remove by value instead - in this case the element(s) matching the string
Based on the documentation, it's possible to find the index via
SELECT JSON_REMOVE('["a", "b", "c"]', '$[1]') result;
+------------+
| result |
+------------+
| ["a", "c"] |
+------------+
Is it possible to remove by value instead - in this case the element(s) matching the string
"b"?Based on the documentation, it's possible to find the index via
JSON_SEARCH and then passing it to JSON_REMOVE, however, I was wondering if there's an expression/function to do it in a single function.Solution
As of writing this, MySQL has no dedicated JSON function to allow you to remove a JSON array element by its value. You have to use whatever functions are available to come up with a custom expression to achieve your goal. Depending on the requirements, you might even want to create a custom function.
More specifically, if the requirement is to remove the first occurrence of a value, then one way to do it is like this:
Returns:
before_remove
after_remove
["a", "b", "c", "b"]
["a", "c", "b"]
If you want to remove an arbitrary occurrence of a value, the expression becomes somewhat more complex:
Output:
before_remove
after_remove
["a", "b", "c", "b"]
["a", "b", "c"]
In this case, depending on the number of entries found,
Finally, in order to be able to remove all occurrences, you need to loop over the results of the path array returned by
Alternatively you could try solving this in a set-based manner, for instance using the
The options are there and I am leaving the solution as an exercise for the reader. I will only add that both queries above are available online at db<>fiddle.
More specifically, if the requirement is to remove the first occurrence of a value, then one way to do it is like this:
SET @value := 'b';
SELECT
j.j AS before_remove
, JSON_REMOVE(
j.j
, JSON_UNQUOTE(
JSON_SEARCH(
j.j
, 'one'
, @value
)
)
) AS after_remove
FROM
(SELECT '["a", "b", "c", "b"]') AS j (j)
;Returns:
before_remove
after_remove
["a", "b", "c", "b"]
["a", "c", "b"]
JSON_SEARCH returns a quoted path to the value found ("$[1]" in this case), JSON_UNQUOTE removes the quotes (yielding $[1]), following which the path can be fed to JSON_REMOVE.If you want to remove an arbitrary occurrence of a value, the expression becomes somewhat more complex:
SET @value := 'b';
SET @occurrence := 2;
SELECT
j.j AS before_remove
, JSON_REMOVE(
j.j
, JSON_UNQUOTE(
JSON_EXTRACT(
JSON_SEARCH(
j.j
, 'all'
, @value
)
, CONCAT('$[', @occurrence - 1, ']')
)
)
) AS after_remove
FROM
(SELECT '["a", "b", "c", "b"]') AS j (j)
;Output:
before_remove
after_remove
["a", "b", "c", "b"]
["a", "b", "c"]
In this case, depending on the number of entries found,
JSON_SEARCH may return a JSON array of paths rather than a single path. Before applying JSON_UNQUOTE, therefore, we need to extract the specific path from the array matching the required occurrence, for which we can use JSON_EXTRACT. The CONCAT expression returns the corresponding path for JSON_EXTRACT (and then we get a path to use with JSON_REMOVE – yes, I know, it is a little confusing).Finally, in order to be able to remove all occurrences, you need to loop over the results of the path array returned by
JSON_SEARCH and either sequentially run JSON_REMOVE with each path or somehow build and evaluate a single dynamic JSON_REMOVE expression including all paths. Either way you need to do this in a custom-created function.Alternatively you could try solving this in a set-based manner, for instance using the
JSON_TABLE function to turn the path array – output of JSON_SEARCH – into a row set, one path per row, and then using a recursive CTE on the set, applying each path one by one and getting the last result as the final JSON.The options are there and I am leaving the solution as an exercise for the reader. I will only add that both queries above are available online at db<>fiddle.
Code Snippets
SET @value := 'b';
SELECT
j.j AS before_remove
, JSON_REMOVE(
j.j
, JSON_UNQUOTE(
JSON_SEARCH(
j.j
, 'one'
, @value
)
)
) AS after_remove
FROM
(SELECT '["a", "b", "c", "b"]') AS j (j)
;SET @value := 'b';
SET @occurrence := 2;
SELECT
j.j AS before_remove
, JSON_REMOVE(
j.j
, JSON_UNQUOTE(
JSON_EXTRACT(
JSON_SEARCH(
j.j
, 'all'
, @value
)
, CONCAT('$[', @occurrence - 1, ']')
)
)
) AS after_remove
FROM
(SELECT '["a", "b", "c", "b"]') AS j (j)
;Context
StackExchange Database Administrators Q#293864, answer score: 4
Revisions (0)
No revisions yet.