MySQL JSON_SET() 用于处理空数组
MySQL JSON_SET() to work with empty arrays
我有一个 table 这样的:
CREATE TABLE `test` (
`id` int(11) NOT NULL,
`settings` json NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `test` (`id`, `settings`) VALUES
('1', '{\"foo\": {\"bar\": 1}}'),
('2', '{\"foobar\": 2}'),
('3', '[]');
我想为一行添加新设置,所以我尝试了 JSON_SET:
SELECT *, JSON_SET(settings, '$.newFoo', 10) FROM test;
如你所见,它对第3项不起作用。预期结果当然与第4行相同; []
是 json 在 php 中编码空数组的结果,如果该项目没有任何设置。
可以进行适用于所有情况的单个查询吗?
P.S.: 我需要做的是更新,即 UPDATE test SET settings=JSON_SET(...) WHERE id=?;
您可以使用 CASE .. WHEN
条件运算符在单个查询中处理此问题。此外,由于 settings
的数据类型是 Json
,因此您需要先将其 Cast()
转换为字符串 (Char) 以检查 []
:
SELECT *,
CASE WHEN CAST(settings AS CHAR) = '[]'
THEN JSON_SET('{}', '$.newFoo', 10)
ELSE JSON_SET(settings, '$.newFoo', 10)
END AS modified_settings
FROM test;
Update
相同的查询如下:
UPDATE test
SET settings = CASE WHEN CAST(settings AS CHAR) = '[]'
THEN JSON_SET('{}', '$.newFoo', 10)
ELSE JSON_SET(settings, '$.newFoo', 10)
END
您对 JSON_SET
的调用未按预期运行的原因是您需要使用 JSON 数组的语法不同于您需要使用单个 JSON 的语法.考虑以下查询,它将 JSON 添加到一个空数组:
SELECT JSON_SET('[]', '$[0]', '{"newFoo": 10}') AS output;
这会打印:
["{\"newFoo\": 10}"]
所以 JSON_SET
函数似乎有两种行为。当在 JSON 上正确操作时,它可以插入新密钥或更新已存在的密钥。在对数组进行操作时,它可以 insert/update 个数组元素,这些元素可能是整个 JSON 个对象。
我通过始终将值存储为对象而不是数组来解决,在 PHP 中,您需要向 json_encode
函数添加一个标志:
json_encode($value, JSON_FORCE_OBJECT)
或者,作为替代方案,使字段可为空并使用 COALESCE:
SELECT *, JSON_SET(COALESCE(settings, '{}'), '$.newFoo', 10) FROM test;
这也让我很困惑,但请尝试使用 JSON_ARRAY() 而不是双括号符号“[]”。例如:
INSERT INTO `test` (`id`, `settings`) VALUES
('1', '{\"foo\": {\"bar\": 1}}'),
('2', '{\"foobar\": 2}'),
('3', JSON_ARRAY());
我有一个 table 这样的:
CREATE TABLE `test` (
`id` int(11) NOT NULL,
`settings` json NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `test` (`id`, `settings`) VALUES
('1', '{\"foo\": {\"bar\": 1}}'),
('2', '{\"foobar\": 2}'),
('3', '[]');
我想为一行添加新设置,所以我尝试了 JSON_SET:
SELECT *, JSON_SET(settings, '$.newFoo', 10) FROM test;
如你所见,它对第3项不起作用。预期结果当然与第4行相同; []
是 json 在 php 中编码空数组的结果,如果该项目没有任何设置。
可以进行适用于所有情况的单个查询吗?
P.S.: 我需要做的是更新,即 UPDATE test SET settings=JSON_SET(...) WHERE id=?;
您可以使用 CASE .. WHEN
条件运算符在单个查询中处理此问题。此外,由于 settings
的数据类型是 Json
,因此您需要先将其 Cast()
转换为字符串 (Char) 以检查 []
:
SELECT *,
CASE WHEN CAST(settings AS CHAR) = '[]'
THEN JSON_SET('{}', '$.newFoo', 10)
ELSE JSON_SET(settings, '$.newFoo', 10)
END AS modified_settings
FROM test;
Update
相同的查询如下:
UPDATE test
SET settings = CASE WHEN CAST(settings AS CHAR) = '[]'
THEN JSON_SET('{}', '$.newFoo', 10)
ELSE JSON_SET(settings, '$.newFoo', 10)
END
您对 JSON_SET
的调用未按预期运行的原因是您需要使用 JSON 数组的语法不同于您需要使用单个 JSON 的语法.考虑以下查询,它将 JSON 添加到一个空数组:
SELECT JSON_SET('[]', '$[0]', '{"newFoo": 10}') AS output;
这会打印:
["{\"newFoo\": 10}"]
所以 JSON_SET
函数似乎有两种行为。当在 JSON 上正确操作时,它可以插入新密钥或更新已存在的密钥。在对数组进行操作时,它可以 insert/update 个数组元素,这些元素可能是整个 JSON 个对象。
我通过始终将值存储为对象而不是数组来解决,在 PHP 中,您需要向 json_encode
函数添加一个标志:
json_encode($value, JSON_FORCE_OBJECT)
或者,作为替代方案,使字段可为空并使用 COALESCE:
SELECT *, JSON_SET(COALESCE(settings, '{}'), '$.newFoo', 10) FROM test;
这也让我很困惑,但请尝试使用 JSON_ARRAY() 而不是双括号符号“[]”。例如:
INSERT INTO `test` (`id`, `settings`) VALUES
('1', '{\"foo\": {\"bar\": 1}}'),
('2', '{\"foobar\": 2}'),
('3', JSON_ARRAY());