LEFT JOIN中ON加IsDeleted=0无效,需用WHERE o.IsDeleted=0 OR o.Id IS NULL保留无关联记录;INNER JOIN可直接在ON中过滤;推荐用视图或CTE封装逻辑删除条件,多层关联须逐层显式处理IsDeleted。

JOIN时ON子句里加IsDeleted = 0不等于逻辑删除过滤生效
直接在ON子句里写t2.IsDeleted = 0看似合理,但对LEFT JOIN会出问题:被关联表(右表)记录若本身已逻辑删除,该行仍会保留在结果中,只是所有t2.*字段为NULL——因为ON只控制“如何匹配”,不控制“是否保留”。真正要筛掉这些行,得靠WHERE或把逻辑移到ON的匹配条件里并配合INNER JOIN语义。
LEFT JOIN场景下必须把IsDeleted判断挪到WHERE子句
当需要左表全量返回、仅排除右表中已逻辑删除的关联项时,ON里加IsDeleted无效;正确做法是保持ON只做关联,再用WHERE过滤右表非删除记录(注意NULL处理):
SELECT u.*, o.OrderNo FROM Users u LEFT JOIN Orders o ON u.Id = o.UserId WHERE o.IsDeleted = 0 OR o.Id IS NULL;
这个OR o.Id IS NULL很关键——它保留了没订单的用户。漏掉它,所有无订单用户都会被WHERE o.IsDeleted = 0过滤掉。
-
INNER JOIN可直接在ON里加IsDeleted = 0,等效于WHERE -
LEFT JOIN中,ON加IsDeleted只会让匹配失败,不阻止NULL填充 - 若业务要求“只显示有有效订单的用户”,改用
INNER JOIN更清晰,避免WHERE歧义
用视图或CTE预封装逻辑删除过滤更安全
重复写IsDeleted = 0易遗漏,也难维护。推荐提前抽象出“有效数据”层:
CREATE VIEW ActiveOrders AS SELECT * FROM Orders WHERE IsDeleted = 0;
后续查询直接JOIN ActiveOrders,无需每次想条件。CTE也适用:
WITH ActiveOrders AS ( SELECT * FROM Orders WHERE IsDeleted = 0 ) SELECT u.Name, o.OrderNo FROM Users u LEFT JOIN ActiveOrders o ON u.Id = o.UserId;
这种写法把过滤逻辑收口,避免JOIN类型变化时条件错位,也方便后期统一加租户ID、时间范围等额外约束。
外键关联表多层嵌套时,每层IsDeleted都得显式处理
比如查用户→订单→订单项→商品,四层关联。只要某一层漏了IsDeleted判断,就可能带出已删除的中间数据。不能指望“父层过滤了,子层自然干净”——SQL没有级联逻辑删除的隐式传播。
- 每个
JOIN对应的表,若含IsDeleted字段,就必须在ON或WHERE中明确参与过滤 - 用视图封装时,注意视图定义里是否已包含该表的
IsDeleted条件;未包含则仍需额外加 - ORM生成SQL时尤其危险:有些框架对逻辑删除字段支持不完整,生成的JOIN可能完全忽略
IsDeleted
最易被忽略的是多对多关联表(如UserRoles),它自己也有IsDeleted,但常被当成纯关系表而跳过判断。










