Yii2 ActiveRecord查询JSON字段时反引号引发SQL错误
问题描述
直接执行以下SQL可正常获取JSON类型字段saved中的total值:
SELECT `id`, `created`, `type`, `saved`->>"$.total" AS `total` FROM `invoices` LIMIT 20;
但在Yii2的ActiveRecord查询中使用如下写法时:
$query = Invoices::find()->select(['id','created','type','`saved`->>"$.total" AS total']);
框架会自动给JSON键total添加多余反引号,导致生成的SQL失效:
SELECT `id`, `created`, `type`, `saved`->>"$.`total"` AS `total` FROM `invoices` LIMIT 20
尝试多种写法均报错:
- 去掉
saved的反引号:
$query = Invoices::find()->select(['id','created','type','saved->>"$.total" AS total']);
生成的SQL提示未知列:
SELECT `id`, `created`, `type`, `saved->>"$`.`total"` AS `total` FROM `invoices` LIMIT 20
- 给JSON路径加反引号:
$query = Invoices::find()->select(['id','created','type','`saved`->>`"$.total"` AS total']);
生成的SQL报语法错误:
SELECT `id`, `created`, `type`, `saved`->>`"$.total"` AS `total` FROM `invoices` LIMIT 20
解决方案
方法1:用Expression类包裹原生SQL片段
直接使用yii\db\Expression避免框架自动转义,这是最直接的解决方式:
use yii\db\Expression; $query = Invoices::find()->select([ 'id', 'created', 'type', new Expression('`saved`->>"$.total" AS total') ]);
Expression会将内容作为原生SQL直接插入,不会进行自动转义处理,生成的SQL格式完全符合需求。
方法2:使用JSON函数替代语法糖
用MySQL原生的JSON_EXTRACT和JSON_UNQUOTE函数替代->>语法糖,同样通过Expression传递:
use yii\db\Expression; // 等价于`saved`->>"$.total" $query = Invoices::find()->select([ 'id', 'created', 'type', 'total' => new Expression('JSON_UNQUOTE(JSON_EXTRACT(saved, "$.total"))') ]);
这种写法更规范,也能避免框架的自动转义干扰。
方法3:给模型添加虚拟属性
如果需要在模型中直接使用total属性,可以在Invoices模型中添加虚拟属性并通过getter方法赋值:
class Invoices extends ActiveRecord { public function getTotal() { $savedData = json_decode($this->saved, true); return $savedData['total'] ?? null; } }
之后查询时无需手动指定select,直接通过$invoice->total即可获取值,适合小数据量场景;大数据量场景推荐用前两种SQL层面的处理方式,性能更优。
内容的提问来源于stack exchange,提问作者SaidbakR
相关产品推荐
相关产品推荐

