1:注意点
1.1:描述:
用户表用8人,但是订单表只有7个人的订单,在统计每个用户的订单数量的时候,要注意count(*),count(列名),null值的处理
1.2:原有SQL:
sql
select a.user_id,count(*) from dim_users a left join fact_orders b
on a.user_id = b.user_id
group by a.user_id
1.3:发现问题(结果)
结果是,一个用户没有订单时,根据下面的SQL,没有订单的用户 获取得到的订单总数 = 1;
1.4:分析和解决问题
1:问题描述
select a.user_id,count(*) from dim_users a left join fact_orders b on a.user_id = b.user_id group by a.user_id ; 为什么表dim_users有8个用户,表fact_orders只有7个用户的订单;但是最后没有订单的用户=1;
核心:count(*) 和count(列名)
COUNT(*)统计的是行数(是否存在这行)。
COUNT(列名)统计的是该列中非 NULL 值的个数。
1.2:分析:
关键点在于,由于是
LEFT JOIN,主表dim_users的所有记录都会被保留 。对于没有匹配订单的用户U8,来自fact_orders表的所有字段都会用NULL填充。接下来,
GROUP BY a.user_id对这个结果集进行分组,然后COUNT(*)统计每个分组内的行数:
对于用户 U1 到 U7:他们的分组里都至少有一条与订单匹配的记录,
COUNT(*)的结果就是他们的订单数量(可能是1,也可能是多条)。对于用户 U8:他的分组里只有 1 行 (就是那条由
LEFT JOIN生成的、订单字段全为NULL的行)。因此,COUNT(*)的结果是 1。
1.3:结论和建议
✅ 结论与建议
所以,你的查询结果准确地显示了:8个用户都出现在结果中,其中7个有订单的用户显示了订单数,那1个没有订单的用户计数为1。
如果你想更直观地看到"没有订单的用户数为0",可以将
COUNT(*)改为COUNT(b.user_id)或COUNT(b.order_id)。这是因为COUNT(列名)会忽略该列的NULL值。SELECT
a.user_id,
COUNT(*) AS total_rows, -- 会为无订单用户返回1
COUNT(b.user_id) AS order_count -- 会为无订单用户返回0
FROM dim_users a
LEFT JOIN fact_orders b ON a.user_id = b.user_id
GROUP BY a.user_id;
运行上面的修改后查询,你就能清楚地看到
COUNT(*)和COUNT(特定列)在处理NULL值时的核心区别了。如果你还有其他关于 SQL 逻辑的疑问,随时可以提出。
1.5:区分count(*)和count(列名)
COUNT(*)是"元组计数"
它关心的是"行"这个容器本身 。只要这行数据存在,无论其内部字段是值还是
NULL,它都客观占据一行。因此,它必然包含所有行。在上面的
LEFT JOIN例子中,即使用户U8对应的订单字段全是NULL,但这一行数据是确定存在的 ,所以COUNT(*)会将其计入。
COUNT(列名)是"值计数"
它关心的是某个特定"值"是否有效 。在 SQL 中,
NULL表示"未知"、"缺失"或"不适用",不是一个有效的"值"。因此,在统计时,所有NULL都会被排除在外。在
LEFT JOIN的例子中,对COUNT(b.user_id)来说,用户U8对应的b.user_id是NULL,所以被忽略,结果为0。