SQL进阶:如何利用左连接解决包含零订单的消费群体统计

一颗松果
一颗松果
发布时间:2026-10-14| 5人浏览过
原创

在进行数据提取时,我们经常需要找出满足特定条件的用户群体,例如“来自某地且订单数极少”的客户。这类需求在分析流失风险或营销潜力时非常常见。本文将通过一个具体案例,演示如何构建SQL查询,重点讲解在使用LEFT JOIN(左连接)处理未匹配数据时的注意事项,确保统计结果的准确性。

问题目标与数据准备

我们的目标是找出:居住在城市为 “Madrid”,且历史订单总数少于 3 个的消费者。

为了验证逻辑,我们需要建立两张测试表:

  • dbo.Customers:存储消费者信息,包括 customerid(主键)和 city(城市)。
  • dbo.Orders:存储订单记录,包括 orderid(主键)和 customerid(外键,引用客户表)。

创建表的完整脚本如下:

SET NOCOUNT ON
USE SY
GO
-- 清理旧表以确保环境干净
IF OBJECT_ID('dbo.Orders') IS NOT NULL DROP TABLE dbo.Orders;
IF OBJECT_ID('dbo.Customers') IS NOT NULL DROP TABLE dbo.Customers;
GO

-- 1. 创建客户表
CREATE TABLE dbo.Customers (
    customerid CHAR(5) NOT NULL PRIMARY KEY,
    city VARCHAR(10) NOT NULL
);

-- 插入测试数据
INSERT INTO dbo.Customers VALUES('FISSA','Madrid');
INSERT INTO dbo.Customers VALUES('FRNDO','Madrid');
INSERT INTO dbo.Customers VALUES('KRLOS','Madrid');
INSERT INTO dbo.Customers VALUES('MRPHS','Zion');

-- 2. 创建订单表
CREATE TABLE dbo.Orders (
    orderid INT NOT NULL PRIMARY KEY,
    customerid CHAR(5) NULL REFERENCES Customers(customerid)
);

-- 插入测试数据(注意 FISSA 没有订单记录)
INSERT INTO dbo.Orders VALUES(1,'FRNDO');
INSERT INTO dbo.Orders VALUES(2,'FRNDO');
INSERT INTO dbo.Orders VALUES(3,'KRLOS');
INSERT INTO dbo.Orders VALUES(4,'KRLOS');
INSERT INTO dbo.Orders VALUES(5,'KRLOS');
INSERT INTO dbo.Orders VALUES(6,'MRPHS');
INSERT INTO dbo.Orders VALUES(7,NULL); -- 无效订单数据

此时数据状态如下:FISSA, FRNDO, KRLOS 来自 Madrid;其中 FIISA 没有任何订单记录,而 ORDERS 表中甚至存在一条 customerid 为空的垃圾数据。

第一种尝试:内查询与内连接的局限性

面对此题,最直观的思路是先筛选出所有来自 Madrid 的客户 ID,然后在订单表中统计他们的订单量。

第1步:使用子查询+WHERE过滤

我们可以利用 IN 关键字进行嵌套查询:

SELECT customerid AS 消费者,
       COUNT(customerid) AS 订单数
FROM dbo.Orders
WHERE customerid IN (SELECT customerid FROM dbo.Customers WHERE city = 'Madrid')
GROUP BY customerid
HAVING COUNT(customerid) < 3;

执行结果如图所示:

分析缺陷:这种写法虽然语法正确,但忽略了那些“符合条件但在另一张表中没有记录”的行。由于 FISSA 在 Orders 表中完全不存在,上述查询会从 Orders 的视角出发,直接漏掉了 FISSA,导致返回结果中缺失了一个应被统计的消费者(其订单数为0)。

即便改用经典的 INNER JOIN 方式,只要核心过滤条件写在错误的位置或使用了内连接,依然无法保留左侧表中没有右表数据的记录:

-- 同样的逻辑陷阱
SELECT C.customerid,
       COUNT(O.customerid) AS 订单数
FROM dbo.Customers AS C
INNER JOIN dbo.Orders AS O ON C.customerid = O.customerid
WHERE C.city = 'Madrid'
GROUP BY C.customerid
HAVING COUNT(O.customerid) < 3;

正确的解决方案:LEFT JOIN(左连接)的应用

要找回那个订单数为 0 的 FISSA,我们必须使用左连接(LEFT JOIN)。它的作用是:无论右侧表是否有匹配的记录,都会将左侧表(Customers)的所有行都返回出来。对于没有匹配行的记录,右侧字段显示为 NULL。

第2步:构建完整的左连接查询

我们将两表进行左连接,并将筛选城市的条件保留在 WHERE 子句中,最后通过 HAVING 过滤掉订单数>=3的用户:

SELECT C.customerid AS 消费者,
       COUNT(O.customerid) AS 订单数
FROM dbo.Customers AS C
LEFT JOIN dbo.Orders AS O ON C.customerid = O.customerid
WHERE C.city = 'Madrid'
GROUP BY C.customerid
HAVING COUNT(O.customerid) < 3;

执行结果如图所示:

可以看到,这次的列表中包含了 FISSA(订单数为0),以及只有1单和2单的 FRNDO。这正是题目要求的精准范围。

第3步:深入理解 LEFT JOIN 的数据表现

为了看清为什么这个写法有效,我们单独观察不带任何限制条件的左连接全景图。这里揭示了当左表有数据而右表无对应数据时,数据库是如何处理的:

SELECT * FROM dbo.Customers AS C
LEFT JOIN dbo.Orders AS O ON C.customerid = O.customerid;

中间过程展示:

从结果可以看出,像 FISSA 这样的客户,在与订单表关联后,对应的 order 列全部呈现为 NULL。而在前面的 HAVING COUNT(O.customerid) < 3 操作中,NULL 值不会被 COUNT 计数(即视为 0),从而完美符合题意。

标准答案优化与排序建议

书中最严谨的标准写法与我们推导的结果一致,但增加了针对输出结果易读性的排序指令:

SELECT C.customerid,
       COUNT(O.orderid) AS numorders
FROM dbo.Customers AS C
LEFT JOIN dbo.Orders AS O ON C.customerid = O.customerid
WHERE C.city = 'Madrid'
GROUP BY C.customerid
HAVING COUNT(O.orderid) < 3
ORDER BY numorders ASC;

最终结果如图所示:

多出的 ORDER BY numorders 并非功能必需,而是业务规范的一部分——它将结果按照订单数量从小到大排列,让低活跃度用户排在最前面,方便后续跟进。

总结

处理此类统计问题的关键在于识别是否包含“零匹配”项。若需保留这些项,请务必使用 LEFT JOIN,且关联键的判断必须在 ON 子句中完成,而非挪到 WHERE 中,以免意外退化为内连接效果。以上就是关于SQL左连接统计零订单消费者的详细内容,更多关于SQL查询技巧的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。