SQL 新手入门:从第一条查询到避坑指南
如果你刚接触后端开发、数据分析,或者正在准备找一份与数据打交道的工作,SQL 几乎是绕不开的第一课。它不需要搭建复杂的开发环境,也不像算法那样抽象难懂——SQL 的核心就是一句话:用接近自然语言的语句,向数据库要你想要的数据。本文从零开始,带你写出第一条查询,再把新手最容易踩的坑一个个排掉。
一、SQL 是什么,学了能干什么
SQL 是 Structured Query Language(结构化查询语言)的缩写,是操作关系型数据库的标准语言。我们平时用的网站、App、小程序,背后几乎都有一个关系型数据库在存数据:用户信息存在一张表里,订单存在另一张表里,商品、评论、物流记录各有各的表。SQL 就是你和这些表对话的语言。
学会 SQL 能做什么?查一下今天有多少新用户、统计本月销售额、找出库存不足的商品、给某个用户改一下手机号——这些日常需求,一条 SQL 就能解决。对后端开发来说,增删改查是基本功;对数据分析来说,SQL 是取数的第一工具;对测试和运维来说,会写 SQL 意味着能快速定位问题。
关系型数据库有很多种:MySQL、PostgreSQL、SQLite、SQL Server、Oracle 等。它们都支持标准 SQL,语法大同小异。初学者完全可以先从任何一种入手,学会一家,再学另一家只是换几个函数名和细节。本文示例以通用语法为主,个别地方会注明差异。
二、准备工作:先装一个数据库
新手推荐两种入门路线。
第一种,安装 SQLite。它只有一个文件,不需要装服务、不需要配账号密码,很多编程语言自带支持。下载一个图形工具 DB Browser for SQLite,就能可视化地建表、查数据,非常适合练手。
第二种,安装 MySQL 或 PostgreSQL。两者都是主流的开源数据库,网上教程最多。如果你打算走后端开发路线,装 MySQL 是更常见的选择;如果偏向数据分析,PostgreSQL 更受欢迎。安装时按默认配置一路下一步即可,记得记好自己设置的 root 密码。
不管选哪一种,练手用的数据都不要拿生产库开玩笑。在自己的电脑上随便折腾,删库了也能重建,成本为零。
三、先认识表:行、列与主键
关系型数据库里,数据都存在“表”里。一张表由若干行和若干列组成:每一列是一个字段(比如姓名、年龄、城市),每一行是一条记录(比如某一个学生的全部信息)。
我们先建一张学生表练手:
CREATE TABLE students (
id INT PRIMARY KEY,
name VARCHAR(50),
age INT,
city VARCHAR(50),
score INT
);
这条语句的意思是:创建一张名叫 students 的表,包含五列。id 是整数类型,PRIMARY KEY 表示主键——每行数据的唯一标识,不可重复、不能为空。name 和 city 是最长 50 个字符的文本,age 和 score 是整数。
主键是新手必须理解的概念。没有主键的表,就像没有学号的学生名单,改数据的时候都不知道改的是哪一行。实际开发中,主键通常用自增整数或 UUID,不要用姓名、手机号这种可能重复或变化的业务字段做主键。
四、增删改查:SQL 的四件套
建好表之后,就可以往里放数据了。SQL 最常用的四类操作是增删改查,对应四个关键字:INSERT、SELECT、UPDATE、DELETE。
先插入三条数据:
INSERT INTO students (id, name, age, city, score) VALUES
(1, '小明', 20, '北京', 88),
(2, '小红', 21, '上海', 92),
(3, '小刚', 19, '北京', 76);
字符串要用单引号括起来,数字不需要。一次插入多行可以写在一个 INSERT 里,效率更高。
查询是最常用的操作。把整张表查出来:
SELECT * FROM students;
星号表示所有列。只查姓名和分数两列:
SELECT name, score FROM students;
改数据用 UPDATE,删数据用 DELETE:
UPDATE students SET score = 90 WHERE id = 1;
DELETE FROM students WHERE id = 3;
这两条语句里的 WHERE 是条件:只改、只删满足条件的行。如果漏写 WHERE,UPDATE 会改掉整张表,DELETE 会清空整张表——这是新手最容易酿成大祸的地方,后面避坑部分还会专门讲。
五、WHERE:把数据筛出来
真实的数据表动不动就有几十万行,不可能每次都把整张表拖出来看。WHERE 就是筛子,只留下符合条件的行。
-- 分数大于等于 85 的学生
SELECT name, score FROM students WHERE score >= 85;
-- 北京的学生
SELECT * FROM students WHERE city = '北京';
-- 分数在 80 到 90 之间
SELECT * FROM students WHERE score BETWEEN 80 AND 90;
-- 名字里带“小”的学生
SELECT * FROM students WHERE name LIKE '小%';
常用的比较运算符有 =、!=(或 <>)、>、<、>=、<=,逻辑组合用 AND、OR、NOT。% 是 LIKE 的通配符,代表任意多个字符。如果要查 NULL,不能用 = NULL,要用 IS NULL,因为 NULL 在 SQL 里代表“未知”,任何值和未知比较的结果都是未知。
字符串比较是否区分大小写,取决于数据库的字符集设置。写条件的时候,涉及大小写要特别留意。
六、排序与分页:ORDER BY 和 LIMIT
查出来的数据默认没有固定顺序,跟你想要的顺序可能完全不一样。想按分数从高到低排:
SELECT name, score FROM students ORDER BY score DESC;
DESC 是降序,ASC 是升序(默认)。也可以按多个字段排序:先按城市升序,城市相同再按分数降序。
SELECT * FROM students ORDER BY city ASC, score DESC;
如果表里有一万行,你一般只想看前十名。用 LIMIT 限制返回的行数:
SELECT * FROM students ORDER BY score DESC LIMIT 10;
做分页查询时,LIMIT 常和 OFFSET 搭配:第 1 页 LIMIT 10 OFFSET 0,第 2 页 LIMIT 10 OFFSET 10,以此类推。记住一个原则:没有 ORDER BY 的分页是没有意义的,因为数据库不保证默认顺序,每次翻页可能看到重复或跳号的数据。
七、聚合:让数据库帮你算数
很多时候我们要的不是明细,而是一个总数、平均值或最大值。聚合函数就是干这个的:
-- 一共多少学生
SELECT COUNT(*) FROM students;
-- 平均分
SELECT AVG(score) FROM students;
-- 最高分和最低分
SELECT MAX(score), MIN(score) FROM students;
-- 每个城市的平均分
SELECT city, AVG(score) AS avg_score
FROM students
GROUP BY city;
GROUP BY 是把行按某个字段分组,再对每组分别聚合。AS 是给结果列起别名,让输出更易读。分组之后如果想进一步过滤分组的结果,用 HAVING 而不是 WHERE——WHERE 在分组前过滤行,HAVING 在分组后过滤组:
-- 平均分高于 85 的城市
SELECT city, AVG(score) AS avg_score
FROM students
GROUP BY city
HAVING AVG(score) > 85;
WHERE 和 HAVING 混用是新手最常见的困惑之一:记住“先 WHERE 后 GROUP BY 再 HAVING”这个顺序,就不容易错。
八、多表查询:JOIN 的入门用法
真实业务里数据不会只放在一张表里。学生表存学生信息,成绩表存各科成绩,课程表存课程信息。想查“小明各科成绩”,就得把两张表连起来,这就是 JOIN。
-- 学生表和成绩表,内连接
SELECT s.name, c.course, c.score
FROM students s
JOIN scores c ON s.id = c.student_id;
INNER JOIN(简写 JOIN)只返回两边都能对上的行。如果有的学生还没有成绩记录,INNER JOIN 会把他漏掉;想保留所有学生、没有成绩的显示为 NULL,就用 LEFT JOIN:
-- 左连接:保留左边全部学生
SELECT s.name, c.course, c.score
FROM students s
LEFT JOIN scores c ON s.id = c.student_id;
JOIN 的 ON 条件写错是常见 bug:忘记写 ON 或写错字段,查询结果的行数会暴涨(笛卡尔积)。每次写完 JOIN,先看一眼返回的行数合不合理,再继续。
s 和 c 是表的别名,让语句更短。如果字段名在两边都有(比如 id),不写别名数据库会报“字段不明确”的错误,所以多表查询养成写别名的习惯。
九、子查询与视图:先有个概念
有时候一个查询的结果要作为另一个查询的条件,这就是子查询:
-- 查出分数高于平均分的学生
SELECT name, score FROM students
WHERE score > (SELECT AVG(score) FROM students);
括号里的查询先算出平均分,外层再拿它来比较。子查询可以放在 WHERE、FROM、SELECT 几乎所有位置,还能嵌套多层,但嵌套太深可读性会变差,实际工作中能写成 JOIN 的优先写 JOIN。
视图(VIEW)是把一条查询保存成一个“虚拟表”,以后可以像查表一样查它:
CREATE VIEW top_students AS
SELECT name, score FROM students WHERE score >= 90;
数据量小的时候,子查询和视图理解到这个程度就够了。后面遇到复杂报表,再系统学习窗口函数和 CTE。
十、新手避坑指南
这些坑,几乎每一个新手都会踩一遍,提前知道能省很多时间。
第一,UPDATE 和 DELETE 漏写 WHERE。先拿 SELECT 把 WHERE 条件查一遍,确认命中行数再执行;或者开启事务,执行完先看结果再提交。
第二,SELECT * 用得太随意。开发调试时无所谓,但写到业务代码里,星号会拖回不需要的列,浪费流量,还可能因为表结构变化导致程序出错。生产代码只列出需要的字段。
第三,拿 = 去比较 NULL。记住:NULL 不是值,是“未知”。判断空值用 IS NULL / IS NOT NULL。
第四,字符串的引号和转义。SQL 里字符串用单引号,不用双引号。内容里本身带单引号(比如名字叫 O’Brien)时,要写成两个单引号转义,或者用参数化查询。
第五,删库之前没有备份。测试库都建议定期备份,生产库更不用说。DROP TABLE、TRUNCATE 这类命令没有回收站,执行前再三确认。
第六,把 SQL 拼接进代码字符串。自己拼 SQL 很容易写出 SQL 注入漏洞——用户输入的内容可能被当成代码执行。正确做法是用参数化查询或 ORM,把数据和语句分开。
第七,不建索引就怪数据库慢。小表无所谓,数据量上万之后,没有索引的查询会全表扫描。先学会用 EXPLAIN 看执行计划,再决定给哪个字段建索引。索引也不是越多越好,每个索引都会拖慢写入速度。
第八,忽视字符集和时区。建库建表时指定 utf8mb4,避免 emoji 存不进去;时间字段统一存 UTC,展示时再转本地时区,避免夏令时和跨时区统计出错。
十一、给新手的学习路线建议
SQL 的学习路径很清晰,跟着走不容易迷路。
第一周,装好 SQLite 或 MySQL,练熟建表、增删改查、WHERE、ORDER BY、LIMIT 这几个基本功。每天手写二三十条语句,比看十篇文章有用。
第二周,攻克聚合和分组、JOIN 多表查询、GROUP BY 和 HAVING。再找一份公开数据集练手,把问题变成查询写出来。
第三周,学习子查询、视图、事务和索引的基础概念;学 EXPLAIN 看执行计划,理解什么是全表扫描。
之后,可以结合自己的方向深入:后端开发去学事务隔离级别、锁和分库分表;数据分析去学窗口函数、日期函数和常见报表写法。SQL 是越用越熟的技能,日常工作中多写、多看执行计划,半年下来就能超过大多数同事。
最后再强调一遍:SQL 并不难,难的是没有数据可练。找一个自己感兴趣的数据集——比赛成绩、书单、开支记录都可以——先建表,再提问题,再用 SQL 回答它。第一条查询跑通的那一刻,你就已经入门了。
- 点赞
- 收藏
- 关注作者
评论(0)