跳到主要内容

SQL 基础知识体系概览

什么是 SQL?

SQL(Structured Query Language,结构化查询语言) 是与关系型数据库"对话"的标准语言。对于数据分析师来说,SQL 是最重要的技能之一——它是你从海量数据中提取洞察的第一步工具。

你可以把 SQL 想象成一种"向数据库提问的语言":

  • "上个月销售额最高的商品是什么?"→ 一条 SELECT 语句
  • "最近 30 天新注册了多少用户?"→ 一条 COUNT 语句
  • "每个城市的平均客单价是多少?"→ 一条 GROUP BY 语句
小知识

SQL 诞生于 1970 年代 IBM 的研究实验室,最初叫 SEQUEL。如今它已成为 ISO 国际标准,几乎所有主流数据库(MySQL、PostgreSQL、Oracle、SQL Server、Hive、Spark SQL)都支持它,语法大同小异。


为什么数据分析师必须精通 SQL?

原因说明
日常工具数据分析工作中 60%~80% 的时间在写 SQL
面试必考几乎所有数据分析岗都会考 SQL 手写题
通用性强MySQL、PostgreSQL、Hive、Spark SQL 语法大同小异
效率极高用 SQL 处理数据比 Python/Excel 快得多,几行代码处理百万行数据
职场通用语和数据工程师、产品经理、业务方沟通,SQL 是最通用的语言

面试中 SQL 题目通常涉及:

学习路线

SQL 基础(本章)→ SQL 分析进阶(窗口函数、CTE)→ 具体数据库特性


数据库与表的基本概念

在学习 SQL 之前,先理解几个核心概念:数据库、表、行、列

概念层次关系

具体示例:电商数据库

假设我们有一个电商平台的数据库,其中有一张用户表users):

user_idnameagecityregister_date
1张三28北京2023-01-15
2李四35上海2023-02-20
3王五22广州2023-03-10
4赵六31北京2023-04-05

在这张表中:

  • 数据库(Database):整个电商系统的数据仓库,包含 usersordersproducts 等多张表
  • 表(Table)users 就是一张表,专门存储用户信息
  • 行(Row)/ 记录(Record):每一行代表一个用户,如第一行是"张三"的所有信息
  • 列(Column)/ 字段(Field):每一列代表一种属性,如 city 列存储所有用户的城市信息
  • 主键(Primary Key)user_id 是主键,唯一标识每一行,不能重复、不能为空
重要概念:NULL

NULL 表示"缺失值"或"未知值",不等于空字符串 '',也不等于 0。在 SQL 中判断 NULL 必须用 IS NULLIS NOT NULL,而不能用 = NULL


SQL 语言分类(DDL / DML / DQL / DCL)

SQL 语句按功能分为四大类,面试中有时会被问到这些概念:

类别全称中文代表命令说明
DDLData Definition Language数据定义语言CREATEDROPALTER定义数据库结构(建表、删表、改字段)
DMLData Manipulation Language数据操作语言INSERTUPDATEDELETE操作表中的数据(增、改、删)
DQLData Query Language数据查询语言SELECT查询数据(数据分析师用得最多)
DCLData Control Language数据控制语言GRANTREVOKE权限管理(通常由 DBA 负责)
对于数据分析师

DQL(SELECT) 是工作中用得最多的,占 90% 以上。DMLINSERT/UPDATE/DELETE 偶尔用到。DDL 和 DCL 通常由数据工程师或 DBA 负责,了解即可。

各类别代表示例

-- DDL:创建一张用户表
CREATE TABLE users (
user_id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
age INT,
city VARCHAR(50),
register_date DATE
);

-- DML:插入一条用户记录
INSERT INTO users (name, age, city, register_date)
VALUES ('张三', 28, '北京', '2023-01-15');

-- DQL:查询北京用户
SELECT name, age
FROM users
WHERE city = '北京'
ORDER BY age DESC;

-- DML:更新用户信息
UPDATE users SET city = '深圳' WHERE user_id = 1;

-- DML:删除用户
DELETE FROM users WHERE user_id = 1;

SQL 核心执行模型

很多初学者觉得 SQL "写起来顺序和执行顺序不一样,很迷惑"。掌握 SQL 的逻辑执行顺序是写出正确查询的关键。

SQL 的书写顺序 vs 执行顺序

为什么执行顺序重要?

这个顺序解释了很多常见的"为什么报错"问题:

-- ❌ 错误:WHERE 中不能用 SELECT 里定义的别名
-- 因为 WHERE 比 SELECT 先执行,别名还不存在
SELECT price * quantity AS total_amount
FROM orders
WHERE total_amount > 1000; -- 报错!total_amount 此时尚未定义

-- ✅ 正确:改用 HAVING,或者重写表达式
SELECT price * quantity AS total_amount
FROM orders
HAVING total_amount > 1000; -- HAVING 在 SELECT 之后执行,可以用别名

-- ✅ 或者直接用原始表达式
SELECT price * quantity AS total_amount
FROM orders
WHERE price * quantity > 1000; -- WHERE 重写原始表达式
WHERE vs HAVING 的本质区别
  • WHERE:在分组之前过滤,不能使用聚合函数
  • HAVING:在分组之后过滤,可以使用聚合函数(如 COUNT(*) > 5

一个完整查询的执行流程

以"统计每个城市下单金额超过 10000 元的用户数"为例:

SELECT
city,
COUNT(DISTINCT user_id) AS user_count -- 5. 计算每组的用户数
FROM orders
WHERE order_date >= '2024-01-01' -- 2. 先过滤出今年的订单
GROUP BY city -- 3. 按城市分组
HAVING SUM(amount) > 10000 -- 4. 过滤掉总金额不足的城市
ORDER BY user_count DESC -- 6. 按用户数降序排列
LIMIT 10; -- 7. 只取前 10 个城市

初学者常犯的 5 个错误

避坑指南

错误 1:混淆 =IS NULL

-- ❌ 错误:找不到任何结果,NULL 不能用 = 比较
SELECT * FROM users WHERE city = NULL;

-- ✅ 正确
SELECT * FROM users WHERE city IS NULL;

错误 2:GROUP BY 遗漏列

-- ❌ 错误:SELECT 中出现了没有被聚合、也没有在 GROUP BY 中的列
SELECT city, name, COUNT(*) AS cnt -- name 没有在 GROUP BY 里!
FROM users
GROUP BY city;

-- ✅ 正确:要么加入 GROUP BY,要么用聚合函数
SELECT city, COUNT(*) AS cnt
FROM users
GROUP BY city;

错误 3:在 WHERE 中使用聚合函数

-- ❌ 错误:WHERE 不能用聚合函数(执行顺序问题)
SELECT city, COUNT(*) AS cnt
FROM users
WHERE COUNT(*) > 5 -- 报错!
GROUP BY city;

-- ✅ 正确:改用 HAVING
SELECT city, COUNT(*) AS cnt
FROM users
GROUP BY city
HAVING COUNT(*) > 5;

错误 4:忽视 NULL 对聚合函数的影响

-- 假设 age 列有 NULL 值
-- COUNT(*) 统计所有行(包含 NULL)
-- COUNT(age) 只统计 age 非 NULL 的行
-- AVG(age) 只计算非 NULL 的平均值

SELECT
COUNT(*) AS total_rows, -- 所有行数(含 NULL)
COUNT(age) AS non_null_age, -- age 非 NULL 的行数
AVG(age) AS avg_age -- 只对非 NULL 求平均
FROM users;

错误 5:过早使用 DISTINCT,忽视性能

-- ⚠️ 危险:数据量大时 DISTINCT 很耗性能,先想清楚为什么会重复
SELECT DISTINCT user_id FROM orders;

-- ✅ 更好:先理解重复的原因,可能是 JOIN 引起的,从根源解决
-- 而不是盲目加 DISTINCT 掩盖问题

核心知识导航

主题说明面试重要度
SELECT 查询基础WHERE、ORDER BY、LIMIT、DISTINCT——数据查询的基本功⭐⭐⭐⭐⭐
JOIN 连接查询多表关联查询——面试最高频考点之一⭐⭐⭐⭐⭐
聚合函数与 GROUP BYCOUNT、SUM、AVG + 分组统计——数据分析的核心⭐⭐⭐⭐⭐
子查询嵌套查询、EXISTS、IN——解决复杂问题的利器⭐⭐⭐⭐
数据操作 DMLINSERT、UPDATE、DELETE——数据的增删改⭐⭐⭐
SQL 常用函数字符串函数、日期函数、数值函数、类型转换⭐⭐⭐

学习路径与时间建议

根据你的目标,推荐以下学习路径:

路径一:快速入门(求职导向,2~3 周)

路径二:系统学习(全面掌握,6~8 周)

阶段时间内容目标
第一阶段1~2 周SELECT / WHERE / JOIN / GROUP BY能独立写出基础分析查询
第二阶段1~2 周子查询 / 常用函数 / DML能处理中等复杂度的业务需求
第三阶段2~3 周窗口函数 / CTE / 行列转换能完成留存、漏斗、RFM 等分析
第四阶段1~2 周性能优化 / 特定数据库特性写出高效 SQL,理解执行计划
建议

边学边练是学 SQL 最快的方式。推荐同步刷 LeetCode SQL,每学一个知识点就找对应的练习题。50 道免费题刷完,基础 SQL 面试基本无忧。

已有编程基础的人如何快速上手?

如果你已经掌握 Python 或其他编程语言,SQL 的核心思维转换如下:

SQL 操作对应的 Python Pandas 操作
SELECT col FROM tdf['col']
WHERE conditiondf[df['col'] > x]
JOINpd.merge(df1, df2, on='key')
GROUP BY + COUNTdf.groupby('col').count()
ORDER BYdf.sort_values('col')
LIMIT 10df.head(10)

SQL 方言说明

不同数据库的 SQL 语法略有差异(称为"方言")。本文档以 MySQLPostgreSQL 为主,同时标注差异:

功能MySQLPostgreSQLHive/Spark SQL
字符串连接CONCAT()||CONCAT()CONCAT()
限制行数LIMIT nLIMIT nLIMIT n
当前时间NOW()NOW()current_timestamp()
自增主键AUTO_INCREMENTSERIAL不支持
类型转换CAST()CAST()::CAST()
字符串截取SUBSTRING()SUBSTRING()SUBSTR()SUBSTR()
日期格式化DATE_FORMAT()TO_CHAR()DATE_FORMAT()
条件表达式IF(cond, a, b)不支持 IF(),用 CASE WHENIF()
建议

面试中如果不确定用哪种方言,可以直接问面试官:"请问是用 MySQL 还是 PostgreSQL 的语法?"这不丢分,反而体现你对方言差异有认知。


一个完整的分析案例

在正式开始学习各章节之前,先看一个完整的 SQL 分析查询,感受 SQL 的表达能力:

业务问题:找出 2024 年每个城市下单金额 Top 3 的用户,并显示他们的总下单金额和下单次数。

-- 使用窗口函数(进阶内容,先感受一下 SQL 的表达能力)
WITH user_stats AS (
-- 第一步:按城市和用户统计下单信息
SELECT
u.city,
u.name,
COUNT(o.order_id) AS order_count,
SUM(o.amount) AS total_amount
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE o.order_date >= '2024-01-01'
AND o.order_date < '2025-01-01'
GROUP BY u.city, u.name
),
ranked AS (
-- 第二步:在每个城市内按金额排名
SELECT
city,
name,
order_count,
total_amount,
ROW_NUMBER() OVER (
PARTITION BY city
ORDER BY total_amount DESC
) AS rank_in_city
FROM user_stats
)
-- 第三步:只取每个城市 Top 3
SELECT city, name, order_count, total_amount
FROM ranked
WHERE rank_in_city <= 3
ORDER BY city, rank_in_city;

看不懂也没关系,学完本章节和 SQL 分析进阶 后,这段代码就会变得非常清晰。


相关链接