SQL 数据分析 · 指南 · 2026年10月30日 · 11 分钟阅读
SQL 和 Excel 的区别:数据分析该用哪个,以及怎么从 Excel 转到 SQL

Excel 和 SQL 与其说是竞争对手,不如说是同一项工作不同阶段的工具。SQL 更擅长取数、关联和汇总数据:它能处理几百万行,直接读取数据所在的地方,而且一条查询就是你具体做了什么的书面记录,明天可以原样再跑一遍。Excel 更擅长查看、调整和展示一份小结果:假设分析模型、快速出图、手工修正,以及一个公司里谁都能打开的文件。大多数数据分析师每天两样都用:用 SQL 产出一张几百行的表,再用 Excel 或 BI 工具处理它。
如果你已经会用 Excel,你懂的 SQL 比你以为的多。数据透视表就是 GROUP BY,VLOOKUP 就是 JOIN,筛选就是 WHERE。本文把你熟悉的 Excel 操作逐一对应到 SQL,查询都在 Nomto SQL 课程的航空公司数据库上运行(104 个机场、33,121 个航班、262,788 笔订单,金额单位为美元)。下面每张结果表都是真实输出。
简短的答案:什么时候用哪个
| 任务 | 更适合 | 原因 |
|---|---|---|
| 从公司数据库取数 | SQL | 数据就在那里;导出是一个会过时的手工步骤 |
| 超过大约一百万行 | SQL | 一张 Excel 工作表最多只能放 1,048,576 行 |
| 关联多张表 | SQL | 一个 JOIN 代替每张表一列查找公式 |
| 每周都要重做的报表 | SQL | 重新运行查询即可,不会漏掉哪一步复制粘贴 |
| 核查某个数字是怎么算出来的 | SQL | 查询本身就是完整的配方 |
| 假设分析和财务模型 | Excel | 改一个单元格,所有相关数字跟着更新 |
| 快速看一眼一个小文件 | Excel | 打开、排序、扫一眼,完事 |
| 手工修改、备注、标颜色 | Excel | SQL 不能在结果里直接打字 |
| 五分钟内做好开会用的图表 | Excel | 内置功能,人人都熟悉 |
| 分享给不写代码的人 | Excel | 人人都有,都能打开 |
一条实用的规则:如果工作是「查找、合并、计数」,从 SQL 开始。如果工作是「摆弄这张表再拿给别人看」,用 Excel 就行,而且往往更好。
为什么行数上限比看起来更重要
在这个课程数据库里,ticket_flights(每位乘客每个航段一行)有 1,045,726 行,刚好塞得进一张工作表。App 事件表则完全放不下:
SELECT COUNT(*) FROM app_events;
| count_star() |
|---|
| 1494723 |
而且塞得进不等于用得顺手:一个有一百万行查找公式和计算公式的工作簿,重新计算会很慢。在 SQL 里,表的大小几乎不影响你怎么写查询。
筛选 → WHERE
在 Excel 里你点筛选箭头、勾选值。在 SQL 里你写出条件。8 月份下单、金额 1,000 美元及以上的订单,以及它们的总金额:
SELECT COUNT(*) AS bookings, SUM(total_amount) AS revenue
FROM bookings
WHERE book_date >= '2026-08-01'
AND total_amount >= 1000;
| bookings | revenue |
|---|---|
| 16081 | 29341684.00 |
对应关系:
| Excel 筛选 | SQL |
|---|---|
| 勾选「Arrived」和「Departed」 | WHERE status IN ('Arrived', 'Departed') |
| 数字筛选:大于或等于 | WHERE total_amount >= 1000 |
| 文本筛选:开头是 | WHERE city LIKE 'Novo%' |
| 日期筛选:介于 | WHERE book_date >= '2026-08-01' AND book_date < '2026-09-01' |
| (空白) | WHERE actual_departure IS NULL |
有一个区别常让 Excel 用户栽跟头:在 SQL 里空单元格是 NULL,而 = NULL 永远匹配不到任何东西。要用 IS NULL。
排序和前 10 项 → ORDER BY 和 LIMIT
「降序排列,看前五名」写成:
SELECT book_ref, book_date, total_amount
FROM bookings
ORDER BY total_amount DESC
LIMIT 5;
| book_ref | book_date | total_amount |
|---|---|---|
| 3B54BB | 2026-07-01 17:08:00 | 12045.00 |
| 3AC131 | 2026-07-27 01:06:00 | 10871.00 |
| 65A6EA | 2026-06-29 06:28:00 | 10656.00 |
| D7E9AA | 2026-08-04 05:29:00 | 10628.00 |
| EF479E | 2026-07-29 15:58:00 | 10351.00 |
在 Excel 里,排序会改变工作表本身。在 SQL 里它只改变这一次的结果,表保持原样。
VLOOKUP / XLOOKUP → JOIN
在 flights 表里,机场和机型都只以代码出现。在 Excel 里你要加三列查找公式,每列指向另一张工作表。在 SQL 里,就是一个查询里的三个 JOIN:
SELECT f.flight_no,
f.scheduled_departure,
dep.city AS from_city,
arr.city AS to_city,
a.model
FROM flights f
JOIN airports dep ON dep.airport_code = f.departure_airport
JOIN airports arr ON arr.airport_code = f.arrival_airport
JOIN aircrafts a ON a.aircraft_code = f.aircraft_code
WHERE f.departure_airport = 'SVO'
AND f.scheduled_departure >= '2026-08-01'
AND f.scheduled_departure < '2026-08-02'
ORDER BY f.scheduled_departure, f.flight_no
LIMIT 4;
| flight_no | scheduled_departure | from_city | to_city | model |
|---|---|---|---|---|
| PG0120 | 2026-08-01 09:00:00 | Moscow | Mirny | Boeing 737-300 |
| PG0591 | 2026-08-01 09:00:00 | Moscow | Tomsk | Sukhoi Superjet-100 |
| PG0241 | 2026-08-01 09:40:00 | Moscow | Cheboksary | Sukhoi Superjet-100 |
| PG0677 | 2026-08-01 09:40:00 | Moscow | Makhachkala | Boeing 737-300 |
注意同一张 airports 表被 JOIN 了两次,航班两端各一次,用了不同的别名。在 Excel 里,这相当于对同一张工作表做两次独立的查找。
有两种行为和 VLOOKUP 不同,而且都很重要:
没有匹配。 VLOOKUP 显示 #N/A。内连接 JOIN 会悄悄丢掉这一行。如果想保留它并留空,用 LEFT JOIN。下面就是那个没有任何航班的机型,内连接会把它藏起来:
SELECT a.model
FROM aircrafts a
LEFT JOIN flights f ON f.aircraft_code = a.aircraft_code
WHERE f.flight_id IS NULL;
| model |
|---|
| Airbus A320-200 |
多个匹配。 VLOOKUP 返回第一个匹配,忽略其余的。JOIN 返回所有匹配,每匹配一次就重复一次这一行。一笔订单最多可以有五张机票:
SELECT b.book_ref, b.total_amount, COUNT(t.ticket_no) AS tickets
FROM bookings b
JOIN tickets t ON t.book_ref = b.book_ref
GROUP BY b.book_ref, b.total_amount
ORDER BY tickets DESC, b.book_ref
LIMIT 3;
| book_ref | total_amount | tickets |
|---|---|---|
| 070133 | 1133.00 | 5 |
| 070682 | 8012.00 | 5 |
| 111C34 | 2120.00 | 5 |
所以,如果你把订单和机票 JOIN 起来再对 total_amount 求和,每笔订单会按机票张数被重复计算:
SELECT (SELECT SUM(total_amount) FROM bookings) AS correct,
SUM(b.total_amount) AS after_join
FROM bookings b
JOIN tickets t ON t.book_ref = b.book_ref;
| correct | after_join |
|---|---|
| 207669809.00 | 343206192.00 |
这是从 Excel 转过来的人最常犯的 SQL 错误,因为 VLOOKUP 从不会让行数翻倍。JOIN 之前先问自己:左边的一行,在右边最多能匹配到几行?SQL JOIN 用法详解 对此有详细讲解。
数据透视表 → GROUP BY
「行」区域 = GROUP BY 的列;「值」区域 = 聚合函数。按舱位统计收入,以及每个舱位占总收入的比例(在透视表里就是「值显示方式:总计的百分比」):
SELECT fare_conditions,
COUNT(*) AS segments,
SUM(amount) AS revenue,
ROUND(100.0 * SUM(amount) / SUM(SUM(amount)) OVER (), 1) AS pct_of_revenue
FROM ticket_flights
GROUP BY fare_conditions
ORDER BY revenue DESC;
| fare_conditions | segments | revenue | pct_of_revenue |
|---|---|---|---|
| Economy | 920793 | 146956844.00 | 70.8 |
| Business | 107642 | 55051796.00 | 26.5 |
| Comfort | 17291 | 5661169.00 | 2.7 |
这个查询扫描了一百多万行。SUM(SUM(amount)) OVER () 是一个窗口函数,它把总计放在每个分组旁边;原理见 SQL 窗口函数详解。
带「列」区域的透视表(状态往下排、月份往右排),就是每一列写一个条件计数:
SELECT status,
COUNT(*) FILTER (WHERE EXTRACT(MONTH FROM scheduled_departure) = 7) AS jul,
COUNT(*) FILTER (WHERE EXTRACT(MONTH FROM scheduled_departure) = 8) AS aug,
COUNT(*) FILTER (WHERE EXTRACT(MONTH FROM scheduled_departure) = 9) AS sep,
COUNT(*) AS total
FROM flights
GROUP BY status
ORDER BY total DESC;
| status | jul | aug | sep | total |
|---|---|---|---|---|
| Arrived | 10821 | 5886 | 0 | 16707 |
| Scheduled | 0 | 10345 | 5038 | 15383 |
| On Time | 0 | 518 | 0 | 518 |
| Cancelled | 5 | 6 | 403 | 414 |
| Departed | 0 | 58 | 0 | 58 |
| Delayed | 0 | 41 | 0 | 41 |
说实话,这里 Excel 更方便:透视表会自动生成各列,而在 SQL 里你得一列列写出来。很多分析师正是这样分工的:SQL 产出长表(状态、月份、计数),再由 Excel 或 BI 工具里的透视表把它展开成列。
透视表的「筛选」区域就是 WHERE(在分组之前),而「只显示合计超过 N 的组」就是 HAVING。见 GROUP BY 和 HAVING 的区别。
公式 → 表达式
| Excel | SQL |
|---|---|
=A2*B2、=ROUND(A2, 2) |
在 SELECT 里写 price * qty、ROUND(amount, 2) |
=IF(A2>=1000, "large", "small") |
CASE WHEN total_amount >= 1000 THEN 'large' ELSE 'small' END |
=IFERROR(A2/B2, "") |
a / NULLIF(b, 0) |
=COUNTIF(range, "Cancelled") |
COUNT(*) FILTER (WHERE status = 'Cancelled') |
=SUMIFS(amount, class, "Business") |
SUM(amount) FILTER (WHERE fare_conditions = 'Business') |
=A2 & " " & B2 |
a || ' ' || b |
=LEFT(A2, 3)、=UPPER(A2)、=TRIM(A2) |
LEFT(s, 3)、UPPER(s)、TRIM(s) |
=EOMONTH、=YEAR、日期相减 =A2-B2 |
DATE_TRUNC、EXTRACT(YEAR FROM d)、d1 - d2 |
| 删除重复项 | SELECT DISTINCT / COUNT(DISTINCT col) |
累计求和列 =C1+B2 |
SUM(x) OVER (ORDER BY day) |
思维上最大的转变是:Excel 公式写在一个单元格里,然后往下拖。SQL 表达式只写一次,就作用于每一行。嵌套的 IF 变成一个 CASE,从上往下读:
SELECT book_ref, total_amount,
CASE WHEN total_amount >= 1000 THEN 'large'
WHEN total_amount >= 300 THEN 'medium'
ELSE 'small'
END AS size
FROM bookings
ORDER BY book_ref
LIMIT 4;
| book_ref | total_amount | size |
|---|---|---|
| 00000F | 2657.00 | large |
| 000012 | 379.00 | medium |
| 000068 | 181.00 | small |
| 000181 | 1318.00 | large |
而你会在旁边加一列、再按它做透视的那一步,在 SQL 里只是多一个 GROUP BY:
SELECT CASE WHEN total_amount >= 1000 THEN 'large'
WHEN total_amount >= 300 THEN 'medium'
ELSE 'small'
END AS size,
COUNT(*) AS bookings
FROM bookings
GROUP BY size
ORDER BY bookings DESC;
| size | bookings |
|---|---|
| medium | 130009 |
| small | 68305 |
| large | 64474 |
删除重复项,比如有机场的城市一共有多少个:
SELECT COUNT(*) AS airports, COUNT(DISTINCT city) AS cities
FROM airports;
| airports | cities |
|---|---|
| 104 | 101 |
Excel 真正占优的地方
这一点值得说实话,因为「SQL 更好」并不是在所有事情上都成立:
- 建模。 预算、预测或定价方案,改一个假设就能看到其余数字跟着变,这正是电子表格的设计初衷。SQL 没有可以改的单元格。
- 小而杂乱的一次性数据。 别人用邮件发来的一份名单,带合并单元格和页边备注,手工清理比导入数据库快。
- 手工录入。 加一条备注、改一个值、给要讨论的行标上颜色。
- 沟通。 你的经理能打开一个工作簿,但大多数经理打不开一条查询。
- Power Query 和数据模型。 新版 Excel 能加载和关联超出一张工作表容量的数据。如果你们公司离不开 Excel,这些功能能弥补一部分差距,只是其中的逻辑仍然比一条查询更难审查。
SQL 给你的回报是可复现性。一张经过二十步手工操作的表格很难核查或重建;而一条查询可以逐行阅读,在下个月的数据上重新运行,也能让同事审查。
怎么转:一条实用路径
- 学会理解「一行」。 每次写查询之前,先弄清每张表的一行代表什么(一个航班、一笔订单、一个机票航段)。这取代了上下滚动工作表的习惯。
- 从你在 Excel 里本来就做的事开始。 筛选 →
WHERE,排序 →ORDER BY,透视 →GROUP BY。把你每周真实要做的一份报表用 SQL 重做一遍。 - 然后是 JOIN。 替换掉你的 VLOOKUP,每次 JOIN 前后都检查行数。
- 最后一步留给 Excel。 把很小的最终结果导出到工作表里做图表和备注。这不是偷懒,大多数分析师就是这么工作的。
- 基础用熟之后,再加上
CASE、CTE 和窗口函数。 SQL 速查表 把它们都放在了一页上。
在 Nomto 的 SQL 练习场里,你可以直接在浏览器里对航空公司数据库运行这些查询,什么都不用安装,课程也正是从这些类似 Excel 的操作开始的:选列、筛选、排序和分组。SQL 基础查询语句 是很好的入门读物。
常见问题
SQL 比 Excel 难学吗?
基础部分不难。如果你会做数据透视表、会用 VLOOKUP,几周之内就能学会 SELECT、WHERE、GROUP BY 和 JOIN。需要练习的是用整张表而不是单个单元格来思考,以及察觉 JOIN 什么时候让行数翻倍。
SQL 能完全取代 Excel 吗?
取数和汇总,基本可以。建模、手工修改和快速展示,不能。大多数分析师用 SQL 产出一份小结果,再用电子表格或 BI 工具来处理它。
数据分析师还用 Excel 吗?
用,而且很普遍,尤其是分析的最后一步和分享结果的时候。SQL 负责取数据;大家最终看到的,往往是 Excel、Google Sheets 或 BI 工具。
VLOOKUP 在 SQL 里对应什么?
对查找列做 JOIN:FROM flights f JOIN aircrafts a ON a.aircraft_code = f.aircraft_code。如果想像 VLOOKUP 那样保留没有匹配的行(VLOOKUP 会保留这一行并显示 #N/A),用 LEFT JOIN。
其他语言: English, Español, Português (Brasil), Tiếng Việt