SQL及Eloquent中如何排除满足指定关联条件的TableA数据行?
原生SQL逻辑实现
首先明确需求:排除同时满足以下两个条件的TableA记录:
- TableA自身的status_id取值为1
- 存在关联TableB的对应记录,且TableB的status_id取值为2
根据德摩根逻辑定律,排除「A且B」等价于保留「非A 或 非B」,有两种常用实现方案:
方案1:使用NOT EXISTS(推荐,性能更优)
EXISTS子查询匹配到符合条件的记录就会终止扫描,不需要全量关联表,数据量大时效率远高于JOIN后过滤或WHERE IN写法
SELECT a.* FROM TableA a WHERE NOT ( a.status_id = 1 AND EXISTS ( SELECT 1 FROM TableB b WHERE b.table_a_id = a.id AND b.status_id = 2 ) );
方案2:使用WHERE NOT IN(仅适合小表场景)
如果你更熟悉IN语法也可以用,但是TableB数据量大时不推荐,DISTINCT去重会带来额外性能开销
SELECT a.* FROM TableA a WHERE NOT ( a.status_id = 1 AND a.id IN ( SELECT DISTINCT table_a_id FROM TableB WHERE status_id = 2 ) );
需求偏差提示:如果你实际要排除的是满足任意一个条件的记录,只需要把上面逻辑里的
OR改成AND即可,示例SQL:SELECT a.* FROM TableA a WHERE a.status_id != 1 AND NOT EXISTS ( SELECT 1 FROM TableB b WHERE b.table_a_id = a.id AND b.status_id = 2 );
Eloquent 实现写法
等你需要转Laravel Eloquent时,可以直接用框架封装的whereDoesntHave方法,本质是对NOT EXISTS的语法封装,代码更简洁。
首先确保你的TableA模型已经定义了和TableB的关联:
// App\Models\TableA.php public function tableBs() { return $this->hasMany(TableB::class); }
查询代码如下,和原生SQL逻辑完全对应:
// 保留「status_id≠1 或 无符合条件的TableB关联记录」的行 $result = TableA::where(function ($query) { $query->where('status_id', '!=', 1) ->orWhereDoesntHave('tableBs', function ($bQuery) { $bQuery->where('status_id', 2); }); })->get();
如果是要排除任意一个条件的场景,写法简化为:
$result = TableA::where('status_id', '!=', 1) ->whereDoesntHave('tableBs', function ($bQuery) { $bQuery->where('status_id', 2); })->get();
内容的提问来源于stack exchange,提问作者Gimme Cookiz
相关产品推荐
相关产品推荐

