2.1SQL 学习:先懂数据库概念再学 SQL
开篇:为什么学SQL前要先搞懂数据库概念
我入行第一年,领导丢给我一个数据库账号,说"去把昨天的订单数据查出来"。我打开Navicat,看到左边一长串陌生的表名,完全不知道从哪里下手。后来硬着头皮学了SQL语法,写了个SELECT * FROM orders,跑出来结果,但完全不明白为什么数据存在"表"里,为什么订单号不能重复。直到有一次我写DELETE语句忘了加WHERE,把整张表清空了,才被迫去搞懂数据库的底层概念。
很多新人学SQL,一上来就背语法,但连"表""字段""主键""索引"是什么都没搞清楚。这样学出来的SQL,写对了是运气,写错了是常态。这一章不讲任何SQL语法,只讲关系型数据库的核心概念。学完之后,你会彻底搞懂:
-
数据为什么存在"表"里,而不是一个Excel文件
-
订单号为什么不能重复
-
用户表和订单表怎么通过"外键"关联
-
为什么加了索引查询就变快
这些概念是SQL学习的"地基"。地基不牢,后面学再多语法也会在各种报错里反复折腾。
学习前准备:一支笔、一张纸,梳理一下你日常工作中接触到的电商数据(订单、用户、商品),思考它们之间的逻辑关系。
关系型数据库的核心定义
用电商场景说清楚"关系型数据库"
关系型数据库,简单说就是用"表格"来存储数据,并且表格之间可以建立关联的数据库。它和我们平时用的Excel很像,但更强大、更安全、更适合处理海量数据。
在电商系统里,你不会把所有数据塞进一张大表,而是拆分成多张小表:
-
订单表:存每一笔交易
-
用户表:存每一个用户的信息
-
商品表:存每一个商品的详情
然后通过"关系"(比如订单表中的user_id关联用户表的user_id)把这些表连接起来。这就是"关系型"名字的由来。
和非关系型数据库的核心区别
| 维度 | 关系型数据库 | 非关系型数据库 |
|---|---|---|
| 数据存储方式 | 二维表格(行+列) | JSON、键值对、文档等 |
| 数据一致性 | 强一致性(支持事务) | 最终一致性 |
| 适用场景 | 订单、用户、库存等需要精确一致的数据 | 日志、缓存、社交关系等 |
| 电商典型代表 | MySQL、PostgreSQL、Oracle | Redis、MongoDB、Elasticsearch |
在电商数据分析工作中的不可替代性
电商的核心业务数据(订单、用户、商品、库存)必须使用关系型数据库,原因有三:
-
数据一致性:下单扣库存必须同时成功或同时失败,否则会超卖。关系型数据库的事务机制能保证这一点。
-
复杂查询:分析"买了A商品的用户还买了什么",需要多表关联,这是关系型数据库的强项。
-
数据完整性:订单号不能重复、用户ID必须存在,关系型数据库的主键和外键约束能自动保证。
我的踩坑经历 :我入行时听说MongoDB很火,就用它来存订单数据。结果写复盘分析时,要统计"每个店铺每个月的GMV",MongoDB的聚合框架复杂得要命,而且经常算错。后来老老实实换回MySQL,用SQL几行就搞定了。电商数据分析,关系型数据库是首选,别折腾。
数据库核心概念详解
定义
数据库就是一个存放数据的"仓库"。在电商系统里,一个数据库可能包含:订单表、用户表、商品表、库存表等。
电商场景下的实际作用
-
隔离不同业务的数据:一个电商公司可能有"交易库""用户库""日志库"
-
统一管理权限:谁可以读哪个库、写哪个表
-
方便备份和恢复:整个库可以一键备份
真实案例
天猫的后台,订单数据存在trade_db数据库里,用户信息存在user_db数据库里。数据分析师通常只有trade_db的只读权限,不能修改数据。
SQL
-- 查看当前数据库
SELECT DATABASE();
-- 切换到订单库
USE trade_db;
-- 查看该库下所有表
SHOW TABLES;
实操避坑提醒 :不要在生产环境(正式库)里随便建自己的测试表。应该申请一个个人测试库,或者用CREATE TEMPORARY TABLE建临时表,用完自动删除。
表/数据表核心概念详解
定义
表是数据库中存储数据的基本单位。它由行和列组成,和Excel里的Sheet非常像。
电商场景下的实际作用
一张表只存一类业务实体。电商系统最常见的三张表:
-
订单表:记录每一笔交易
-
用户表:记录每一个注册用户
-
商品表:记录每一个上架商品
真实案例
SQL
-- 订单表结构示例
CREATE TABLE orders (
order_id VARCHAR(50),
user_id INT,
amount DECIMAL(10,2),
create_time DATETIME
);
SQL
-- 查看表结构
DESC orders;
字段/列核心概念详解
定义
字段(也叫列)是表中某一类数据的集合。每个字段有固定的数据类型(数字、文本、日期等)。
电商场景下的实际作用
字段定义了"这张表记录什么信息"。比如订单表必须包含:订单号、用户ID、金额、时间。
真实案例
| 字段名 | 数据类型 | 含义 |
|---|---|---|
| order_id | VARCHAR(50) | 订单号(文本) |
| user_id | INT | 用户ID(整数) |
| amount | DECIMAL(10,2) | 金额(小数,保留两位) |
| create_time | DATETIME | 下单时间(日期时间) |
SQL
-- 查询指定字段
SELECT order_id, amount FROM orders;
避坑提醒
-
字段类型选错会导致数据丢失。比如用
INT存金额,小数会被截断。应该用DECIMAL(10,2)。 -
字段命名不要用拼音首字母(如
ddh代表订单号),用清晰的英文或中文。
行/记录核心概念详解
定义
行(也叫记录)是表中的一条具体数据。一行就是一条完整的业务记录。
电商场景下的实际作用
订单表中的每一行,代表一笔订单的详细信息。
真实案例
SQL
-- 插入一行订单记录
INSERT INTO orders (order_id, user_id, amount, create_time)
VALUES ('ORD001', 1001, 299.00, '2025-01-01 10:23:45');
-- 查询该行
SELECT * FROM orders WHERE order_id = 'ORD001';
上述查询返回的一行数据就是一条记录。
主键核心概念详解
定义
主键 是表中用来唯一标识每一行的字段(或字段组合)。主键的值不能重复,也不能为空。
电商场景下的设置规则
-
订单表的主键通常是
order_id(订单号) -
用户表的主键通常是
user_id(用户ID) -
如果一张表没有自然唯一的字段,可以加一个自增ID作为主键(如
id)
实际作用
-
保证数据不重复:同一个订单号不会出现两次
-
快速查找:通过主键查数据最快
-
作为外键被其他表引用
真实案例
SQL
-- 带主键的订单表
CREATE TABLE orders (
order_id VARCHAR(50) PRIMARY KEY, -- 主键
amount DECIMAL(10,2)
);
-- 尝试插入重复主键会报错
INSERT INTO orders VALUES ('ORD001', 299.00); -- 成功
INSERT INTO orders VALUES ('ORD001', 399.00); -- 报错:Duplicate entry
避坑提醒
-
千万不要用"姓名"做主键。同名同姓的人会冲突,而且姓名可能会改。
-
不要用业务含义不稳定的字段做主键。比如手机号(用户可能换号)。
-
复合主键(多个字段组合)是允许的,但会增加复杂度。电商中常见的是订单行表(订单号+商品行号)。
SQL
-- 复合主键示例:订单明细表
CREATE TABLE order_items (
order_id VARCHAR(50),
line_no INT,
product_id INT,
quantity INT,
PRIMARY KEY (order_id, line_no)
);
我的踩坑经历 :我第一次设计表时,用user_phone做主键。后来一个用户换了手机号,我改不了主键,只能删了重插,关联的数据全乱了。从那以后,所有表都用无业务含义的自增ID做主键。
外键核心概念详解
定义
外键 是用来关联两张表的字段。它指向另一张表的主键。
使用规则
-
外键的值必须在被引用表的主键中存在(否则会报错)
-
外键可以为空(比如匿名订单没有用户ID)
实际作用
-
保证数据一致性:不会出现"订单的用户ID在用户表里不存在"
-
实现表关联:通过外键可以把订单表和用户表连接起来
电商场景下的真实案例
SQL
-- 用户表
CREATE TABLE users (
user_id INT PRIMARY KEY,
user_name VARCHAR(50)
);
-- 订单表,user_id是外键
CREATE TABLE orders (
order_id VARCHAR(50) PRIMARY KEY,
user_id INT,
amount DECIMAL(10,2),
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
-- 正常插入
INSERT INTO users VALUES (1001, '张三');
INSERT INTO orders VALUES ('ORD001', 1001, 299.00); -- 成功
-- 外键约束阻止插入不存在的user_id
INSERT INTO orders VALUES ('ORD002', 9999, 199.00); -- 报错:Cannot add foreign key constraint
SQL
-- 通过外键关联查询:查询订单对应的用户名
SELECT o.order_id, o.amount, u.user_name
FROM orders o
JOIN users u ON o.user_id = u.user_id;
避坑提醒
-
外键会降低写入性能(每次插入订单都要检查user_id是否存在)。在数据仓库或分析库中,有时会省略外键约束,但逻辑上仍然保持关联。
-
删除用户时,如果该用户有订单,会报错。需要先删除订单或设置
ON DELETE CASCADE。
SQL
-- 级联删除:删除用户时自动删除其订单
CREATE TABLE orders (
order_id VARCHAR(50) PRIMARY KEY,
user_id INT,
amount DECIMAL(10,2),
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
);
索引核心概念详解
定义
索引 是一种数据结构,用来加速查询。就像书的目录,让你不用翻遍整本书就能找到内容。
电商场景下的核心作用
-
加速
WHERE条件筛选(如WHERE user_id = 12345) -
加速
ORDER BY排序 -
加速
JOIN关联
适用场景
-
经常作为查询条件的字段:如
user_id、order_status -
经常需要排序的字段:如
create_time -
需要唯一约束的字段:如
order_id(主键自动是唯一索引)
大促订单数据查询索引优化真实案例
双11期间,订单表有几千万行。运营要查"用户12345的所有订单"。没有索引时,数据库要扫描全表几千万行,可能几十秒才能返回。加上idx_user_id索引后,直接定位到该用户的几行数据,毫秒级返回。
SQL
-- 没有索引时,查看查询计划(全表扫描)
EXPLAIN SELECT * FROM orders WHERE user_id = 12345;
-- type = ALL, rows = 几千万
-- 创建索引
CREATE INDEX idx_user_id ON orders(user_id);
-- 再次查看查询计划(使用索引)
EXPLAIN SELECT * FROM orders WHERE user_id = 12345;
-- type = ref, rows = 少量
SQL
-- 联合索引示例:经常按user_id和create_time同时查询
CREATE INDEX idx_user_time ON orders(user_id, create_time);
-- 该索引可以加速以下查询:
SELECT * FROM orders WHERE user_id = 12345 AND create_time > '2025-01-01';
-- 也能加速只按user_id的查询(最左前缀原则)
SELECT * FROM orders WHERE user_id = 12345;
-- 但不能加速只按create_time的查询
SELECT * FROM orders WHERE create_time > '2025-01-01'; -- 不会使用该索引
避坑提醒
-
索引不是越多越好。每个索引都会占用磁盘空间,并且会降低写入(INSERT、UPDATE、DELETE)的速度。
-
不要在低区分度的字段上建索引,比如
gender(只有男/女),索引几乎无效。 -
联合索引要遵循"最左前缀"原则。
(a,b)索引可以加速WHERE a=1,但不能加速WHERE b=1。
我的踩坑经历 :有一次我为了优化查询,在一个大表上建了7-8个索引,结果写入速度慢得离谱,每插入一条数据要等好几秒。后来删掉不必要的索引,只保留最常用的2个,写入速度恢复正常。索引是双刃剑,够用就好。
综合实操案例:服饰类目天猫店铺核心业务数据与关系型数据库概念的全匹配映射
案例背景
某服饰类目天猫店铺的核心业务数据包括:
-
订单:订单号、用户ID、商品ID、数量、金额、下单时间
-
用户:用户ID、注册时间、会员等级
-
商品:商品ID、商品名称、类目、价格
请将这些业务数据映射到关系型数据库的概念上:需要建几张表?每张表的主键是什么?表之间如何通过外键关联?哪些字段需要加索引?
分步操作
步骤1:确定需要建几张表
根据业务实体:订单、用户、商品 → 3张表。
步骤2:设计每张表的字段和主键
SQL
-- 用户表
CREATE TABLE users (
user_id INT PRIMARY KEY AUTO_INCREMENT,
register_time DATETIME,
vip_level INT
);
-- 商品表
CREATE TABLE products (
product_id INT PRIMARY KEY AUTO_INCREMENT,
product_name VARCHAR(100),
category VARCHAR(50),
price DECIMAL(10,2)
);
-- 订单表
CREATE TABLE orders (
order_id VARCHAR(50) PRIMARY KEY,
user_id INT,
product_id INT,
quantity INT,
amount DECIMAL(10,2),
create_time DATETIME
);
步骤3:确定外键关联
SQL
-- 添加外键约束
ALTER TABLE orders ADD FOREIGN KEY (user_id) REFERENCES users(user_id);
ALTER TABLE orders ADD FOREIGN KEY (product_id) REFERENCES products(product_id);
步骤4:确定需要加索引的字段
SQL
-- 经常按时间范围查询
CREATE INDEX idx_orders_create_time ON orders(create_time);
-- 经常查某个用户的订单
CREATE INDEX idx_orders_user_id ON orders(user_id);
-- 经常查某个商品的销售情况
CREATE INDEX idx_orders_product_id ON orders(product_id);
步骤5:验证表关系
SQL
-- 查询每个订单的用户名和商品名
SELECT
o.order_id,
u.user_name,
p.product_name,
o.amount,
o.create_time
FROM orders o
JOIN users u ON o.user_id = u.user_id
JOIN products p ON o.product_id = p.product_id
LIMIT 10;
案例小结
通过这个映射,你学会了如何把电商业务需求转化为关系型数据库的表结构设计。这是写SQL之前最重要的准备工作。
📌 电商数据合规提示:在设计用户表时,不要存储用户手机号、地址等敏感信息。如果确实需要,应该加密存储,并且设置严格的访问权限。外键关联的用户ID应使用内部脱敏ID,而不是手机号或身份证。
本章踩坑清单与合规总结
新手常见踩坑
| 错误 | 后果 | 正确做法 |
|---|---|---|
| 用姓名或手机号做主键 | 数据冲突或无法修改 | 用无业务含义的自增ID |
| 不建外键,靠程序保证一致性 | 数据可能不一致 | 逻辑上保持外键关联,分析库可不加物理外键 |
| 索引建太多 | 写入慢,占用空间 | 只给常用查询字段加索引 |
| 字段类型用错(金额用INT) | 小数丢失 | 金额用DECIMAL,日期用DATETIME |
SQL
-- 错误示例:金额用INT
CREATE TABLE orders (amount INT); -- 299.99会被存为299
-- 正确示例
CREATE TABLE orders (amount DECIMAL(10,2));
电商数据合规提示
-
主键设计:不要使用用户手机号、身份证号作为主键。这些属于个人敏感信息,且可能变更。
-
外键关联:在设计用户表时,用户ID应使用内部生成的随机ID,而不是手机号。外键关联的字段不能包含敏感信息。
-
索引和查询:即使你有索引,也不要查询全量用户数据。遵守最小必要原则,只查询分析必需的字段。
结语
关系型数据库的核心概念是SQL学习的"第一块砖"。搞懂数据库、表、字段、行、主键、外键、索引,你才能理解SQL语句为什么这么写、为什么有时候快有时候慢。
有问题的评论区留言,我看到会回复。