每隔几年,编程社区就会冒出一个"该学什么语言"的新答案——Go、Rust、Elixir、Haskell,轮番登场。开发者像追潮一样不断重新投资学习成本,生怕错过下一个风口。Fagner Brack 最近抛出一个反直觉的判断:SQL 才是少有的、学一次就能用三十年的语言。
这不是怀旧,而是基于一个硬事实——SQL 的根基是关系代数和关系演算,不是某家公司的产品路线图,也不是某个社区的流行投票。数学不会过时,潮流会。
为什么新语言总在换,SQL 却不动
新语言的出现往往是为了解决当时某个具体痛点:Go 解决并发部署,Rust 解决内存安全,Elixir 解决高容错分布式。这些痛点真实存在,但它们的"最佳答案"会随硬件、架构、生态的变化而变化。十年前用 Erlang 写高并发是前沿,今天用 Rust 写同样场景可能更合理。
SQL 不一样。它面对的问题——"如何从结构化数据中精确提取你想要的子集"——是一个数学问题。关系代数给出了完备的理论框架:选择(σ)、投影(π)、连接(⋈)、聚合……这些操作的组合能力不会因为云原生或 AI 而失效。1970 年 Codd 提出关系模型,半个世纪后你写的 SELECT ... JOIN ... GROUP BY 仍然直接对应那些原始算子。
这意味着:你投入在 SQL 上的理解力,不会因为某家数据库厂商倒闭、某个新框架崛起而归零。
SQL 的"不时髦"恰恰是它的护城河
很多人觉得 SQL 不酷——没有包管理器,没有类型系统体操,没有异步运行时。但"不酷"的背后是极低的迁移成本:
- 语法稳定:ANSI SQL 标准从 1986 年至今只出了几版修订,核心语法几乎没变。你 2005 年写的查询,2025 年在 PostgreSQL、MySQL、SQLite 里还能跑。
- 实现广泛:从嵌入式 SQLite 到分布式 ClickHouse,从云上的 Redshift 到本地的 DuckDB,SQL 是唯一横跨所有数据规模和部署形态的接口。
- 思维模型统一:无论底层是 B-tree 列存还是向量引擎,你面对的都是"表-行-列"这个模型。学会一次,换引擎不需要重新学查询逻辑。
对比一下:你从 Python 2 迁移到 Python 3 花了多少年?从 React class 组件迁到 hooks 花了多少精力?SQL 几乎不存在这类迁移灾难。
实战:用 SQL 替代你正在用 Python 写的数据处理逻辑
很多开发者习惯用 pandas 或原生 Python 循环处理数据,觉得"SQL 只能做简单查询"。这是一个误解。下面用一组具体例子展示 SQL 能覆盖的范围。
假设你有一张订单表 orders,结构如下:
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
product TEXT NOT NULL,
amount REAL NOT NULL,
created DATE NOT NULL
);
场景 1:每月复购用户数——找出连续两个月都有下单的用户。
Python 帚法通常要排序、分组、逐月比对,代码量不小。SQL 用窗口函数一行搞定:
WITH monthly AS (
SELECT
user_id,
DATE_TRUNC('month', created) AS month,
COUNT(*) AS order_cnt
FROM orders
GROUP BY user_id, DATE_TRUNC('month', created)
),
lagged AS (
SELECT
user_id,
month,
order_cnt,
LAG(month) OVER (PARTITION BY user_id ORDER BY month) AS prev_month
FROM monthly
)
SELECT
month,
COUNT(DISTINCT user_id) AS retained_users
FROM lagged
WHERE prev_month = month - INTERVAL '1 month'
GROUP BY month
ORDER BY month;
上面用 PostgreSQL 语法。SQLite 3.35+ 和 DuckDB 也支持窗口函数,只需把
DATE_TRUNC改为strftime('%Y-%m', created),INTERVAL改为日期计算函数即可。
场景 2:RFM 用户分层——按最近购买时间(Recency)、购买频次(Frequency)、消费总额(Monetary)给用户打分。
WITH rfm_raw AS (
SELECT
user_id,
MAX(created) AS last_order,
COUNT(*) AS frequency,
SUM(amount) AS monetary,
NTILE(4) OVER (ORDER BY MAX(created) DESC) AS r_score,
NTILE(4) OVER (ORDER BY COUNT(*) DESC) AS f_score,
NTILE(4) OVER (ORDER BY SUM(amount) DESC) AS m_score
FROM orders
GROUP BY user_id
)
SELECT
user_id,
r_score * 100 + f_score * 10 + m_score AS rfm_code,
CASE
WHEN r_score = 1 AND f_score = 1 AND m_score = 1 THEN '核心用户'
WHEN r_score <= 2 AND f_score >= 3 THEN '活跃潜力'
WHEN r_score >= 3 THEN '流失风险'
ELSE '普通用户'
END AS segment
FROM rfm_raw
ORDER BY rfm_code DESC;
NTILE(4)把用户均分成 4 档,1 档最好、4 档最差。RFM 编码111就是最高价值用户。这段查询在 DuckDB、PostgreSQL、MySQL 8+ 都能直接运行。
场景 3:用 DuckDB 在本地直接查 CSV 文件——不需要装数据库服务器,不需要导入数据。
# 安装 DuckDB CLI(macOS/Linux)
curl -L https://duckdb.org/releases/latest/duckdb_cli-linux-amd64.zip -o duckdb.zip
unzip duckdb.zip && chmod +x duckdb && mv duckdb /usr/local/bin/
-- 直接查询 CSV,零导入
SELECT
strftime('%Y-%m', created) AS month,
product,
SUM(amount) AS revenue,
COUNT(*) AS cnt
FROM read_csv_auto('orders.csv')
GROUP BY month, product
ORDER BY revenue DESC
LIMIT 10;
read_csv_auto自动推断列类型和分隔符。DuckDB 是进程内引擎,启动耗时 < 50ms,查百万行 CSV 秒级返回。你完全可以把它当成"本地版 pandas,但用 SQL 写逻辑"。
学 SQL 的投资回报率怎么算
把学习成本摊到三十年来看:
| 语言 | 首次学习成本 | 五年后仍核心使用概率 | 三十年累计 ROI |
|---|---|---|---|
| SQL | 中(2-4 周掌握核心) | 极高(几乎所有数据场景) | 极高 |
| Go | 中 | 中(取决于云原生生态走向) | 中 |
| Rust | 高 | 中高(系统编程需求稳定但岗位少) | 中 |
| 热门框架 | 低-中 | 低(3-5 年大概率换一代) | 低 |
SQL 的 ROI 高不是因为它"强大",而是因为它解决的问题域不会消失,它的表达方式不会突变。你今天学会的 JOIN 语义,三十年后仍然是你理解分布式查询引擎的基础。
入手建议与边界认知
如果你还没系统学过 SQL,建议这样起步:
- 用 DuckDB 或 SQLite 作为练习环境,零运维成本,打开即用。
- 先掌握:
SELECT / WHERE / GROUP BY / HAVING / JOIN / 子查询 / 窗口函数。这七项覆盖 90% 的日常需求。 - 刻意练习"用 SQL 替代 Python 循环"的思维——遇到数据分组、排序、聚合,先想"能不能一条 SQL 解决",再决定是否写脚本。
也要认清 SQL 的边界:
- SQL 不擅长复杂的过程控制(递归、异常处理、状态机),这些场景用 Python/Go 更合理。
- SQL 的可移植性有细微差异——
DATE_TRUNC、NTILE、JSON函数在各引擎间语法不完全一致,写跨引擎查询时需要查兼容表。 - SQL 不是万能胶——它只管数据查询和变换,不管网络、UI、并发调度。
一句话总结: 把 SQL 当成你技术栈里的"数据层母语",而不是一个附属工具。它不需要追版本、不需要换框架、不需要重新投资——三十年后你还在用它,而当年追的那些潮流语言,大概率已经换了三代。