SQL 入门教程:零基础快速上手指南 – wiki基地


SQL 入门教程:零基础快速上手指南 (超详细版)

欢迎来到 SQL 的世界!无论你是希望成为数据分析师、后端开发者、数据库管理员,还是仅仅对数据如何存储和检索感到好奇,掌握 SQL(Structured Query Language,结构化查询语言)都是一项至关重要的技能。这篇教程旨在为完全没有 SQL 基础的初学者提供一个清晰、详细且易于理解的入门指南,帮助你快速上手,为后续深入学习打下坚实的基础。

本文目标读者:

  • 对编程或数据库完全陌生的新手。
  • 了解一些编程概念但从未接触过 SQL 的开发者。
  • 任何希望通过数据进行查询和分析的人。

你将学到:

  1. 什么是 SQL?它为什么重要?
  2. 数据库和关系型数据库的基本概念(表、行、列、主键、外键)。
  3. 基本的 SQL 语法规则。
  4. 最核心的 SQL 命令:
    • 查询数据 (SELECT)
    • 筛选数据 (WHERE)
    • 排序数据 (ORDER BY)
    • 限制结果数量 (LIMIT)
    • 聚合数据 (COUNT, SUM, AVG, MIN, MAX)
    • 分组数据 (GROUP BY, HAVING)
    • 插入数据 (INSERT INTO)
    • 更新数据 (UPDATE)
    • 删除数据 (DELETE FROM)
    • 创建表 (CREATE TABLE)
    • 修改表 (ALTER TABLE)
    • 删除表 (DROP TABLE)
    • 连接多个表 (JOIN)
  5. 一些编写 SQL 的最佳实践。
  6. 如何继续学习和实践。

准备工作 (可选但推荐):

虽然你可以只阅读理论,但亲手实践是学习 SQL 最好的方式。你可以选择以下任一方式进行练习:

  • 在线 SQL 编辑器: 许多网站提供在线 SQL 环境,无需安装任何软件(例如 SQL Fiddle, DB Fiddle, LeetCode 数据库题目区)。这是最快开始的方式。
  • 本地数据库: 安装一个轻量级的数据库系统,如 SQLite (非常适合初学者,通常是一个单一文件),或者更强大的系统如 PostgreSQL 或 MySQL。它们都有免费版本。

为了方便讲解,我们将假设有一个简单的数据库,包含两个表:Employees (员工表) 和 Departments (部门表)。

示例表结构:

  1. Employees 表:

    • EmployeeID (整数, 主键 – 唯一标识每个员工)
    • FirstName (文本)
    • LastName (文本)
    • Email (文本, 唯一)
    • HireDate (日期)
    • Salary (数字)
    • DepartmentID (整数, 外键 – 关联到 Departments 表)
  2. Departments 表:

    • DepartmentID (整数, 主键 – 唯一标识每个部门)
    • DepartmentName (文本, 唯一)
    • Location (文本)

**(请注意:在实际操作中,你需要先创建这些表并插入数据,我们将在后续章节讲解如何做。现在,只需理解这个结构即可。) **


第一章:SQL 与数据库基础

1.1 什么是 SQL?

SQL (Structured Query Language) 是一种专门为管理和操作关系型数据库(Relational Database Management System, RDBMS)而设计的标准化语言。你可以把它想象成一种你用来与数据库“对话”的语言。通过 SQL,你可以:

  • 查询 (Query): 从数据库中检索信息。
  • 操作 (Manipulate): 添加、更新或删除数据库中的数据。
  • 定义 (Define): 创建、修改或删除数据库的结构(如表)。
  • 控制 (Control): 设置用户权限,管理数据库访问。

1.2 什么是数据库?

简单来说,数据库就是一个有组织的数据集合,通常以电子方式存储在计算机系统中。它的目的是高效地存储、检索和管理大量信息。想象一个巨大的、高度组织的电子文件柜。

1.3 关系型数据库 (RDBMS)

关系型数据库是目前最流行的一种数据库类型。它以 表 (Table) 的形式组织数据。

  • 表 (Table): 数据的基本存储单元,类似于电子表格 (Spreadsheet)。一个数据库可以包含多个表。例如,我们上面提到的 Employees 表和 Departments 表。
  • 列 (Column / Field / Attribute): 表中的一个垂直部分,定义了表中存储的数据类型。例如,Employees 表中的 FirstNameSalary 等都是列。每一列都有一个名称和数据类型(如文本、数字、日期)。
  • 行 (Row / Record / Tuple): 表中的一个水平部分,代表一个单独的数据项(或实体)。例如,Employees 表中的每一行代表一个具体的员工信息。
  • 数据类型 (Data Type): 指定列可以存储的数据种类。常见的有:
    • INTINTEGER: 整数。
    • VARCHAR(n): 可变长度的文本字符串(最多 n 个字符)。
    • CHAR(n): 固定长度的文本字符串(n 个字符)。
    • TEXT: 大段文本。
    • DECIMAL(p, s)NUMERIC(p, s): 精确的数值,p 是总位数,s 是小数点后的位数。
    • FLOAT, REAL: 浮点数。
    • DATE: 日期 (年-月-日)。
    • TIME: 时间 (时:分:秒)。
    • TIMESTAMPDATETIME: 日期和时间。
    • BOOLEAN: 真 (True) 或假 (False)。

1.4 主键 (Primary Key) 和 外键 (Foreign Key)

这两个概念对于理解关系型数据库至关重要。

  • 主键 (Primary Key – PK): 表中用于 唯一标识 每一行的列(或列的组合)。主键的值必须是唯一的,并且不能为空 (NOT NULL)。例如,Employees 表的 EmployeeIDDepartments 表的 DepartmentID 都是主键。这确保了我们总能精确地找到某一个员工或某一个部门。
  • 外键 (Foreign Key – FK): 表中的一个(或一组)列,其值 引用另一个表主键。外键用于建立和强制两个表之间的链接或关系。例如,Employees 表中的 DepartmentID 列是一个外键,它引用了 Departments 表的 DepartmentID 主键。这表示每个员工都属于 Departments 表中存在的一个部门。外键确保了数据的 参照完整性 (Referential Integrity),即你不能添加一个员工到一个不存在的部门。

第二章:SQL 语法基础

SQL 语法相对简单直观,遵循一些基本规则:

  1. 关键字不区分大小写 (通常): 大多数数据库系统不区分 SQL 关键字的大小写(如 SELECT, FROM, WHERE)。但通常推荐将 SQL 关键字大写,表名和列名按习惯(如驼峰命名法 EmployeeID 或下划线 employee_id)书写,以提高可读性。注意: 表名和列名是否区分大小写取决于具体的数据库系统和配置。
  2. 语句以分号结尾 (;): 虽然在某些 SQL 工具或单条语句执行时分号不是必需的,但它是标准的 SQL 语句结束符。在编写多条语句或脚本时,使用分号是个好习惯。
  3. 注释:
    • 单行注释:以 -- 开始,直到行尾。
    • 多行注释:以 /* 开始,以 */ 结束。注释对于解释复杂查询非常有用。

“`sql
— 这是单行注释:选择所有员工的姓名
SELECT FirstName, LastName — 可以在行末注释
FROM Employees;

/
这是
一个
多行注释。
下面的语句将查询部门信息。
/
SELECT DepartmentName
FROM Departments;
“`


第三章:查询数据 (SELECT) – SQL 的核心

SELECT 语句是 SQL 中最常用、最重要的语句,用于从数据库表中检索数据。

3.1 选择特定列

指定你想看到的列名,用逗号分隔。

sql
-- 查询所有员工的名字和姓氏
SELECT FirstName, LastName
FROM Employees;

3.2 选择所有列

使用星号 (*) 代表选择所有列。

sql
-- 查询员工表中的所有信息
SELECT *
FROM Employees;

注意: 在实际应用中,尤其是在程序代码里,不推荐无节制地使用 *,因为它可能检索不必要的数据,影响性能,并且如果表结构发生变化(如增加列),可能会导致程序出错。明确指定所需列是更好的做法。

3.3 AS – 列别名

可以为查询结果中的列指定一个临时的、更易读的名称(别名)。

“`sql
— 查询员工全名,并将列命名为 FullName
SELECT FirstName, LastName, FirstName || ‘ ‘ || LastName AS FullName — SQLite/PostgreSQL 的字符串连接符是 ||
— 或者 SELECT CONCAT(FirstName, ‘ ‘, LastName) AS FullName — MySQL/SQL Server 的字符串连接函数
FROM Employees;

— 查询员工的年薪,并将 Salary 列命名为 AnnualSalary
SELECT Salary AS AnnualSalary
FROM Employees;
“`


第四章:筛选数据 (WHERE)

WHERE 子句用于根据指定的条件过滤行,只返回满足条件的记录。

4.1 基本比较运算符

  • =: 等于
  • >: 大于
  • <: 小于
  • >=: 大于等于
  • <=: 小于等于
  • !=<>: 不等于

“`sql
— 查询薪水大于 60000 的员工
SELECT FirstName, LastName, Salary
FROM Employees
WHERE Salary > 60000;

— 查询名字是 ‘Alice’ 的员工
SELECT *
FROM Employees
WHERE FirstName = ‘Alice’;

— 查询不在 1 号部门的员工
SELECT EmployeeID, FirstName
FROM Employees
WHERE DepartmentID != 1; — 或者 DepartmentID <> 1
“`

4.2 AND, OR, NOT – 逻辑运算符

用于组合多个条件:

  • AND: 所有条件都必须为真。
  • OR: 至少一个条件为真。
  • NOT: 对条件取反。

“`sql
— 查询薪水大于 60000 并且 在 2 号部门的员工
SELECT FirstName, LastName, Salary, DepartmentID
FROM Employees
WHERE Salary > 60000 AND DepartmentID = 2;

— 查询薪水大于 70000 或者 名字是 ‘Bob’ 的员工
SELECT FirstName, LastName, Salary
FROM Employees
WHERE Salary > 70000 OR FirstName = ‘Bob’;

— 查询名字不是 ‘Alice’ 的员工
SELECT *
FROM Employees
WHERE NOT FirstName = ‘Alice’; — 等同于 WHERE FirstName != ‘Alice’
“`

可以使用括号 () 来控制条件的优先级。

sql
-- 查询 (部门为 1 且薪水大于 50000) 或者 (部门为 2 且薪水大于 60000) 的员工
SELECT *
FROM Employees
WHERE (DepartmentID = 1 AND Salary > 50000) OR (DepartmentID = 2 AND Salary > 60000);

4.3 LIKE – 模式匹配

用于在文本列中搜索指定的模式。

  • %: 百分号代表零个、一个或多个任意字符。
  • _: 下划线代表一个任意字符。

“`sql
— 查询姓氏以 ‘S’ 开头的员工
SELECT FirstName, LastName
FROM Employees
WHERE LastName LIKE ‘S%’;

— 查询邮箱包含 ‘example.com’ 的员工
SELECT EmployeeID, Email
FROM Employees
WHERE Email LIKE ‘%@example.com%’;

— 查询名字是四个字母,且第二个字母是 ‘l’ 的员工 (例如 ‘Alice’, ‘Blair’)
SELECT FirstName
FROM Employees
WHERE FirstName LIKE ‘_l%’;
“`

4.4 IN / NOT IN – 检查是否在集合中

检查列的值是否在给定的列表中。

“`sql
— 查询部门 ID 是 1 或 3 的员工
SELECT FirstName, LastName, DepartmentID
FROM Employees
WHERE DepartmentID IN (1, 3);

— 查询部门 ID 不是 1 或 3 的员工
SELECT FirstName, LastName, DepartmentID
FROM Employees
WHERE DepartmentID NOT IN (1, 3);
“`

4.5 BETWEEN / NOT BETWEEN – 检查范围

检查列的值是否在指定的范围内(包含边界值)。

“`sql
— 查询薪水在 50000 到 70000 之间的员工 (包含 50000 和 70000)
SELECT FirstName, LastName, Salary
FROM Employees
WHERE Salary BETWEEN 50000 AND 70000;

— 查询招聘日期不在 2022 年的员工
SELECT EmployeeID, HireDate
FROM Employees
WHERE HireDate NOT BETWEEN ‘2022-01-01’ AND ‘2022-12-31’;
“`

4.6 IS NULL / IS NOT NULL – 处理空值

NULL 是一个特殊的值,表示“未知”或“缺失”数据。不能使用 =!= 来比较 NULL,必须使用 IS NULLIS NOT NULL

“`sql
— 假设 Email 列允许为空,查询没有邮箱地址的员工
SELECT EmployeeID, FirstName
FROM Employees
WHERE Email IS NULL;

— 查询有邮箱地址的员工
SELECT EmployeeID, FirstName, Email
FROM Employees
WHERE Email IS NOT NULL;
“`


第五章:排序和限制结果

5.1 ORDER BY – 排序结果

用于对查询结果进行排序。

  • ASC: 升序(默认)。
  • DESC: 降序。

“`sql
— 按薪水从低到高排序所有员工
SELECT FirstName, LastName, Salary
FROM Employees
ORDER BY Salary ASC; — ASC 可以省略

— 按薪水从高到低排序所有员工
SELECT FirstName, LastName, Salary
FROM Employees
ORDER BY Salary DESC;

— 先按部门 ID 升序排序,如果部门 ID 相同,则按薪水降序排序
SELECT EmployeeID, FirstName, DepartmentID, Salary
FROM Employees
ORDER BY DepartmentID ASC, Salary DESC;
“`

5.2 LIMIT / TOP – 限制结果数量

用于限制查询返回的行数。语法可能因数据库系统而异。

  • LIMIT n (MySQL, PostgreSQL, SQLite): 返回最多 n 行。
  • LIMIT offset, n (MySQL, SQLite): 跳过 offset 行,然后返回最多 n 行。
  • LIMIT n OFFSET offset (PostgreSQL, SQLite): 标准 SQL 语法,跳过 offset 行,返回 n 行。
  • SELECT TOP n ... (SQL Server, MS Access): 返回前 n 行。
  • FETCH FIRST n ROWS ONLY (Oracle 12c+, Standard SQL): 返回前 n 行。

“`sql
— 查询薪水最高的 5 位员工 (假设使用 MySQL/PostgreSQL/SQLite)
SELECT FirstName, LastName, Salary
FROM Employees
ORDER BY Salary DESC
LIMIT 5;

— 查询薪水排名第 6 到第 10 的员工 (跳过前 5 条) (MySQL/SQLite)
SELECT FirstName, LastName, Salary
FROM Employees
ORDER BY Salary DESC
LIMIT 5, 5; — 或者 LIMIT 5 OFFSET 5 (PostgreSQL/SQLite)

— 查询薪水最高的 5 位员工 (SQL Server)
— SELECT TOP 5 FirstName, LastName, Salary
— FROM Employees
— ORDER BY Salary DESC;
“`


第六章:聚合与分组

聚合函数对一组值执行计算,并返回单个值。它们通常与 GROUP BY 子句一起使用。

6.1 常用聚合函数

  • COUNT(): 计算行数。
    • COUNT(*): 计算所有行数。
    • COUNT(column_name): 计算指定列中非 NULL 值的行数。
    • COUNT(DISTINCT column_name): 计算指定列中唯一值的数量(不含 NULL)。
  • SUM(column_name): 计算数值列的总和。
  • AVG(column_name): 计算数值列的平均值。
  • MIN(column_name): 找出列中的最小值。
  • MAX(column_name): 找出列中的最大值。

“`sql
— 计算员工总数
SELECT COUNT(*) AS TotalEmployees FROM Employees;

— 计算有邮箱地址的员工数量
SELECT COUNT(Email) AS EmployeesWithEmail FROM Employees;

— 计算共有多少个不同的部门 ID (在员工表中)
SELECT COUNT(DISTINCT DepartmentID) AS NumberOfDepartments FROM Employees;

— 计算所有员工的总薪水
SELECT SUM(Salary) AS TotalSalary FROM Employees;

— 计算员工的平均薪水
SELECT AVG(Salary) AS AverageSalary FROM Employees;

— 找出最低和最高薪水
SELECT MIN(Salary) AS MinSalary, MAX(Salary) AS MaxSalary FROM Employees;
“`

6.2 GROUP BY – 分组数据

GROUP BY 子句将具有相同值的行分组到摘要行中。通常与聚合函数一起使用,对每个组应用聚合函数。

重要规则: 当使用 GROUP BY 时,SELECT 列表中的所有非聚合函数列都 必须 包含在 GROUP BY 子句中。

“`sql
— 计算每个部门的员工人数
SELECT DepartmentID, COUNT(*) AS NumberOfEmployees
FROM Employees
GROUP BY DepartmentID;

— 计算每个部门的平均薪水
SELECT DepartmentID, AVG(Salary) AS AverageSalary
FROM Employees
GROUP BY DepartmentID;

— 计算每个部门的最高薪水,并按最高薪水降序排列
SELECT DepartmentID, MAX(Salary) AS MaxSalaryInDept
FROM Employees
GROUP BY DepartmentID
ORDER BY MaxSalaryInDept DESC;
“`

6.3 HAVING – 筛选分组

WHERE 子句在分组 之前 过滤行,而 HAVING 子句在分组 之后 过滤分组。HAVING 通常用于基于聚合函数的结果来筛选分组。

“`sql
— 找出员工人数超过 10 人的部门及其人数
SELECT DepartmentID, COUNT() AS NumberOfEmployees
FROM Employees
GROUP BY DepartmentID
HAVING COUNT(
) > 10;

— 找出平均薪水超过 65000 的部门及其平均薪水
SELECT DepartmentID, AVG(Salary) AS AverageSalary
FROM Employees
GROUP BY DepartmentID
HAVING AVG(Salary) > 65000
ORDER BY AverageSalary DESC;
“`

WHERE vs HAVING:

  • WHERE 作用于 ,在 GROUP BY 之前执行。
  • HAVING 作用于 分组,在 GROUP BY 之后执行。
  • WHERE 不能包含聚合函数,HAVING 可以。

sql
-- 找出部门 1 和 2 中,平均薪水超过 60000 的部门
SELECT DepartmentID, AVG(Salary) AS AverageSalary
FROM Employees
WHERE DepartmentID IN (1, 2) -- 先筛选出部门 1 和 2 的员工
GROUP BY DepartmentID -- 然后按部门分组
HAVING AVG(Salary) > 60000; -- 最后筛选出平均薪水符合条件的分组


第七章:操作数据 (DML – Data Manipulation Language)

DML 语句用于添加、修改和删除数据。

7.1 INSERT INTO – 插入数据

向表中添加新行。

语法 1: 指定列名和值 (推荐)

sql
INSERT INTO Employees (EmployeeID, FirstName, LastName, Email, HireDate, Salary, DepartmentID)
VALUES (101, 'Peter', 'Jones', '[email protected]', '2023-01-15', 62000, 2);

语法 2: 不指定列名 (要求按表定义的列顺序提供所有值)

sql
-- 假设 Employees 表的列顺序是 EmployeeID, FirstName, LastName, ...
INSERT INTO Employees
VALUES (102, 'Maria', 'Garcia', '[email protected]', '2023-02-20', 75000, 1);

强烈建议使用语法 1,因为它更清晰,并且即使表结构发生变化(例如添加新列或改变列顺序),只要你指定的列仍然存在,语句通常还能正常工作。

可以一次插入多行 (语法可能略有不同):

sql
-- MySQL/PostgreSQL/SQL Server
INSERT INTO Departments (DepartmentID, DepartmentName, Location) VALUES
(4, 'Marketing', 'New York'),
(5, 'Finance', 'London');

7.2 UPDATE – 更新数据

修改表中已存在的行的值。

极其重要: UPDATE 语句 必须 配合 WHERE 子句使用,否则会更新表中的 所有行

“`sql
— 将 EmployeeID 为 101 的员工的薪水更新为 65000
UPDATE Employees
SET Salary = 65000
WHERE EmployeeID = 101;

— 将部门 ID 为 2 的所有员工的地点更改 (假设信息冗余存储在 Employees 表,通常不推荐)
— UPDATE Employees
— SET Location = ‘Remote’ — 假设 Employees 表有 Location 列
— WHERE DepartmentID = 2;

— 可以同时更新多个列
UPDATE Employees
SET Email = ‘[email protected]’, Salary = 66000
WHERE EmployeeID = 101;
“`

安全提示: 在执行 UPDATEDELETE 之前,最好先用 SELECT 语句和相同的 WHERE 条件来检查将要被影响的行,确保条件正确无误。

sql
-- 先检查将要更新的行
SELECT * FROM Employees WHERE EmployeeID = 101;
-- 确认无误后,再执行 UPDATE
UPDATE Employees SET Salary = 65000 WHERE EmployeeID = 101;

7.3 DELETE FROM – 删除数据

从表中删除行。

极其重要: DELETE 语句 必须 配合 WHERE 子句使用,否则会删除表中的 所有行

“`sql
— 删除 EmployeeID 为 102 的员工记录
DELETE FROM Employees
WHERE EmployeeID = 102;

— 删除所有薪水低于 40000 的员工记录
DELETE FROM Employees
WHERE Salary < 40000;

— 删除表中所有数据 (小心!通常用 TRUNCATE TABLE 更快,但 TRUNCATE 不可回滚且不触发删除触发器)
— DELETE FROM Employees;
“`

再次强调: 执行 DELETE 前务必用 SELECT 检查 WHERE 条件。数据删除后通常很难恢复(除非有备份或事务回滚)。


第八章:定义数据 (DDL – Data Definition Language)

DDL 语句用于定义数据库的结构,如创建、修改和删除表。

8.1 CREATE TABLE – 创建表

创建新表,需要指定表名、列名、每列的数据类型以及约束(如 PRIMARY KEY, FOREIGN KEY, NOT NULL, UNIQUE)。

“`sql
— 创建 Departments 表
CREATE TABLE Departments (
DepartmentID INT PRIMARY KEY, — 部门 ID,整数类型,设为主键
DepartmentName VARCHAR(100) NOT NULL UNIQUE, — 部门名称,字符串,不能为空,且必须唯一
Location VARCHAR(100) — 地点,字符串
);

— 创建 Employees 表,包含外键约束
CREATE TABLE Employees (
EmployeeID INT PRIMARY KEY,
FirstName VARCHAR(50) NOT NULL,
LastName VARCHAR(50) NOT NULL,
Email VARCHAR(100) UNIQUE, — 邮箱,必须唯一
HireDate DATE,
Salary DECIMAL(10, 2), — 薪水,最多 10 位数,其中 2 位是小数
DepartmentID INT, — 部门 ID,整数
FOREIGN KEY (DepartmentID) REFERENCES Departments(DepartmentID) — 定义外键,关联到 Departments 表的 DepartmentID
— 可以添加其他约束,如 CHECK (Salary > 0)
);
“`

8.2 ALTER TABLE – 修改表

用于修改已存在的表结构,例如添加、删除或修改列,添加或删除约束。

“`sql
— 向 Employees 表添加一个名为 PhoneNumber 的列
ALTER TABLE Employees
ADD COLUMN PhoneNumber VARCHAR(20);

— 删除 Employees 表的 PhoneNumber 列 (某些数据库用 DROP COLUMN)
ALTER TABLE Employees
DROP COLUMN PhoneNumber;

— 修改 Employees 表中 Email 列的数据类型 (注意:这可能因数据已存在而失败或丢失数据)
— ALTER TABLE Employees
— ALTER COLUMN Email TYPE VARCHAR(150); — PostgreSQL 语法
— MODIFY COLUMN Email VARCHAR(150); — MySQL 语法

— 向 Employees 表添加一个检查约束,确保薪水大于 0
ALTER TABLE Employees
ADD CONSTRAINT chk_Salary CHECK (Salary > 0);

— 删除名为 chk_Salary 的约束
ALTER TABLE Employees
DROP CONSTRAINT chk_Salary;
“`

ALTER TABLE 的语法在不同数据库系统中差异较大,请查阅你所使用的数据库文档。

8.3 DROP TABLE – 删除表

永久删除 一个表及其所有数据。这个操作 无法撤销,请极度小心使用!

“`sql
— 删除 Employees 表
DROP TABLE Employees;

— 删除 Departments 表 (如果 Employees 表的外键约束还存在,可能需要先删除 Employees 表或外键约束)
DROP TABLE Departments;
“`


第九章:连接表 (JOIN) – 关系的力量

JOIN 子句用于根据两个或多个表之间的相关列(通常是主键和外键)将它们的行组合起来。这是关系型数据库的核心功能。

假设我们想查询每个员工的名字及其所在的部门名称。员工名字在 Employees 表,部门名称在 Departments 表,它们通过 DepartmentID 关联。

9.1 INNER JOIN (或 JOIN)

返回两个表中 满足连接条件 的所有行。如果某行在一个表中存在,但在另一个表中没有匹配的行,则不会包含在结果中。

sql
SELECT
e.FirstName, -- 使用别名 e 代表 Employees 表
e.LastName,
d.DepartmentName -- 使用别名 d 代表 Departments 表
FROM
Employees AS e -- 为 Employees 表设置别名 e
INNER JOIN -- 使用 INNER JOIN (INNER 可以省略,直接写 JOIN 效果一样)
Departments AS d -- 为 Departments 表设置别名 d
ON
e.DepartmentID = d.DepartmentID; -- 连接条件:两个表的 DepartmentID 相等

这个查询会列出所有有明确部门归属的员工及其部门名称。如果某个员工的 DepartmentIDNULL,或者对应的 DepartmentIDDepartments 表中不存在,该员工不会出现在结果里。

9.2 LEFT JOIN (或 LEFT OUTER JOIN)

返回 左表 (FROM 子句后的第一个表) 的所有行,以及右表中满足连接条件的匹配行。如果右表中没有匹配行,则结果中右表的列将显示为 NULL

sql
-- 查询所有员工的名字,以及他们所在的部门名称。如果员工没有分配部门,部门名称显示为 NULL。
SELECT
e.FirstName,
e.LastName,
d.DepartmentName
FROM
Employees AS e
LEFT JOIN -- 使用 LEFT JOIN
Departments AS d
ON
e.DepartmentID = d.DepartmentID;

这个查询会列出 所有 员工。对于那些 DepartmentIDNULL 或在 Departments 表中找不到对应 DepartmentID 的员工,DepartmentName 列会显示 NULL

9.3 RIGHT JOIN (或 RIGHT OUTER JOIN)

返回 右表 (JOIN 子句后的表) 的所有行,以及左表中满足连接条件的匹配行。如果左表中没有匹配行,则结果中左表的列将显示为 NULL

sql
-- 查询所有部门的名称,以及这些部门下的员工名字。如果某个部门没有员工,员工名字显示为 NULL。
SELECT
e.FirstName,
e.LastName,
d.DepartmentName
FROM
Employees AS e
RIGHT JOIN -- 使用 RIGHT JOIN
Departments AS d
ON
e.DepartmentID = d.DepartmentID;

这个查询会列出 所有 部门。对于那些没有任何员工归属的部门,FirstNameLastName 列会显示 NULL

9.4 FULL OUTER JOIN (或 FULL JOIN)

返回左表和右表中 所有 的行。如果某行在一个表中存在但在另一个表中没有匹配,则另一个表的列将显示为 NULL。(注意:MySQL 不直接支持 FULL OUTER JOIN,但可以通过 LEFT JOIN UNION RIGHT JOIN 模拟)。

sql
-- 查询所有员工和所有部门的信息,无论它们是否有匹配关系。
SELECT
e.FirstName,
e.LastName,
d.DepartmentName
FROM
Employees AS e
FULL OUTER JOIN -- 使用 FULL OUTER JOIN
Departments AS d
ON
e.DepartmentID = d.DepartmentID;

这个查询会包含:
* 有匹配部门的员工及其部门信息。
* 没有分配部门的员工(部门信息为 NULL)。
* 没有员工的部门(员工信息为 NULL)。

9.5 多表连接

你可以连接两个以上的表。

sql
-- 假设还有一个 Projects 表 (ProjectID, ProjectName, DepartmentID)
-- 查询参与了项目的员工姓名、项目名称和部门名称
SELECT
e.FirstName,
e.LastName,
p.ProjectName,
d.DepartmentName
FROM
Employees AS e
INNER JOIN
Departments AS d ON e.DepartmentID = d.DepartmentID
INNER JOIN
Projects AS p ON d.DepartmentID = p.DepartmentID; -- 假设项目直接关联到部门
-- (注意:更现实的场景可能需要一个 EmployeeProjects 关联表)


第十章:SQL 最佳实践与技巧

  1. 使用一致的大小写和格式化: 大写关键字,使用缩进和换行,使代码易于阅读。
  2. 给表和列起有意义的名称: 避免使用模糊或过于简短的名称。
  3. 添加注释: 解释复杂的查询逻辑或业务规则。
  4. 明确指定列名: 避免在生产代码中使用 SELECT *
  5. 谨慎使用 UPDATEDELETE: 务必带上 WHERE 子句,并在执行前用 SELECT 验证。
  6. 了解你的数据: 知道列的数据类型、是否允许 NULL、索引情况等,有助于写出更高效的查询。
  7. 使用别名: 在连接多个表或使用复杂列名时,使用别名可以使查询更简洁。
  8. 理解索引: 索引可以极大提高 SELECT 查询(尤其是带 WHEREJOIN 的查询)的速度,但会降低 INSERT, UPDATE, DELETE 的速度。了解何时以及如何创建索引很重要(这是更进阶的主题)。
  9. 避免在 WHERE 子句的列上使用函数: 例如 WHERE YEAR(HireDate) = 2023 可能导致索引失效。可以改写为 WHERE HireDate >= '2023-01-01' AND HireDate <= '2023-12-31'
  10. 定期备份数据库: 防止数据丢失。

第十一章:总结与后续学习

恭喜你!你已经学习了 SQL 的核心概念和最常用的命令。你现在应该能够:

  • 理解关系型数据库的基本结构。
  • 使用 SELECT 查询和筛选数据。
  • 对结果进行排序和限制。
  • 使用聚合函数和 GROUP BY 进行数据汇总。
  • 使用 INSERT, UPDATE, DELETE 操作数据。
  • 使用 CREATE, ALTER, DROP 定义表结构。
  • 使用 JOIN 连接多个表获取关联数据。

这只是开始!SQL 的世界非常广阔。接下来你可以:

  1. 大量练习: 在线平台(如 LeetCode, HackerRank, Codewars 的数据库题目)、安装本地数据库并创建自己的项目、或者找一些公开的数据集进行分析。实践是巩固知识的最佳途径。
  2. 深入特定数据库系统: 了解你常用数据库(如 PostgreSQL, MySQL, SQL Server, Oracle, SQLite)的特性、函数和优化技巧。它们的 SQL 方言略有不同。
  3. 学习高级 SQL 主题:
    • 子查询 (Subqueries): 查询嵌套在另一个查询中。
    • 窗口函数 (Window Functions): 在与当前行相关的行集上执行计算(如排名、移动平均)。
    • 公共表表达式 (Common Table Expressions – CTEs): 使用 WITH 子句创建临时命名的结果集,提高复杂查询的可读性。
    • 索引 (Indexing): 深入理解索引类型和优化策略。
    • 事务 (Transactions): 保证一系列操作要么全部成功,要么全部失败 (BEGIN, COMMIT, ROLLBACK)。
    • 存储过程 (Stored Procedures) 和 函数 (Functions): 在数据库中存储和重用 SQL 代码。
    • 触发器 (Triggers): 在特定表事件(如 INSERT, UPDATE, DELETE)发生时自动执行的 SQL 代码。
    • 视图 (Views): 存储的查询,可以像表一样使用。
  4. 数据库设计: 学习如何设计高效、规范化的数据库模式(Normalization)。
  5. 性能调优: 学习如何分析慢查询(如使用 EXPLAINANALYZE)并进行优化。

SQL 是一项非常有价值且应用广泛的技能。持续学习和实践,你将能够自如地驾驭数据,从中提取洞见。祝你在 SQL 的学习旅程中一切顺利!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部