You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

尝试多种写法均报错:

  1. 去掉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 
  1. 给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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 06:50:19