如何使用Yii Query Builder编写符合预期的SQL关联查询
问题根因
Yii Query Builder的条件拼接遵循SQL逻辑运算符优先级,链式调用andWhere()、orWhere()时不会自动为独立条件块添加分组括号:你在orWhere()之后调用的所有andWhere(),默认会作用于整个WHERE层级的条件,而非仅绑定到OR后面的条件组,最终就会出现条件嵌套混乱、逻辑和预期不符的问题。
正确实现代码
要完全匹配你给出的原生SQL逻辑,需要将OR两侧的条件分别封装为独立的逻辑分组,再统一拼接,避免优先级错乱:
Table1::find() ->joinWith(['table2']) ->where([ 'or', // OR 左侧的条件组 [ 'and', ['Table1.idOwner' => $idOwner, 'Table2.enabled' => 1], ['<=', 'day(Table2.creationDate)', $expirationDay], ['=', "date_format(Table2.creationDate, '%Y-%m')", $previousExpirationMonthYear] ], // OR 右侧的条件组 [ 'and', ['Table1.idOwner' => $idOwner, 'Table2.enabled' => 1], ['!=', "date_format(Table2.creationDate, '%Y-%m')", $currentExpirationMonthYear], ['!=', "date_format(Table2.creationDate, '%Y-%m')", $previousExpirationMonthYear] ] ]) ->all();
优化写法
观察原SQL逻辑可以发现,两个OR条件组都包含公共条件Table1.idOwner = 156 AND Table2.enabled = 1,可以把公共条件提取出来简化代码,生成的SQL执行逻辑完全一致:
Table1::find() ->joinWith(['table2']) ->where([ 'and', // 公共前置条件 ['Table1.idOwner' => $idOwner, 'Table2.enabled' => 1], [ 'or', // 第一组分支条件 [ 'and', ['<=', 'day(Table2.creationDate)', $expirationDay], ['=', "date_format(Table2.creationDate, '%Y-%m')", $previousExpirationMonthYear] ], // 第二组分支条件 [ 'and', ['!=', "date_format(Table2.creationDate, '%Y-%m')", $currentExpirationMonthYear], ['!=', "date_format(Table2.creationDate, '%Y-%m')", $previousExpirationMonthYear] ] ] ]) ->all();
调用getRawSql()即可验证生成的语句和预期原生SQL逻辑完全匹配,不会出现多余的错误嵌套条件。
内容的提问来源于stack exchange,提问作者Carlos
相关产品推荐
相关产品推荐

