必须用exists做存在性检查而非count或all;基础写法是调用模型exists()方法生成EXISTS子查询返回布尔值;关联检查需手写含外层字段的子查询;优化需建联合索引、EXPLAIN验证、替换IN为EXISTS。

要在Yii模型中快速判断某条记录是否存在关联数据,避免全量查询拖慢接口响应,必须用exists做存在性检查而非先查再count或all。
基础写法:用exists()方法直接返回布尔值
调用模型的exists()方法,它会生成带EXISTS子查询的SQL,只返回true/false,不拉取任何实际数据行。
Customer::find()->where(['status' => 'active'])->exists();
这一步执行后立刻返回一个PHP布尔值,内存占用极低,适合高频校验场景。如果误用count(),数据库仍需扫描所有匹配行并计数,性能差距在万级数据时可达3倍以上。
关联表存在性检查:手写EXISTS子查询
当需要判断“用户是否有未完成订单”这类跨表关系时,不能只靠主模型的where条件,必须嵌套子查询并建立字段关联。
方法一:使用Query Builder拼接子查询
在Customer模型中执行:
$subQuery = (new \yii\db\Query())→select('1')→from('order')→where('order.customer_id = customer.id AND order.status != :status', [':status' => 'completed']);
Customer::find()→where(['exists', $subQuery])→all();
注意:子查询里【必须出现外层表的字段(如customer.id)】,否则变成非相关子查询,会导致全表误判——比如漏掉关联条件,结果所有客户都会被返回。
方法二:用原生SQL字符串(仅限简单场景)
Customer::find()→where('EXISTS (SELECT 1 FROM `order` WHERE `order`.`customer_id` = `customer`.`id` AND `order`.`status` = :status)', [':status' => 'pending'])→all();
实战优化:三步确保EXISTS高效执行
第一步:确认子查询表有覆盖关联字段和过滤条件的联合索引
例如订单表需有(customer_id, status)复合索引,否则MySQL可能放弃使用索引而全表扫描子查询。
第二步:用EXPLAIN验证执行计划
在MySQL客户端运行EXPLAIN SELECT * FROM customer WHERE EXISTS (SELECT 1 FROM `order` WHERE `order`.`customer_id` = `customer`.`id` AND `order`.`status` = 'pending');,观察type列是否为eq_ref或ref,若出现ALL则说明索引失效。
第三步:替换IN为EXISTS的明确信号
当原逻辑是Customer::find()->where(['id' => Order::find()->select('customer_id')->where(['status' => 'pending'])->column()])->all();,直接改用exists写法,避免生成巨大临时数组和PHP层内存压力。


















