PHP操作MySQL:如何正确替换JSON字段中的嵌套数组?
解决MySQL JSON字段嵌套数组的正确存储问题
你遇到的核心问题是:当前productDynamicFields里的price是字符串类型的JSON(带转义引号),而非MySQL原生支持的JSON对象。直接用JSON_REPLACE赋值json_encode后的字符串,MySQL会把它当成普通字符串处理,自然就带上了转义和外层引号。下面给你分步解决的方案:
1. 先修复现有数据(把字符串转成JSON对象)
如果数据库里已有大量price是带转义的字符串,先执行一次批量更新,把它们转换成MySQL能识别的JSON对象:
UPDATE products SET productDynamicFields = JSON_SET( productDynamicFields, '$.price', JSON_UNQUOTE(productDynamicFields->>'$.price') ) WHERE productDynamicFields->>'$.price' LIKE '%"%'; -- 仅处理仍为字符串格式的记录
这里JSON_UNQUOTE配合->>语法,会把转义后的字符串解析成真正的JSON对象,之后price就会变成原生JSON类型。
2. 正确更新price字段的PHP代码
接下来更新时要注意两点:避免SQL注入、让MySQL识别JSON结构而非字符串。推荐两种写法:
写法一:直接更新嵌套的JSON键(更高效)
如果只需要修改price里的特定键(比如44和_default),可以直接在SQL里操作,不用在PHP里处理整个JSON:
$newPrice44 = 13; $newDefaultPrice = 13; $targetSlug = "SLUG"; // 用预处理语句避免SQL注入 $sql = "UPDATE products SET productDynamicFields = JSON_SET( productDynamicFields, '$.price.44', ?, '$.price._default', ? ) WHERE productSlug = ?"; $stmt = mysqli_prepare($link, $sql); // 绑定参数:i=整数,s=字符串 mysqli_stmt_bind_param($stmt, "iis", $newPrice44, $newDefaultPrice, $targetSlug); mysqli_stmt_execute($stmt);
写法二:传递完整JSON对象
如果需要替换整个price的结构,要确保MySQL把传入的字符串解析为JSON对象,而非普通字符串:
$priceData = ['44' => 13, '_default' => 13]; $priceJson = json_encode($priceData); // 得到无多余转义的JSON字符串:{"44":13,"_default":13} $targetSlug = "SLUG"; $sql = "UPDATE products SET productDynamicFields = JSON_SET( productDynamicFields, '$.price', CAST(? AS JSON) ) WHERE productSlug = ?"; $stmt = mysqli_prepare($link, $sql); mysqli_stmt_bind_param($stmt, "ss", $priceJson, $targetSlug); mysqli_stmt_execute($stmt);
这里CAST(? AS JSON)是关键,它告诉MySQL把传入的字符串解析为JSON对象,而不是当成带引号的字符串存储。
3. 为什么原来的方法失效?
你之前直接把json_encode的结果拼接进SQL,MySQL会把它视为字符串字面量,自动给它添加外层引号和转义反斜杠,最终存的还是字符串而非JSON对象。用预处理语句+CAST或者直接操作嵌套键,就能彻底避免这个问题。
另外一定要注意:永远不要直接把变量拼接进SQL语句,预处理语句是防止SQL注入的核心手段!
内容的提问来源于stack exchange,提问作者Mxkert
相关产品推荐
相关产品推荐

