LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

SQL多表连接(JOIN)与条件筛选(WHERE)的基础语法与实际应用

admin
2025年8月30日 12:49 本文热度 69

在数据驱动决策的时代,关系型数据库是企业存储和管理数据的主要载体。SQL 作为与数据库交互的 “通用语言”,其多表连接(JOIN)和条件筛选(WHERE)则是解锁数据价值的重要钥匙。想象一个电商平台,用户信息存储在用户表,商品详情在商品表,订单记录在订单表,如何整合这些分散的数据,查询出 “北京地区近一个月购买高端商品的用户清单?”这就需要运用多表连接(JOIN)和条件筛选(WHERE)语法。下面详细拆解 SQL 多表连接(JOIN)和条件筛选(WHERE)在电商场景的应用示例,先回顾一下基础语法。

一、多表连接(JOIN)基础语法

多表连接是SQL中整合多个表数据的核心操作,通过关联不同表的共同字段(通常是主键和外键),将分散的信息合并为完整的数据集。

1、语法结构

SELECT [表1.字段1], [表2.字段2], ...
FROM 表1
[JOIN类型] 表2 
  ON 表1.关联字段 = 表2.关联字段
[其他JOIN子句...];

语法要素

  • JOIN类型:指定连接方式(如INNER JOINLEFT JOIN等)。
  • ON:定义连接条件,指定两表通过哪个字段关联(必须包含,否则会产生笛卡尔积)。
  • 表别名:为表指定简短别名(如表1 AS t1),简化语句。

2、常用JOIN类型及语法

连接类型
作用
语法示例
INNER JOIN
只保留两表中匹配连接条件的记录
FROM t1 INNER JOIN t2 ON t1.id = t2.t1_id
LEFT JOIN
保留左表所有记录,右表无匹配则补NULL
FROM t1 LEFT JOIN t2 ON t1.id = t2.t1_id
RIGHT JOIN
保留右表所有记录,左表无匹配则补NULL
FROM t1 RIGHT JOIN t2 ON t1.id = t2.t1_id
FULL JOIN
保留两表所有记录,无匹配则补NULL(注:MySQL不直接支持FULL JOIN,需通过LEFT JOIN与RIGHT JOIN结合UNION实现)
FROM t1 FULL JOIN t2 ON t1.id = t2.t1_id

关于FULL JOIN的说明

MySQL不支持FULL JOIN,需要通过LEFT JOIN + RIGHT JOIN + UNION实现,替代方案如下:

-- MySQL中模拟FULL JOIN(以用户和订单表为例)
SELECT u.user_id, u.username, o.order_id
FROM users u LEFT JOIN orders o ON u.user_id = o.user_id
UNION
SELECT u.user_id, u.username, o.order_id
FROM users u RIGHT JOIN orders o ON u.user_id = o.user_id
WHERE u.user_id IS NULL;

关于CROSS JOIN的说明

在关系型数据库里,CROSS JOIN 会生成参与连接的表的笛卡尔积。也就是说,结果表的行数是左表行数与右表行数的乘积,列数则是两表列数之和。

下面是一个简单的示例,假设有两个表:
表A(2行):(1, A1)(2, A2)
表B(3行):(10, B1)(20, B2)(30, B3)

执行 SELECT * FROM A CROSS JOIN B (显式语法,建议优先使用)后,结果表会有 2×3=6 行,具体如下:

A.id | A.name | B.id | B.name
1    | A1     | 10   | B1
1    | A1     | 20   | B2
1    | A1     | 30   | B3
2    | A2     | 10   | B1
2    | A2     | 20   | B2
2    | A2     | 30   | B3

1.慎用场景

① 数据量极大:要是参与连接的表数据量很大,笛卡尔积产生的结果可能会超出数据库的处理能力,比如 1000 行×1000 行就会得到 100 万行数据。
② 缺少连接条件:CROSS JOIN 本身不需要连接条件,但如果在实际应用中没有合理的过滤条件,就很可能产生大量无用数据。
③ 性能问题:这种连接操作会消耗大量的系统资源,容易造成数据库性能下降,甚至导致服务中断。

以下示例可以说明为什么数据量大时会导致性能问题:

-- 假设表A有1000行,表B有1000行,CROSS JOIN会产生100万行结果
SELECT COUNT(*) FROM A CROSS JOIN B; -- 结果:1000000

2.替代方案
建议优先使用 INNER JOIN 或者 LEFT JOIN 等连接方式,并添加合适的连接条件,以此来限制结果集的大小。

3、多表连接语法(3表及以上)

SELECT 
  t1.字段1, 
  t2.字段2, 
  t3.字段3
FROM 表1 t1  -- 表1别名t1
INNER JOIN 表2 t2  -- 表2别名t2
  ON t1.id = t2.t1_id  -- 表1与表2的关联条件
INNER JOIN 表3 t3  -- 表3别名t3
  ON t2.id = t3.t2_id;  -- 表2与表3的关联条件

说明:多表连接需按逻辑顺序关联(如“订单表→用户表→地址表”),每增加一个表需新增一个JOIN子句和ON条件。多表连接的顺序会影响性能。建议先关联数据量小的表,或按业务逻辑的层级顺序(如先关联主表,再关联从表),具体为:

  • 小表优先原则:如果其中一个表的数据量远小于其他表(如用户表只有几百行,而订单表有百万行),建议将小表放在最左侧。
  • 索引优化:关联字段应尽量建立索引,避免全表扫描。例如:
-- 为关联字段创建索引
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_users_user_id ON users(user_id);

二、条件筛选(WHERE)基础语法

WHERE子句用于从查询结果中筛选满足条件的记录,可单独使用或配合JOIN使用。

1、语法结构

SELECT 字段1, 字段2, ...
FROM 表名
[JOIN子句...]
WHERE 筛选条件;  -- 可以是比较运算、逻辑运算等

常用筛选条件

  • 比较运算符:=(等于)、!=(不等于)、>(大于)、<(小于)、BETWEEN...AND...(范围内)。
  • 逻辑运算符:AND(且)、OR(或)、NOT(非)。
  • 其他:IN(在列表中)、LIKE(模糊匹配)、IS NULL(为空)等。

2、语法示例

-- 筛选年龄大于30的用户
SELECT * FROM users WHERE age > 30;

-- 筛选性别为女且来自北京的用户
SELECT * FROM users WHERE gender = '女' AND city = '北京';

-- 筛选分数在80-100之间的学生
SELECT * FROM students WHERE score BETWEEN 80 AND 100;

-- 筛选来自北京、上海或广州的用户
SELECT * FROM users WHERE city IN ('北京''上海''广州');

-- 筛选用户名以“张”开头的用户
SELECT * FROM users WHERE username LIKE '张%';

三、场景应用示例

以下通过“电商订单系统”模拟数据,演示多表连接与条件筛选的实际应用。

1、模拟数据

-- 1. 用户表(存储用户基本信息)
CREATE TABLE users (
  user_id INT PRIMARY KEY,  -- 用户ID(主键)
  username VARCHAR(50),     -- 用户名
  city VARCHAR(50)          -- 所在城市
);

-- 2. 商品表(存储商品信息)
CREATE TABLE products (
  product_id INT PRIMARY KEY,  -- 商品ID(主键)
  product_name VARCHAR(50),    -- 商品名称
  price DECIMAL(10,2)          -- 商品单价
);

-- 3. 订单表(存储订单信息,关联用户和商品)
CREATE TABLE orders (
  order_id INT PRIMARY KEY,    -- 订单ID(主键)
  user_id INT,                 -- 下单用户ID(外键,关联users表)
  product_id INT,              -- 商品ID(外键,关联products表)
  order_time DATE,             -- 下单时间
  quantity INT,                -- 购买数量
  FOREIGN KEY (user_id) REFERENCES users(user_id),
  FOREIGN KEY (product_id) REFERENCES products(product_id)
);

-- 插入模拟数据
INSERT INTO users VALUES
(1, '张三''上海'),
(2, '李四''北京'),
(3, '王五''广州'),
(4, '赵六''深圳');  -- 未下单用户

INSERT INTO products VALUES
(101, '笔记本电脑', 5999.00),
(102, '机械键盘', 299.00),
(103, '鼠标', 99.00),
(104, '显示器', 1499.00);

INSERT INTO orders VALUES
(1001, 1, 101, '2023-10-01', 1),  -- 张三买了1台笔记本
(1002, 1, 102, '2023-10-02', 2),  -- 张三买了2个键盘
(1003, 2, 103, '2023-10-01', 3),  -- 李四买了3个鼠标
(1004, 3, 104, '2023-10-03', 1),  -- 王五买了1个显示器
(1005, 2, 101, '2023-10-04', 1);  -- 李四买了1台笔记本
-- 增加一条未关联的订单(演示LEFT JOIN效果)
-- INSERT INTO orders VALUES (1006, 99, 99, '2023-10-05', 1);

2、多表连接(INNER JOIN)示例

查询所有订单的详细信息,包括用户名、商品名称、下单时间和购买数量。

SELECT 
  o.order_id AS 订单ID,
  u.username AS 用户名,       -- 来自用户表
  p.product_name AS 商品名称, -- 来自商品表
  o.order_time AS 下单时间,
  o.quantity AS 购买数量
FROM orders o  -- 订单表,别名o
-- 关联用户表(获取用户名)
-- INNER JOIN示例:只保留有匹配的记录
INNER JOIN users u 
  ON o.user_id = u.user_id  -- 订单的user_id关联用户表的user_id
-- 关联商品表(获取商品名称)
INNER JOIN products p 
  ON o.product_id = p.product_id;  -- 订单的product_id关联商品表的product_id

输出结果

订单ID
用户名
商品名称
下单时间
购买数量
1001
张三
笔记本电脑
2023-10-01
1
1002
张三
机械键盘
2023-10-02
2
1003
李四
鼠标
2023-10-01
3
1004
王五
显示器
2023-10-03
1
1005
李四
笔记本电脑
2023-10-04
1

3、左连接(LEFT JOIN)示例

查询所有用户的订单情况,包括没有下单的用户(未下单的用户订单信息显示NULL)。

SELECT 
  u.user_id AS 用户ID,
  u.username AS 用户名,
  o.order_id AS 订单ID,
  p.product_name AS 商品名称
FROM users u  -- 左表:用户表(保留所有用户)
LEFT JOIN orders o 
  ON u.user_id = o.user_id  -- 关联订单表
LEFT JOIN products p 
  ON o.product_id = p.product_id;  -- 关联商品表

输出结果(假设用户“赵六”未下单:未下单用户,订单信息为NULL):

用户ID
用户名
订单ID
商品名称
1
张三
1001
笔记本电脑
1
张三
1002
机械键盘
2
李四
1003
鼠标
2
李四
1005
笔记本电脑
3
王五
1004
显示器
4
赵六
NULL
NULL

4、多表连接+条件筛选示例

查询2023年10月1日后,北京用户购买的单价大于1000元的商品订单,显示用户名、商品名称、价格和下单时间。

SELECT 
  u.username AS 用户名,
  p.product_name AS 商品名称,
  p.price AS 商品单价,
  o.order_time AS 下单时间
FROM orders o
INNER JOIN users u 
  ON o.user_id = u.user_id
INNER JOIN products p 
  ON o.product_id = p.product_id
-- 条件筛选:北京用户、2023-10-01后下单、单价>1000
WHERE 
  u.city = '北京'  -- 用户来自北京
  AND o.order_time > '2023-10-01'  -- 下单时间在10月1日后
  AND p.price > 1000;  -- 商品单价超1000元
-- 也可以使用 BETWEEN 精确指定日期范围,如:
-- AND o.order_time BETWEEN '2023-10-02' AND '2023-10-31'

输出结果(符合所有条件):

用户名
商品名称
商品单价
下单时间
李四
笔记本电脑
5999.00
2023-10-04

5、复杂条件筛选(IN、BETWEEN)示例

查询购买了“鼠标”或“键盘”,且数量在2-5之间的订单,显示订单ID、商品名称和数量。

SELECT 
  o.order_id AS 订单ID,
  p.product_name AS 商品名称,
  o.quantity AS 购买数量
FROM orders o
INNER JOIN products p 
  ON o.product_id = p.product_id
WHERE 
  p.product_name IN ('鼠标''机械键盘')  -- 商品是鼠标或键盘
  AND o.quantity BETWEEN 2 AND 5;  -- 数量在2-5之间

输出结果(数量2与3符合条件):

订单ID
商品名称
购买数量
1002
机械键盘
2
1003
鼠标
3

6、分组聚合(进阶)示例

查询每个城市的用户购买的商品类别数量。

SELECT 
  u.city AS 城市,
  COUNT(DISTINCT p.product_name) AS 购买商品种类数
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id
INNER JOIN products p ON o.product_id = p.product_id
GROUP BY u.city;

输出结果(未下单不计):

城市
购买商品种类数
上海
2
北京
2
广州
1

四、场景应用进阶

代理商可以代理很多种商品,但某一代理商只属于某一个厂家,正常情况下代理商品也应该只属于该厂家。现在需要查询出所有“代理商代理商品的所属厂家与代理商的所属厂家不同”的记录。

1、建表语句

-- 厂家表
CREATE TABLE factories (
    factory_id INT PRIMARY KEY,
    factory_name VARCHAR(50) NOT NULL
);
-- 代理商表
CREATE TABLE agents (
    agent_id INT PRIMARY KEY,
    agent_name VARCHAR(50) NOT NULL,
    factory_id INT NOT NULL -- 代理商所属厂家,与表factories关联
);
-- 商品表
CREATE TABLE products (
    product_id INT PRIMARY KEY,
    product_name VARCHAR(50) NOT NULL,
    product_factory_id INT NOT NULL  -- 商品所属厂家,与表factories关联
);
-- 代理商与商品的关联表
CREATE TABLE distribution (
    agent_id INT NOT NULL,  -- 代理商id,与表agents关联
    product_id INT NOT NULL,  -- 商品id,与表products关联
    PRIMARY KEY (agent_id, product_id)
);

2、插入数据语句

INSERT INTO factories (factory_id, factory_name) VALUES
(1, 'A电子厂'),
(2, 'B文具厂'),
(3, 'C玩具厂');

INSERT INTO agents (agent_id, agent_name, factory_id) VALUES
(101, '张代理', 1),  -- 属于A电子厂
(102, '李代理', 2),  -- 属于B文具厂
(103, '王代理', 3);  -- 属于C玩具厂

INSERT INTO products (product_id, product_name, product_factory_id) VALUES
(201, '智能手机', 1),    -- A电子厂商品
(202, '笔记本', 2),    -- B文具厂商品
(203, '积木', 3),    -- C玩具厂商品
(204, '平板电脑', 1),    -- A电子厂商品
(205, '钢笔', 2);    -- B文具厂商品

INSERT INTO distribution (agent_id, product_id) VALUES
-- 正常情况(代理商代理本厂家商品)
(101, 201),  -- 张代理(A电子)代理智能手机(A电子)
(102, 202),  -- 李代理(B文具)代理笔记本(B文具)
-- 异常情况
(101, 205),  -- 张代理(A电子)代理钢笔(B文具)
(103, 204);  -- 王代理(C玩具)代理平板电脑(A电子)

3、查询语句

-- 选择需要展示的字段,并指定别名
select 
  ag.agent_id 代理商ID,
  ag.agent_name 代理商姓名,
  fa.factory_name 代理商所属厂家,  -- 代理商所属厂家名称
  dt.product_id 商品ID,
  pd.product_name 商品名称,
  fb.factory_name 商品所属厂家   -- 商品所属厂家名称
from 
  agents ag  -- 代理商表,别名ag
-- 关联代理商所属厂家表(获取代理商的厂家名称)
join factories fa 
  on fa.factory_id = ag.factory_id  -- 关联条件:厂家ID匹配
-- 关联代理商与商品的关联表(获取代理商代理的商品)
join distribution dt 
  on dt.agent_id = ag.agent_id  -- 关联条件:代理商ID匹配
-- 关联商品表(获取商品信息)
join products pd 
  on pd.product_id = dt.product_id  -- 关联条件:商品ID匹配
-- 关联商品所属厂家表(获取商品的厂家名称)
join factories fb 
  on fb.factory_id = pd.product_factory_id  -- 关联条件:厂家ID匹配
-- 筛选条件:代理商所属厂家与商品所属厂家不同
where
  fa.factory_id != fb.factory_id;  -- 用ID对比更严谨(避免名称重复问题)

4、输出结果

所有“代理商代理商品的所属厂家与代理商的所属厂家不同”的记录:

代理商ID
代理商姓名
代理商所属厂家
商品ID
商品名称
商品所属厂家
101
张代理
A电子厂
205
钢笔
B文具厂
103
王代理
C玩具厂
204
平板电脑
A电子厂

5、语法解析

主要涉及SQL中的多表连接(JOIN) 和条件筛选(WHERE) 等语法,具体如下:

1. 多表连接(JOIN)

多表连接是SQL中用于合并多个表数据的核心操作,通过关联不同表中的共同字段(通常是主键和外键),将分散在多个表中的信息整合到一起。这里是确保获取代理商的基本信息、所属厂家,以及其代理的商品信息和商品所属厂家。

这里使用的是INNER JOIN(可简写为JOIN),通过多表join关联所需数据,关联顺序为:
agents(代理商)→ factories(代理商所属厂家)→ distribution(代理商与商品的关系)→ products(商品)→ factories(商品所属厂家)。具体连接逻辑如下:

  • agentsfactories(别名fa)连接:通过factory_id获取代理商所属厂家的名称。
  • agentsdistribution连接:通过agent_id获取代理商所代理商品的ID。
  • distributionproducts连接:通过product_id获取商品的名称和所属厂家ID。
  • productsfactories(别名fb)连接:通过product_factory_id获取商品所属厂家的名称。
2. 条件筛选(WHERE)

条件筛选用于从连接后的结果中提取满足特定条件的记录,通过WHERE子句实现。这里的筛选逻辑如下:
核心条件fa.factory_id != fb.factory_id用于判断“代理商所属厂家”与“商品所属厂家”是否不同,直接定位到异常的记录。
筛选依据:使用factory_id而非factory_name,利用主键的唯一性确保判断准确(避免因厂家名称重复导致的错误)。

3. 别名(Alias)的使用

为表和字段指定别名(如ag代表agentsfa代表代理商所属的factories),简化语句结构,提高可读性,尤其在多表连接时可避免字段名冲突(如两个factories表需区分别名)。

4. 字段选择与展示

通过SELECT子句指定需要输出的字段,并为其设置中文别名(如“代理商ID”“商品所属厂家”),使结果更直观。

5. 无冗余操作

无多余子查询:直接通过表连接筛选结果,避免临时表生成,效率高。
无冗余操作:无需group by(因distribution表主键确保每条代理关系记录唯一,无需去重),减少计算成本。

五、总结

1、多表连接是整合数据的基础,需根据业务需求选择合适的连接类型(如INNER JOIN取交集,LEFT JOIN保留左表全部数据),并通过ON指定明确的关联条件。
2、条件筛选通过WHERE子句实现,支持多种运算符组合,可精准过滤数据。
3、实际开发中,多表连接与条件筛选通常结合使用,先关联表获取完整数据,再通过筛选条件提取目标结果。熟练掌握多表连接和条件筛选后,可以进一步学习子查询、窗口函数等高级功能,以处理更复杂的业务场景。

通过上述示例可见,掌握多表连接和条件筛选能有效解决实际业务中的数据查询需求,只要夯实多表连接和条件筛选的基础,未来面对子查询、窗口函数等高级功能,乃至大数据分析与数据仓库搭建,都能游刃有余。


阅读原文:原文链接


该文章在 2025/9/1 11:55:46 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved