在yii2中用active record实现“无子记录”筛选需用exists而非in,子查询关联条件必须写成字符串'interval_id={{a}}.id'以确保外层表引用生效,且not exists对null安全并支持索引。

要在Yii2中用Active Record模型写带子查询的复杂条件,比如查出“没有特定code子记录的主表数据”,必须避开字符串拼接陷阱,让关联字段真正被数据库识别为外层表引用。
用exists实现“无子记录”筛选
第一步:调用A_AR::find()启动主查询。
第二步:在where中传入数组,第一个元素固定为字符串'exists',第二个元素是B_AR::find()构建的子查询对象。
第三步:子查询里用字符串形式写关联条件——【'interval_id={{A}}.id'】,其中{{A}}是主查询给A_AR起的别名(AR默认别名即模型类名小写),不能写成['interval_id' => '{{A}}.id'],否则会被当成字面量字符串匹配,永远查不到结果。
第四步:子查询继续链式调用andWhere(['code' => 'a'])限定子表筛选条件。
用not exists反向表达“排除有子记录的项”
方法一:直接套用exists结构,但把整个条件包进NOT()函数里。
方法二:改用Query Builder拼装,先生成子查询SQL再注入主查询,适合需要多层嵌套或跨库关联的场景。
注意:如果子查询涉及多个JOIN或GROUP BY,优先考虑用with()预加载+PHP层过滤,避免数据库端过度嵌套导致执行计划失效。
避免IN子查询返回NULL导致全不匹配
当业务逻辑本意是“排除code为a的子项”,但子表code字段允许NULL时,用NOT IN (SELECT code FROM b WHERE ...)会因NULL传播特性使整行被跳过。
这时必须改用NOT EXISTS,它对NULL天然免疫,且能走索引——只要B_AR表的interval_id和code字段联合建了索引,性能几乎与等值查询持平。
这一步操作起来很简单,直接把where里的IN换成EXISTS,再补上关联条件就行。











