You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django查询排除指定数据时出现异常:排除2条数据却导致结果集大幅缩减

排查Django QuerySet排除数据后计数异常的问题

这问题确实够诡异的!我之前在处理带关联和聚合的QuerySet时也碰到过类似的计数偏差,大概率是JOIN导致的笛卡尔积或者聚合逻辑的小问题,给你几个具体的排查方向:

1. 先确认原QuerySet的计数是否是「唯一记录数」

你打印的qs.count()是97,但因为你的QuerySet里关联了sku__reconciliation_activity(一对多关系),Django执行查询时会做JOIN操作——如果一个sku对应多条reconciliation_activity记录,一条主表数据会被拆分成多条结果,导致count出来的是JOIN后的行数,不是实际唯一的sku数量。

你可以先验证下:

# 查看去重后的真实sku数量
print(qs.distinct('sku').count())

如果这个数值接近70,那基本就是笛卡尔积导致的计数偏差,原97是重复后的行数,不是真实的待处理记录数。

2. 拆分过滤器,分步验证数据分布

你现在的排除逻辑是~Q(action=NO_ACTION, missing__gte=1),但action和missing都是从关联表annotate来的,可能存在NULL值或者关联数据异常,导致交集统计不准。建议分步查询:

# 统计所有action为NO_ACTION的记录数
print("NO_ACTION总数:", qs.filter(action=ReconciliationActivity.ActionChoices.NO_ACTION).count())
# 统计所有missing>=1的记录数
print("missing>=1总数:", qs.filter(missing__gte=1).count())
# 统计两者的交集(你认为的脏数据)
print("脏数据交集:", qs.filter(action=ReconciliationActivity.ActionChoices.NO_ACTION, missing__gte=1).count())

如果前两个数值远大于你预期,说明很多记录被关联表的数据重复展开了,这时候的「脏数据数量2」其实是重复后的行数,不是真实的脏sku数量。

3. 检查annotate里的表达式错误

看你的代码里有个明显的小问题:

Sum( 'ending_warehouse_balance', filter=Q(disposition='SELLABLE') ), total_units=Sum( F('ending_warehouse_balance') + 
Abs('in_transit_between_warehouses') + 
Abs('customer_shipments') 
)

这里的Abs('in_transit_between_warehouses')应该写成Abs(F('in_transit_between_warehouses'))!直接传字符串的话,Django会把它当成字段名的字符串字面量,而不是字段值的表达式,这会导致total_units计算错误,进而触发~Q(total_units=0)过滤掉大量本不该被排除的记录,这可能是你得到70的直接原因!

另外,missing_subquery_count = Count(missing_subquery_count)这个写法也有问题,Count函数需要传入字段名或者聚合对象,你这里用变量名当参数,大概率是笔误,应该改成比如Count('id')或者对应Subquery的聚合字段。

4. 用去重后的sku列表验证真实数量

如果前面的排查还没找到问题,直接用sku去重后的列表来统计:

# 原QuerySet的唯一sku集合
original_skus = set(qs.values_list('sku', flat=True))
print("真实sku总数:", len(original_skus))

# 排除脏数据后的唯一sku集合
filtered_skus = set(qs.filter(
    ~Q(action=ReconciliationActivity.ActionChoices.NO_ACTION, missing__gte=1)
).values_list('sku', flat=True))
print("排除后sku数:", len(filtered_skus))

# 脏数据的唯一sku集合
dirty_skus = set(qs.filter(
    action=ReconciliationActivity.ActionChoices.NO_ACTION, missing__gte=1
).values_list('sku', flat=True))
print("真实脏sku数:", len(dirty_skus))

这样就能明确是记录重复导致的计数偏差,还是真的有记录被错误过滤了。


内容的提问来源于stack exchange,提问作者mohamed naser

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 16:37:28