零基础学习 SQL:常用语句指南
欢迎来到数据世界的大门!如果你曾对那些复杂的数据报表、用户列表、产品库存感到好奇,想知道它们是如何被管理和提取的,那么学习 SQL(Structured Query Language,结构化查询语言)将是你的第一步。SQL 是一种用于管理关系型数据库的标准语言,它是数据分析、软件开发、数据科学等众多领域不可或缺的技能。
许多人认为编程很难,但 SQL 相对来说更容易上手。它更像是一种指令语言,你用清晰的指令告诉数据库“给我这个数据”、“修改那个数据”或“创建这样一个数据结构”。
本篇文章将从零开始,带你逐步了解 SQL 的核心概念和最常用的语句。我们将从最简单的数据查询开始,然后学习如何插入、更新和删除数据,最后触及如何定义和修改数据结构,以及如何进行更复杂的数据筛选和分组。读完这篇文章,你将能够理解和编写基本的 SQL 查询,为进一步深入学习打下坚实的基础。
文章目录
- SQL 是什么?为什么学它?
- SQL 的定义和作用
- SQL 的应用领域
- 学习 SQL 的优势
- 数据库基础概念入门
- 数据库 (Database)
- 表 (Table)
- 列 (Column / Field)
- 行 (Row / Record)
- 关系型数据库的核心思想
- 准备实践环境
- 选择一个数据库系统 (SQLite, MySQL, PostgreSQL, SQL Server)
- 推荐及获取方式 (以 SQLite 为例)
- 创建一个示例数据库和表
- SQL 核心语句 (CRUD)
- SELECT:查询数据 (Read)
SELECT *
: 查询所有列SELECT column1, column2
: 查询指定列FROM table_name
: 指定从哪个表查询- 实践演练与示例
- INSERT INTO:插入数据 (Create)
- 插入所有列的值
- 插入指定列的值
- 实践演练与示例
- UPDATE:更新数据 (Update)
SET column = value
: 指定要更新的列和值WHERE condition
: 指定更新哪一行(非常重要!)- 实践演练与示例 (含不使用
WHERE
的警告)
- DELETE FROM:删除数据 (Delete)
WHERE condition
: 指定删除哪一行(非常重要!)- 实践演练与示例 (含不使用
WHERE
的警告)
- SELECT:查询数据 (Read)
- 数据定义语句 (DDL)
- CREATE TABLE:创建表
- 指定表名、列名和数据类型
- 常用数据类型介绍 (INT, VARCHAR, TEXT, DATE, FLOAT, BOOLEAN)
- 定义主键 (PRIMARY KEY)
- 实践演练与示例
- ALTER TABLE:修改表结构
- 添加列 (
ADD COLUMN
) - 删除列 (
DROP COLUMN
) - 实践演练与示例
- 添加列 (
- DROP TABLE:删除表
- 实践演练与示例 (含警告)
- CREATE TABLE:创建表
- 进阶查询技巧
- WHERE 子句详解:条件筛选
- 比较运算符 (
=
,!=
,>
,<
,>=
,<=
) - 逻辑运算符 (
AND
,OR
,NOT
) - 范围查询 (
BETWEEN
) - 集合查询 (
IN
) - 模糊匹配 (
LIKE
和通配符%
,_
) - 空值判断 (
IS NULL
,IS NOT NULL
) - 实践演练与示例
- 比较运算符 (
- ORDER BY 子句:排序结果
- 升序 (
ASC
) 与降序 (DESC
) - 按多个列排序
- 实践演练与示例
- 升序 (
- LIMIT / TOP 子句:限制结果数量
- 不同数据库系统的语法差异
- 实践演练与示例
- DISTINCT 关键字:去除重复行
- 实践演练与示例
- WHERE 子句详解:条件筛选
- 聚合函数与分组
- 常用聚合函数:
COUNT()
,SUM()
,AVG()
,MIN()
,MAX()
- GROUP BY 子句:按列分组
- 如何在分组中使用聚合函数
- 实践演练与示例
- HAVING 子句:筛选分组
WHERE
与HAVING
的区别- 实践演练与示例
- 常用聚合函数:
- 连接多个表 (JOIN 入门)
- 为什么需要连接表?关系型数据库的优势
- INNER JOIN:内连接
- 连接条件 (
ON
) - 实践演练与示例 (简单示例)
- 连接条件 (
- 总结与下一步学习
- 回顾常用语句
- 鼓励实践
- 推荐进一步学习方向 (外连接, 子查询, 索引, 事务等)
1. SQL 是什么?为什么学它?
SQL 的定义和作用
SQL,全称 Structured Query Language,即结构化查询语言。它是一种专门用来与数据库沟通的语言。想象一下数据库是巨大的电子档案馆,里面存放着按照特定规则组织好的信息(数据)。SQL 就是你用来对这个档案馆发号施令的语言:
- “找出所有年龄大于 25 岁的用户。”
- “给我上周所有的新订单。”
- “把某个产品的库存数量减少 10。”
- “创建一个新的空间来存放客户的地址信息。”
SQL 不是一种通用的编程语言(比如 Python、Java),它主要专注于数据的管理和操作。尽管存在不同的数据库系统(如 MySQL, PostgreSQL, SQL Server, Oracle, SQLite 等),它们都使用 SQL,只是在一些高级功能或特定语法上略有差异,但核心的常用语句是通用的。
SQL 的应用领域
SQL 几乎渗透在所有与数据相关的领域:
- 软件开发: 绝大多数网站、应用程序的后端都需要与数据库交互来存储和读取用户数据、产品信息、日志等。
- 数据分析: 分析师使用 SQL 从海量数据中提取有价值的信息,生成报表、洞察用户行为、进行业务决策。
- 数据科学/机器学习: 数据科学家通常需要先使用 SQL 清理、转换和准备数据,然后才能用于建模。
- 数据库管理: 数据库管理员 (DBA) 使用 SQL 来维护、优化数据库的性能和安全。
- 商业智能 (BI): 构建仪表板和报告的工具通常底层都依赖 SQL 来获取数据。
学习 SQL 的优势
- 高需求: SQL 是数据相关职位的基本要求,掌握它能显著提升你的就业竞争力。
- 通用性强: 一旦掌握了标准 SQL,你可以相对轻松地适应不同的数据库系统。
- 理解数据: 学习 SQL 能让你更好地理解数据的组织方式和如何有效地获取所需信息。
- 门槛相对低: 相比其他编程语言,SQL 的概念和语法更接近自然语言,更容易入门。
2. 数据库基础概念入门
在我们开始学习 SQL 语句之前,理解一些数据库的基本术语至关重要。
- 数据库 (Database): 可以想象它是一个大型的电子档案馆,是存储数据的整体容器。一个数据库可以包含一个或多个表。
- 表 (Table): 数据库中最基本的数据存储单元。它类似于电子表格(如 Excel)中的一个工作表。每个表都用来存储特定类型的数据,比如一个表存储“用户信息”,另一个表存储“产品信息”。表由行和列组成。
- 列 (Column / Field): 表中的一个垂直方向的类别。每列存储特定类型的数据(例如,用户的姓名、产品的价格)。列有名字(列名)和数据类型(例如,文本、数字、日期)。
- 行 (Row / Record): 表中的一个水平方向的记录。每一行代表一个独立的数据实体(例如,一个特定的用户、一个特定的产品)。一行包含了该实体在每一列上的具体值。
- 关系型数据库的核心思想: 关系型数据库将数据存储在相互关联的表中。表之间的关系通常通过共享某些列(称为键,特别是主键和外键)来建立。这种结构化的存储方式使得数据的查询、管理和维护变得高效和灵活。我们将在后续的 JOIN 部分初步体会这种“关系”的好处。
举例:
想象一个存储学生信息的数据库:
- 数据库: 学校管理系统数据库
- 表:
Students
(学生表) - 列:
StudentID
(学生ID),FirstName
(名),LastName
(姓),Age
(年龄),Major
(专业) - 行:
- 一行代表张三:
StudentID
=101,FirstName
=’三’,LastName
=’张’,Age
=20,Major
=’计算机科学’ - 另一行代表李四:
StudentID
=102,FirstName
=’四’,LastName
=’李’,Age
=21,Major
=’物理’
- 一行代表张三:
学习 SQL 的过程,本质上就是学习如何用 SQL 语句来操作这些表中的行和列。
3. 准备实践环境
学习任何编程或查询语言,动手实践是最好的方法。幸运的是,入门 SQL 非常容易设置环境。你不需要安装大型的数据库服务器,可以选择一个轻量级的工具。
推荐及获取方式 (以 SQLite 为例)
对于初学者,强烈推荐使用 SQLite。
- 优点:
- 非常轻量级,整个数据库就是一个
.sqlite
文件。 - 无需安装服务器,易于设置。
- 语法符合标准 SQL,学到的知识可以迁移到其他数据库。
- 有很多图形界面工具 (GUI) 可以方便地管理和查询。
- 非常轻量级,整个数据库就是一个
- 获取方式:
- SQLite 命令行工具: 访问 https://www.sqlite.org/download.html 下载预编译的二进制文件,它是一个单独的可执行文件,直接运行即可进入命令行界面。
- SQLite GUI 工具: 有许多免费或付费的图形界面工具,让你可以通过点击、拖拽来管理数据库,并且有更友好的查询编辑器。推荐的有:DB Browser for SQLite (免费开源, https://sqlitebrowser.org/)、DBeaver (通用数据库工具, 支持 SQLite 及其他多种数据库, https://dbeaver.io/)。安装其中一个 GUI 工具将极大地方便你的学习。
创建一个示例数据库和表
无论你选择命令行还是 GUI 工具,第一步通常是创建一个新的数据库文件。然后,我们需要创建一个或多个表来存放数据。让我们创建一个简单的 Students
表,用于后续的练习。
使用 GUI 工具通常可以通过菜单或按钮来创建数据库文件和表。如果在 SQLite 命令行中,你可以:
- 打开命令行工具,输入
sqlite3 mydatabase.sqlite
(或者你喜欢的任何文件名)。如果文件不存在,SQLite 会为你创建一个新的数据库文件;如果存在,它会打开该数据库。 - 输入以下 SQL 语句来创建
Students
表:
sql
CREATE TABLE Students (
StudentID INTEGER PRIMARY KEY,
FirstName VARCHAR(50) NOT NULL,
LastName VARCHAR(50) NOT NULL,
Age INT,
Major VARCHAR(100),
EnrollmentDate DATE
);
解释这段 CREATE TABLE
语句:
CREATE TABLE Students
: 告诉数据库我们要创建一个名为Students
的新表。- 圆括号
()
里面定义了表的列。 - 每一行定义一列:
列名 数据类型 [约束]
, 例如:StudentID INTEGER PRIMARY KEY
: 创建一个名为StudentID
的列,数据类型是INTEGER
(整数)。PRIMARY KEY
是一个约束,表示这列的值必须唯一,且不能为空,它通常用来唯一标识表中的每一行。FirstName VARCHAR(50) NOT NULL
: 创建FirstName
列,数据类型是VARCHAR(50)
(变长字符串,最多 50 个字符)。NOT NULL
约束表示这列的值不能为空。LastName VARCHAR(50) NOT NULL
: 类似FirstName
。Age INT
: 创建Age
列,数据类型是INT
(整数)。Major VARCHAR(100)
: 创建Major
列,数据类型是VARCHAR(100)
,允许为空。EnrollmentDate DATE
: 创建EnrollmentDate
列,数据类型是DATE
(日期)。
- 每列的定义之间用逗号
,
分隔。 - 语句末尾用分号
;
结束(这是一个好习惯,虽然有些工具允许省略)。
执行这个语句后,你就拥有了一个空的 Students
表,可以开始学习如何操作它了。
4. SQL 核心语句 (CRUD)
CRUD 是数据库操作中最常用的四个基本功能的首字母缩写:
- Create (创建 – 对应 SQL 的
INSERT
) - Read (读取 – 对应 SQL 的
SELECT
) - Update (更新 – 对应 SQL 的
UPDATE
) - Delete (删除 – 对应 SQL 的
DELETE
)
我们先从最常用的 SELECT
开始。
4.1 SELECT:查询数据 (Read)
SELECT
语句用于从数据库表中检索数据。它是 SQL 中最常用的语句,也是你学习的重点。
基本语法:
sql
SELECT column1, column2, ...
FROM table_name
[WHERE condition]; -- 方括号表示可选部分
或者查询所有列:
sql
SELECT *
FROM table_name
[WHERE condition];
SELECT
: 关键字,表示要查询数据。column1, column2, ...
: 你想要查询的列的名称,用逗号分隔。*
: 星号代表查询表中的所有列。FROM table_name
: 指定你要从哪个表中查询数据。WHERE condition
: (可选) 指定筛选条件,只有满足条件的行才会被返回。我们会在后面详细讲解WHERE
子句。
实践演练与示例:
首先,我们需要向表中添加一些数据,才能看到查询结果。我们可以先手动添加几行数据(使用 INSERT 语句,虽然还没正式学到,但先用起来):
sql
-- 假设已经创建了 Students 表
INSERT INTO Students (StudentID, FirstName, LastName, Age, Major, EnrollmentDate) VALUES (101, '三', '张', 20, '计算机科学', '2022-09-01');
INSERT INTO Students (StudentID, FirstName, LastName, Age, Major, EnrollmentDate) VALUES (102, '四', '李', 21, '物理', '2021-09-01');
INSERT INTO Students (StudentID, FirstName, LastName, Age, Major, EnrollmentDate) VALUES (103, '五', '王', 20, '计算机科学', '2022-09-01');
INSERT INTO Students (StudentID, FirstName, LastName, Age, Major, EnrollmentDate) VALUES (104, '六', '赵', 22, '数学', '2020-09-01');
INSERT INTO Students (StudentID, FirstName, LastName, Age, Major, EnrollmentDate) VALUES (105, '七', '钱', 21, '计算机科学', '2021-09-01');
现在,我们可以开始查询了。
示例 1:查询 Students 表中的所有数据
sql
SELECT *
FROM Students;
- 解释: 这条语句会返回
Students
表中的所有列和所有行。
示例 2:查询 Students 表中学生的姓名和年龄
sql
SELECT FirstName, LastName, Age
FROM Students;
- 解释: 这条语句只会返回
Students
表中FirstName
,LastName
, 和Age
这三列的数据。
示例 3:查询 Students 表中所有学生的专业
sql
SELECT Major
FROM Students;
- 解释: 这会列出所有学生的专业。注意,如果多个学生专业相同,结果中会出现重复的专业名称。如果想去除重复项,可以使用
DISTINCT
关键字(稍后会讲)。
4.2 INSERT INTO:插入数据 (Create)
INSERT INTO
语句用于向表中添加新的行(记录)。
基本语法 1 (为所有列提供值):
sql
INSERT INTO table_name VALUES (value1, value2, value3, ...);
- 这种方式要求你按照表中列的定义顺序,为 所有 列提供一个值。如果表的列很多或者顺序不确定,这种方式容易出错。
基本语法 2 (为指定列提供值):
sql
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);
- 这种方式更常用和推荐。你明确指定要插入值的列,然后按顺序提供对应的值。未指定的列如果允许为空 (
NULL
) 且没有默认值,将会被设置为NULL
。
实践演练与示例:
继续使用我们的 Students
表。
示例 1:插入一条完整的新学生记录 (使用语法 1)
假设 Students
表的列顺序是 StudentID
, FirstName
, LastName
, Age
, Major
, EnrollmentDate
。
sql
INSERT INTO Students VALUES (106, '八', '孙', 20, '电子工程', '2022-09-01');
- 解释: 这会将一个新行添加到
Students
表中,各个值按照列的顺序对应。
示例 2:插入一条新学生记录,只提供部分信息 (使用语法 2)
假设我们暂时不知道某个学生的专业和入学日期。
sql
INSERT INTO Students (StudentID, FirstName, LastName, Age)
VALUES (107, '九', '周', 19);
- 解释: 这将插入一个新行,
StudentID
为 107,姓名和年龄也已赋值。Major
和EnrollmentDate
列因为没有在括号中指定且提供了对应的值,并且我们在CREATE TABLE
时没有给Major
和EnrollmentDate
设置NOT NULL
约束(FirstName
和LastName
是NOT NULL
,所以必须提供值),它们将变为NULL
(空值)。
重要提示:
- 插入的值的数据类型必须与对应列的数据类型兼容。字符串值通常用单引号
'
括起来,数字不需要。日期和时间值通常也用单引号括起来,格式取决于数据库系统和设置,但'YYYY-MM-DD'
是常见的日期格式。 - 如果列有
NOT NULL
约束,你在插入时必须为它提供一个非NULL
的值。 - 如果列是
PRIMARY KEY
且数据类型是整数,有些数据库系统(如 SQLite)会自动生成唯一值,你可以在INSERT
时忽略这列,或者为它提供NULL
值(具体行为取决于数据库系统和表定义)。但在我们的Students
表中,我们明确要求提供了StudentID
的值。
4.3 UPDATE:更新数据 (Update)
UPDATE
语句用于修改表中已存在的记录。
基本语法:
sql
UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition; -- **非常重要!**
UPDATE table_name
: 指定要更新哪个表。SET column1 = value1, ...
: 指定要修改哪些列,以及它们的新值。可以同时更新多个列,用逗号分隔。WHERE condition
: (可选,但通常必选) 指定要更新哪些行。只有满足condition
的行才会被修改。
实践演练与示例:
继续使用 Students
表。
示例 1:将某个学生的专业从“物理”更新为“应用物理”
假设学生ID为 102 的专业是物理。
sql
UPDATE Students
SET Major = '应用物理'
WHERE StudentID = 102;
- 解释: 这条语句会找到
StudentID
等于 102 的那一行,然后将其Major
列的值更新为'应用物理'
。
示例 2:将所有“计算机科学”专业的学生的年龄增加 1 岁
sql
UPDATE Students
SET Age = Age + 1
WHERE Major = '计算机科学';
- 解释: 这会找到所有
Major
是'计算机科学'
的行,并将它们的Age
值在原有基础上加 1。
示例 3:更新多个列
将学生ID为 103 的年龄更新为 21,并将专业更新为“软件工程”。
sql
UPDATE Students
SET Age = 21, Major = '软件工程'
WHERE StudentID = 103;
- 解释: 使用逗号分隔,可以同时更新多个列。
示例 4:🚨🚨🚨 更新所有行的年龄(请谨慎执行!) 🚨🚨🚨
sql
UPDATE Students
SET Age = Age + 1; -- **注意:没有 WHERE 子句!**
- 解释: 极其危险! 这条语句会更新
Students
表中的所有行,将所有学生的年龄都加 1。在实际操作中,除非你确定要对全表进行修改,永远不要省略WHERE
子句! 如果不小心执行了没有WHERE
的UPDATE
,可能会导致数据大面积错误,且难以恢复。
再次强调: 在编写 UPDATE
语句时,务必仔细检查 WHERE
子句,确保它准确地选择了你想要修改的行。
4.4 DELETE FROM:删除数据 (Delete)
DELETE FROM
语句用于从表中删除已存在的记录。
基本语法:
sql
DELETE FROM table_name
WHERE condition; -- **非常重要!**
DELETE FROM table_name
: 指定要从哪个表中删除数据。WHERE condition
: (可选,但通常必选) 指定要删除哪些行。只有满足condition
的行才会被删除。
实践演练与示例:
继续使用 Students
表。
示例 1:删除学生ID为 106 的学生记录
sql
DELETE FROM Students
WHERE StudentID = 106;
- 解释: 这会找到
StudentID
等于 106 的那一行,并将其从表中删除。
示例 2:删除所有年龄小于 20 岁的学生记录
sql
DELETE FROM Students
WHERE Age < 20;
- 解释: 这会删除所有年龄小于 20 的学生行。
示例 3:🚨🚨🚨 删除表中的所有数据(请谨慎执行!) 🚨🚨🚨
sql
DELETE FROM Students; -- **注意:没有 WHERE 子句!**
- 解释: 极其危险! 这条语句会删除
Students
表中的所有行。表结构本身(列定义、约束等)会保留,但数据将全部丢失。这通常用于清空表。在实际操作中,除非你确定要清空整个表,永远不要省略WHERE
子句! 不小心执行可能导致所有数据丢失。
再次强调: 在编写 DELETE
语句时,务必仔细检查 WHERE
子句,确保它准确地选择了你想要删除的行。对于重要数据,执行 DELETE
前最好先用相同的 WHERE
条件执行一个 SELECT
语句,看看会选中哪些行,以确认无误。
5. 数据定义语句 (DDL)
除了操作数据 (CRUD),SQL 还用于定义和管理数据库对象的结构,这部分语句被称为数据定义语言 (Data Definition Language, DDL)。最常见的 DDL 语句包括 CREATE
, ALTER
, DROP
。
5.1 CREATE TABLE:创建表
我们已经在前面创建 Students
表时演示了 CREATE TABLE
的基本用法。这里我们再详细回顾一下语法和常用的数据类型。
基本语法:
sql
CREATE TABLE table_name (
column1 datatype [constraints],
column2 datatype [constraints],
...
);
CREATE TABLE table_name
: 指定要创建的表名。column datatype [constraints]
: 定义列的名称、数据类型以及可选的约束。
常用数据类型介绍:
不同的数据库系统支持的数据类型略有差异,但以下类型是通用的或具有相似概念:
- 整型:
INT
,INTEGER
,SMALLINT
,BIGINT
(用于存储整数,根据类型不同支持的范围大小不同)。 - 浮点型:
FLOAT
,DOUBLE
,REAL
(用于存储浮点数)。 - 定点型/精确数值:
DECIMAL(p, s)
,NUMERIC(p, s)
(用于存储需要精确计算的数值,如货币。p
是总位数,s
是小数点后位数)。 - 字符串/文本:
VARCHAR(length)
(变长字符串,需要指定最大长度length
),CHAR(length)
(定长字符串),TEXT
(长文本)。 - 日期和时间:
DATE
(存储日期,如 ‘2023-10-26’),TIME
(存储时间,如 ’14:30:00′),DATETIME
/TIMESTAMP
(存储日期和时间)。 - 布尔型:
BOOLEAN
/BOOL
(存储真或假值)。 - 空值:
NULL
(表示缺失或未知的值,不是零或空字符串)。
常用列约束 (Constraints):
NOT NULL
: 该列的值不能为 NULL。UNIQUE
: 该列的所有值必须是唯一的(可以有多个 NULL 值,取决于数据库系统)。PRIMARY KEY
: 该列是主键。主键用于唯一标识表中的每一行,它集成了NOT NULL
和UNIQUE
的特性。一个表通常只有一个主键,可以是单列或多列组合。FOREIGN KEY
: 外键。用于建立两个表之间的关系,指向另一个表的主键(或其他具有唯一约束的列)。我们将在 JOIN 部分稍微触及这个概念。DEFAULT value
: 为该列设置一个默认值,如果在插入新行时没有为该列指定值,将使用默认值。CHECK condition
: 定义一个检查约束,确保该列的值满足指定的条件。
示例:创建一个更详细的 Courses 表
sql
CREATE TABLE Courses (
CourseID INTEGER PRIMARY KEY,
CourseName VARCHAR(100) NOT NULL UNIQUE, -- 课程名不能为空且必须唯一
Credits INT CHECK (Credits > 0), -- 学分必须是大于0的整数
Department VARCHAR(50) DEFAULT 'General' -- 如果不指定,默认部门为 'General'
);
- 解释: 创建了一个
Courses
表,包含课程ID(主键)、课程名(非空且唯一)、学分(必须大于0)和部门(默认值为 ‘General’)。
5.2 ALTER TABLE:修改表结构
ALTER TABLE
语句用于修改表的结构,例如添加、删除或修改列。
基本语法 (添加列):
sql
ALTER TABLE table_name
ADD COLUMN column_name datatype [constraints];
基本语法 (删除列):
sql
ALTER TABLE table_name
DROP COLUMN column_name;
- 注意:修改列的数据类型或约束的语法在不同数据库系统中有较大差异,这里不做详细介绍,主要关注添加和删除列。
实践演练与示例:
继续使用 Students
表。
示例 1:向 Students 表添加一个“Email”列
sql
ALTER TABLE Students
ADD COLUMN Email VARCHAR(255);
- 解释: 在
Students
表中添加一个名为Email
的列,数据类型为VARCHAR(255)
。现有行的Email
列将被设置为NULL
。
示例 2:向 Students 表添加一个“GPA”列,允许小数
sql
ALTER TABLE Students
ADD COLUMN GPA REAL;
- 解释: 添加一个
GPA
列,数据类型为REAL
(浮点数)。
示例 3:删除 Students 表中的“Major”列
sql
ALTER TABLE Students
DROP COLUMN Major;
- 解释: 从
Students
表中删除Major
列。注意,这会丢失该列中的所有数据。
5.3 DROP TABLE:删除表
DROP TABLE
语句用于完全删除一个表,包括表结构和所有数据。
基本语法:
sql
DROP TABLE table_name;
实践演练与示例:
示例:删除 Courses 表
sql
DROP TABLE Courses;
- 解释: 这会从数据库中永久删除
Courses
表及其所有数据。
🚨🚨🚨 警告: DROP TABLE
是一个破坏性操作,一旦执行,表及其数据将很难恢复。请务必谨慎使用,特别是在生产环境中!
6. 进阶查询技巧
掌握了基本的 CRUD 和 DDL 语句后,我们可以学习如何更精确、更灵活地查询数据。
6.1 WHERE 子句详解:条件筛选
WHERE
子句是 SELECT
, UPDATE
, DELETE
语句中用于指定筛选条件的强大工具。它允许你根据一个或多个条件来选择或操作特定的行。
基本语法回顾:
sql
SELECT column1, column2, ...
FROM table_name
WHERE condition; -- 筛选条件在这里
常用的筛选条件构成:
condition
通常由一个或多个表达式和运算符组成。
a) 比较运算符:
用于比较列的值与另一个值或列的值。
=
:等于!=
或<>
:不等于 (不同数据库系统可能使用其中一个)>
:大于<
:小于>=
:大于或等于<=
:小于或等于
示例:
sql
SELECT * FROM Students WHERE Age > 20; -- 查询年龄大于20岁的学生
SELECT FirstName, LastName FROM Students WHERE Major = '计算机科学'; -- 查询专业是计算机科学的学生
SELECT * FROM Students WHERE EnrollmentDate < '2022-01-01'; -- 查询2022年之前入学的学生
SELECT * FROM Students WHERE StudentID <> 101; -- 查询学生ID不等于101的学生
b) 逻辑运算符:
用于组合多个条件。
AND
: 当所有连接的条件都为真时,整个条件为真。OR
: 当任一连接的条件为真时,整个条件为真。NOT
: 对连接的条件取反。
示例:
sql
SELECT * FROM Students WHERE Age >= 20 AND Major = '计算机科学'; -- 查询年龄大于等于20岁且专业是计算机科学的学生
SELECT * FROM Students WHERE Major = '物理' OR Major = '数学'; -- 查询专业是物理或数学的学生
SELECT * FROM Students WHERE NOT Major = '计算机科学'; -- 查询专业不是计算机科学的学生 (等同于 Major <> '计算机科学')
SELECT * FROM Students WHERE (Age > 20 AND Major = '物理') OR Age < 19; -- 使用括号组合复杂的逻辑
c) 范围查询 (BETWEEN
):
用于判断一个值是否在某个范围内(包含边界)。
sql
SELECT * FROM Students WHERE Age BETWEEN 20 AND 22; -- 查询年龄在20到22岁之间(含20和22)的学生
SELECT * FROM Students WHERE EnrollmentDate BETWEEN '2022-01-01' AND '2022-12-31'; -- 查询2022年入学的学生
d) 集合查询 (IN
):
用于判断一个值是否在一个给定的值列表内。
sql
SELECT * FROM Students WHERE Major IN ('计算机科学', '数学', '电子工程'); -- 查询专业是计算机科学、数学或电子工程的学生 (等同于使用多个 OR)
SELECT * FROM Students WHERE StudentID NOT IN (101, 103, 105); -- 查询学生ID不是101、103或105的学生
e) 模糊匹配 (LIKE
):
用于在字符串列中进行模式匹配。需要配合通配符使用:
%
: 代表任意零个或多个字符。_
: 代表任意一个字符。
sql
SELECT * FROM Students WHERE LastName LIKE '张%'; -- 查询姓“张”的学生
SELECT * FROM Students WHERE FirstName LIKE '%三'; -- 查询名以“三”结尾的学生
SELECT * FROM Students WHERE CourseName LIKE '%数据%'; -- 查询课程名中包含“数据”二字的课程 (假设有 Courses 表)
SELECT * FROM Students WHERE StudentID LIKE '10_'; -- 查询学生ID是10几的学生 (如 101, 102, ..., 109)
SELECT * FROM Students WHERE FirstName LIKE '_三'; -- 查询名是两个字且第二个字是“三”的学生 (如“小三”)
f) 空值判断 (IS NULL
, IS NOT NULL
):
用于判断一个列的值是否为 NULL
。注意:不能使用 =
或 !=
来判断 NULL
。
sql
SELECT * FROM Students WHERE Major IS NULL; -- 查询专业为空的学生
SELECT * FROM Students WHERE Email IS NOT NULL; -- 查询Email不为空的学生
6.2 ORDER BY 子句:排序结果
ORDER BY
子句用于对查询结果集进行排序。默认是升序。
基本语法:
sql
SELECT column1, column2, ...
FROM table_name
WHERE condition
ORDER BY column1 [ASC|DESC], column2 [ASC|DESC], ...;
ORDER BY
: 关键字,表示要排序。column1, column2, ...
: 指定用于排序的列。可以按多个列排序,优先级从左到右。ASC
: 升序 (Ascending),默认值。DESC
: 降序 (Descending)。
实践演练与示例:
sql
SELECT * FROM Students ORDER BY Age; -- 按年龄升序排序所有学生
SELECT * FROM Students ORDER BY EnrollmentDate DESC; -- 按入学日期降序排序所有学生 (最近入学的排在前面)
SELECT FirstName, LastName, Major, Age FROM Students ORDER BY Major ASC, Age DESC; -- 先按专业升序排序,如果专业相同,则按年龄降序排序
6.3 LIMIT / TOP 子句:限制结果数量
在处理大量数据时,有时我们只需要查看前面几条或指定范围的记录。可以使用 LIMIT
或 TOP
子句。不同数据库系统使用的关键字不同:
- 标准 SQL, MySQL, PostgreSQL, SQLite: 使用
LIMIT
- SQL Server, MS Access: 使用
TOP
语法 (LIMIT):
sql
SELECT column1, column2, ...
FROM table_name
WHERE condition
ORDER BY column
LIMIT number; -- 获取前 number 行
或 LIMIT offset, number
(获取从 offset
开始的 number
行,用于分页,offset 从 0 或 1 开始取决于数据库)
语法 (TOP):
sql
SELECT TOP number column1, column2, ...
FROM table_name
WHERE condition
ORDER BY column; -- SQL Server 的 TOP 通常放在 SELECT 后面
实践演练与示例 (以 LIMIT 为例):
sql
SELECT * FROM Students LIMIT 3; -- 获取表中的前3行 (不保证顺序,通常需要配合 ORDER BY)
SELECT * FROM Students ORDER BY Age DESC LIMIT 5; -- 查询年龄最大的5个学生
SELECT * FROM Students ORDER BY StudentID LIMIT 5 OFFSET 5; -- 查询学生ID排序后的第6到第10个学生 (从偏移量5开始,取5个)
6.4 DISTINCT 关键字:去除重复行
DISTINCT
关键字用于去除结果集中的重复行,只返回唯一的组合。
基本语法:
sql
SELECT DISTINCT column1, column2, ...
FROM table_name
WHERE condition;
DISTINCT
: 放在SELECT
后面,作用于SELECT
后面的所有列的组合。
实践演练与示例:
sql
SELECT Major FROM Students; -- 可能返回重复的专业,如:计算机科学, 物理, 计算机科学, 数学, 计算机科学
SELECT DISTINCT Major FROM Students; -- 返回唯一的专业列表:计算机科学, 物理, 数学
SELECT DISTINCT Age, Major FROM Students; -- 返回年龄和专业的唯一组合
7. 聚合函数与分组
仅仅查询原始数据往往不够,我们经常需要对数据进行统计分析,比如计算总数、平均值、最大/最小值等。这就需要用到聚合函数。
有时候,我们还需要对数据进行分组统计,比如计算每个专业的学生人数,或者每个专业的平均年龄。这时就需要用到 GROUP BY
子句。
7.1 常用聚合函数:
聚合函数作用于一组行,并返回单个值。
COUNT(column)
: 统计指定列中非NULL
值的行数。COUNT(*)
统计所有行(包括NULL
值的行)。SUM(column)
: 计算指定列的总和(仅适用于数值类型)。AVG(column)
: 计算指定列的平均值(仅适用于数值类型)。MIN(column)
: 获取指定列的最小值。MAX(column)
: 获取指定列的最大值。
实践演练与示例:
sql
SELECT COUNT(*) FROM Students; -- 统计学生总人数 (所有行)
SELECT COUNT(Major) FROM Students; -- 统计有专业信息的学生人数 (Major列非NULL的行)
SELECT AVG(Age) FROM Students; -- 计算学生的平均年龄
SELECT MIN(Age) FROM Students; -- 获取最小年龄
SELECT MAX(EnrollmentDate) FROM Students; -- 获取最晚的入学日期
SELECT SUM(Credits) FROM Courses; -- 计算所有课程的总学分 (假设有 Courses 表)
7.2 GROUP BY 子句:按列分组
GROUP BY
子句用于将结果集按照一个或多个列进行分组。聚合函数通常与 GROUP BY
一起使用,以便对每个组而不是整个结果集进行计算。
基本语法:
sql
SELECT column_to_group, aggregate_function(column_to_aggregate), ...
FROM table_name
WHERE condition
GROUP BY column_to_group, ...
ORDER BY column_to_group, ...;
GROUP BY column_to_group
: 指定用于分组的列。所有具有相同值的行将被分到同一个组。SELECT
列表中,除了聚合函数外,只能包含GROUP BY
子句中指定的列。
实践演练与示例:
sql
SELECT Major, COUNT(*) AS NumberOfStudents -- AS 为列取别名,方便阅读
FROM Students
GROUP BY Major;
- 解释: 这会按照
Major
列将学生分组,然后统计每个组(每个专业)的学生人数。结果可能是:
Major NumberOfStudents
------------- ----------------
计算机科学 3
物理 1
数学 1
电子工程 1
NULL 1
(如果有的学生专业是 NULL,会单独分为一组)
sql
SELECT Major, AVG(Age) AS AverageAge
FROM Students
GROUP BY Major;
- 解释: 统计每个专业的平均年龄。
sql
SELECT EnrollmentDate, COUNT(*) AS NewStudentsCount
FROM Students
GROUP BY EnrollmentDate
ORDER BY EnrollmentDate;
- 解释: 统计每个入学日期的新生人数,并按日期排序。
7.3 HAVING 子句:筛选分组
WHERE
子句用于在分组之前筛选原始行,而 HAVING
子句用于在分组之后筛选分组结果。聚合函数不能用于 WHERE
子句,但可以用于 HAVING
子句。
基本语法:
sql
SELECT column_to_group, aggregate_function(column_to_aggregate), ...
FROM table_name
WHERE condition -- (可选) 先筛选原始行
GROUP BY column_to_group, ...
HAVING group_condition -- (可选) 再筛选分组结果
ORDER BY column_to_group, ...;
实践演练与示例:
sql
-- 查询学生人数多于1个的专业及其人数
SELECT Major, COUNT(*) AS NumberOfStudents
FROM Students
GROUP BY Major
HAVING COUNT(*) > 1;
- 解释: 先按专业分组并计算各专业人数,然后只保留人数大于 1 的分组。
sql
-- 查询平均年龄大于20岁的专业及其平均年龄
SELECT Major, AVG(Age) AS AverageAge
FROM Students
GROUP BY Major
HAVING AVG(Age) > 20;
- 解释: 先按专业分组并计算各专业平均年龄,然后只保留平均年龄大于 20 的分组。
WHERE
与 HAVING
的区别总结:
WHERE
在GROUP BY
之前执行,用于筛选原始行。它不能包含聚合函数。HAVING
在GROUP BY
之后执行,用于筛选分组的结果。它可以包含聚合函数。
8. 连接多个表 (JOIN 入门)
在实际应用中,数据通常分散在多个相关的表中,而不是全部挤在一个大表中。例如,学生信息在一个表,课程信息在另一个表,学生选课记录在第三个表。关系型数据库的强大之处在于能够通过表之间的“关系”将这些数据有效地组合起来。JOIN
语句就是用来实现这个功能的。
为什么需要连接表?
考虑一下,如果要在学生选课记录中显示学生的姓名和课程名称,而不是只有学生ID和课程ID,我们就需要:
- 从“选课记录表”获取学生ID和课程ID。
- 根据学生ID去“学生表”查找对应的学生姓名。
- 根据课程ID去“课程表”查找对应的课程名称。
JOIN
语句能够一次性完成这个“查找”和“组合”的过程。
表之间的关系通常通过主键 (PRIMARY KEY) 和 外键 (FOREIGN KEY) 来建立。外键是某个表中的一列或一组列,它们的值指向另一个表的主键。例如,Enrollments
(选课记录) 表可能有 StudentID
和 CourseID
列,它们分别是指向 Students
表的 StudentID
主键和 Courses
表的 CourseID
主键的外键。
8.1 INNER JOIN:内连接
内连接是最常用的连接类型。它返回两个表中在连接列上匹配成功的行。如果某个行在其中一个表中没有匹配项,则不会出现在结果中。
基本语法:
sql
SELECT column_list
FROM table1
INNER JOIN table2 ON table1.column = table2.column;
SELECT column_list
: 选择你想要显示的列。为了避免歧义,通常使用table_name.column_name
的形式指定列属于哪个表。FROM table1
: 指定第一个表。INNER JOIN table2
: 指定要与table1
进行内连接的第二个表。ON table1.column = table2.column
: 指定连接条件。这通常是两个表中具有相同意义且存在对应关系的列(例如,一个表的外键与另一个表的主键)。
示例:
假设我们有一个 Enrollments
(选课记录) 表,包含 EnrollmentID
, StudentID
, CourseID
, Grade
列,其中 StudentID
是外键指向 Students
表的 StudentID
,CourseID
是外键指向 Courses
表的 CourseID
。
“`sql
— 创建 Enrollments 表
CREATE TABLE Enrollments (
EnrollmentID INTEGER PRIMARY KEY,
StudentID INTEGER,
CourseID INTEGER,
Grade VARCHAR(10),
FOREIGN KEY (StudentID) REFERENCES Students(StudentID), — 假设 Students 表已存在
FOREIGN KEY (CourseID) REFERENCES Courses(CourseID) — 假设 Courses 表已存在
);
— 添加一些选课数据
INSERT INTO Enrollments (EnrollmentID, StudentID, CourseID, Grade) VALUES (1, 101, 1, ‘A’); — 张三 选课ID 1 (假设课程ID 1 是 数据库原理)
INSERT INTO Enrollments (EnrollmentID, StudentID, CourseID, Grade) VALUES (2, 101, 2, ‘B’); — 张三 选课ID 2 (假设课程ID 2 是 算法导论)
INSERT INTO Enrollments (EnrollmentID, StudentID, CourseID, Grade) VALUES (3, 102, 1, ‘C’); — 李四 选课ID 1
INSERT INTO Enrollments (EnrollmentID, StudentID, CourseID, Grade) VALUES (4, 103, 3, ‘A-‘); — 王五 选课ID 3 (假设课程ID 3 是 操作系统)
— 假设学生 104 (赵六) 和 105 (钱七) 还没有选课记录
— 假设课程 ID 4 (例如 软件工程) 还没有学生选
“`
现在,我们来查询选课记录,并显示学生的姓名和课程名称:
sql
SELECT
Students.FirstName,
Students.LastName,
Courses.CourseName,
Enrollments.Grade
FROM
Enrollments -- 从选课记录表开始
INNER JOIN
Students ON Enrollments.StudentID = Students.StudentID -- 连接学生表,条件是选课表的 StudentID 等于学生表的 StudentID
INNER JOIN
Courses ON Enrollments.CourseID = Courses.CourseID; -- 连接课程表,条件是选课表的 CourseID 等于课程表的 CourseID
-
解释: 这条语句将
Enrollments
,Students
,Courses
三个表连接起来。它会找到Enrollments
表中的每一行,然后根据StudentID
去匹配Students
表,根据CourseID
去匹配Courses
表。只有当在三个表中都能找到对应的匹配项时,才会返回结果行。结果将包含学生的姓名、课程名称和该学生在该课程中的成绩。例如,
Enrollments
表中的(1, 101, 1, 'A')
会与Students
表中StudentID = 101
的行(张三)以及Courses
表中CourseID = 1
的行(数据库原理)匹配,最终产生一行结果:张
,三
,数据库原理
,A
。如果学生 104 没有选课记录,或者课程 ID 4 没有被任何学生选择,涉及它们的行就不会出现在
INNER JOIN
的结果中。
连接是关系型数据库查询的核心,也是 SQL 的难点之一。这里只介绍了最基本的内连接,还有外连接 (LEFT JOIN, RIGHT JOIN, FULL JOIN) 等更复杂的连接方式,用于处理不完全匹配的情况。
9. 总结与下一步学习
恭喜你!如果你已经阅读到这里,并尝试了其中的例子,你已经掌握了 SQL 的核心基础:
- 理解了数据库、表、列、行的基本概念。
- 学会了如何使用
SELECT
查询数据,包括选择特定列和使用*
。 - 掌握了使用
INSERT
添加新数据。 - 了解了使用
UPDATE
修改数据,并记住了WHERE
子句的重要性。 - 学会了使用
DELETE
删除数据,并记住了WHERE
子句的重要性。 - 学会了使用
CREATE TABLE
定义表结构,包括常用数据类型和主键。 - 了解了如何使用
ALTER TABLE
修改表结构(添加/删除列)。 - 学会了使用
DROP TABLE
删除表(并记住了其风险)。 - 深入学习了
WHERE
子句,掌握了各种条件筛选的方法(比较、逻辑、范围、集合、模糊匹配、空值判断)。 - 学会了使用
ORDER BY
对结果进行排序。 - 了解了使用
LIMIT
或TOP
限制结果数量。 - 学会了使用
DISTINCT
去除重复行。 - 掌握了常用的聚合函数 (
COUNT
,SUM
,AVG
,MIN
,MAX
)。 - 学会了使用
GROUP BY
进行分组统计,并理解了HAVING
与WHERE
的区别。 - 初步了解了关系型数据库如何连接多个表,并掌握了最基本的
INNER JOIN
用法。
这篇指南覆盖了 SQL 中最常用、最基础但也是最重要的语句。掌握了这些,你已经具备了与数据库进行基本交流的能力。
下一步学习方向:
SQL 的世界远不止这些,你可以继续学习:
- 更高级的连接:
LEFT JOIN
,RIGHT JOIN
,FULL JOIN
等,处理更复杂的表关联场景。 - 子查询 (Subquery): 在一个查询中使用另一个查询的结果。
- 联合查询 (UNION): 合并多个
SELECT
语句的结果集。 - 窗口函数 (Window Functions): 进行更复杂的分析和排名。
- 索引 (Index): 理解如何通过创建索引来优化查询性能。
- 事务 (Transaction): 学习如何确保一系列数据库操作要么全部成功,要么全部失败,保证数据一致性。
- 特定数据库系统的特性: 深入了解你正在使用的具体数据库系统(如 MySQL, PostgreSQL, SQL Server)的独特功能和优化技巧。
最后的建议:
- 持续实践: 找一个实际的数据集或自己创建数据,不断练习编写各种查询。遇到问题多查阅文档和在线资源。
- 理解数据: 在编写查询之前,花时间理解你的数据是什么、表之间有什么关系,这将帮助你写出正确的 SQL 语句。
- 谨慎操作: 在执行
UPDATE
,DELETE
,DROP TABLE
等修改或删除数据的语句时,务必再三确认,最好先在测试环境中验证。
祝你在 SQL 的学习旅程中一切顺利!数据世界的精彩正等待着你去探索。