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}"]

Demo

所以 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());