学习SQL语句的必要性
在可视化工具中(如Navicat)许多基本的数据库操作可以通过图形用户界面(GUI)完成,而无需手动编写SQL语句。例如,你可以通过点击按钮来创建表、插入数据、查询记录等。这种方式对于不熟悉SQL的用户非常友好。
不过,虽然可视化工具可以简化操作,了解基本的SQL语句仍然很重要,因为:
- 灵活性:有时你可能需要执行更复杂的查询或操作,GUI可能无法满足所有需求。
- 性能优化:编写高效的SQL语句能够提高查询性能,尤其在处理大数据集时。
- 故障排除:当遇到问题时,查看SQL语句可以帮助你更好地理解发生了什么。
1. MySQL数据库基本特点
1.1 表格结构
-
表 是数据库的基本结构,所有数据都以表的形式存储。表由行 和列组成。
- 行(记录):每一行表示一条独立的数据记录。对于"学生"表而言,每一行都代表一个学生的具体信息,例如姓名、年龄等。
- 列(字段):每一列存储数据的特定属性。例如,列可以包括姓名、学号、年龄等,每列的数据类型决定了可以存储什么类型的信息(如整数、字符串等)。
例如,学生表的结构可以如下:
sqlCREATE TABLE 学生 ( 学号 INT PRIMARY KEY, 姓名 VARCHAR(50) NOT NULL, 年龄 INT CHECK (年龄 >= 0), 性别 ENUM('M', 'F') NOT NULL );
这里,
学号
是主键,确保每个学生的唯一性。年龄
字段使用了检查约束,确保值为非负数,性别
字段限制为特定值(男或女)。
1.2 数据完整性
-
完整性约束确保数据的准确性和一致性,主要包括:
- 主键 :每个表必须有一个主键,用于唯一标识每条记录。主键不能为NULL,并且值必须唯一。例如,学生表中的
学号
。 - 外键 :用于创建表与表之间的关系,确保引用的有效性。外键指向其他表的主键,例如在"订单"表中引用"客户"表的
客户ID
,确保每个订单都关联一个有效的客户。 - 唯一性约束:确保某列的值是唯一的,防止重复数据。例如,电子邮件地址字段通常会设置为唯一,以确保没有两个用户使用相同的电子邮件。
- 主键 :每个表必须有一个主键,用于唯一标识每条记录。主键不能为NULL,并且值必须唯一。例如,学生表中的
1.3 关系性
-
数据关系是关系型数据库的核心,允许通过外键在表之间建立关系。关系的类型主要包括:
- 一对一:一个实体对应一个实体。例如,一个学生可能只拥有一个学号,反之亦然。
- 一对多 :一个客户可以有多个订单。这种情况下,
客户ID
在订单表中作为外键引用客户表的主键。 - 多对多 :学生与课程之间的关系,通常需要通过中间表实现。中间表记录学生和课程之间的关系,如
学生ID
和课程ID
。
1.4 使用SQL
-
**结构化查询语言(SQL)**是与关系型数据库交互的主要语言,包括多个子语言:
- 数据定义语言(DDL) :用于创建和修改数据库结构,如创建表、删除表等。常用命令有
CREATE
、ALTER
和DROP
。 - 数据操作语言(DML) :用于对数据进行操作,包括插入、更新和删除。常用命令有
INSERT
、UPDATE
和DELETE
。 - 数据查询语言(DQL) :用于从数据库中查询数据,主要命令是
SELECT
,可以结合WHERE
、JOIN
等进行复杂查询。
- 数据定义语言(DDL) :用于创建和修改数据库结构,如创建表、删除表等。常用命令有
1.5 数据冗余和一致性
- 数据冗余:通过合理设计数据库结构,关系型数据库可以减少数据冗余。例如,通过使用外键关联表,而不是在每个表中重复存储相同的数据,减少数据存储的冗余。
- 数据一致性 :数据库管理系统通过事务处理来确保数据的一致性。事务是一组操作,要么全部成功,要么全部失败,这样可以避免数据不一致的情况。例如,在更新账户余额时,必须同时更新交易记录,确保两个操作一致。
1.6 事务管理
- ACID特性 确保事务的可靠性:
- 原子性(Atomicity):事务要么成功完成所有操作,要么不执行任何操作。例如,如果在转账过程中扣款成功但存款失败,则整个事务回滚。
- 一致性(Consistency):事务开始和结束时,数据库的状态必须保持一致,遵循所有定义的约束。
- 隔离性(Isolation):并发事务执行时,彼此之间的影响被隔离,确保一个事务的操作不会影响其他事务的结果。
- 持久性(Durability):一旦事务提交,结果就会被保存,即使系统崩溃也不会丢失。
2. 数据库与表的管理
2.1 创建和删除数据库
创建数据库
- 使用 CREATE DATABASE 语句可以创建新的数据库。数据库是存储表和其他对象的容器。
sql
CREATE DATABASE 数据库名;
- 注意事项 :
- 创建数据库后,可以使用
USE
命令切换到该数据库进行后续操作。 - 数据库名必须唯一,且遵循命名规则(如不能包含特殊字符)。
- 创建数据库后,可以使用
查看数据库
- 使用 SHOW DATABASE
删除数据库
- 使用 DROP DATABASE语句删除指定数据库。此操作将永久删除数据库及其所有表和数据。
sql
DROP DATABASE 数据库名;
- 注意事项 :
- 删除数据库时需谨慎,确保不再需要数据库中的数据。
2.2 创建和删除表
创建表的语法
sql
CREATE TABLE 表名 (
列名1 数据类型 [约束],
列名2 数据类型 [约束],
...
);
数据类型
数据类型定义了每列可以存储的数据的类型。常见的数据类型包括:
类别 | 数据类型 | 描述 |
---|---|---|
整数类型 | INT | 存储整数,范围为 -2,147,483,648 到 2,147,483,647。 |
TINYINT | 小整数,范围为 -128 到 127(或 0 到 255)。 | |
SMALLINT | 小整数,范围为 -32,768 到 32,767。 | |
MEDIUMINT | 中等大小的整数,范围为 -8,388,608 到 8,388,607。 | |
BIGINT | 大整数,范围为 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。 | |
浮点类型 | FLOAT | 单精度浮点数,用于存储带小数的数值。 |
DOUBLE | 双精度浮点数,存储精度更高的数值。 | |
字符串类型 | VARCHAR(n) | 可变长度字符串,n 为最大长度。 |
CHAR(n) | 定长字符串,总是存储 n 长度,不足时右侧填充空格。 |
|
TEXT | 存储长文本数据,最大长度为65,535字符。 | |
BLOB | 二进制大对象,用于存储二进制数据(如图像、文件等)。 | |
日期时间类型 | DATE | 存储日期,格式为 YYYY-MM-DD 。 |
TIME | 存储时间,格式为 HH:MM:SS 。 |
|
DATETIME | 存储日期和时间,格式为 YYYY-MM-DD HH:MM:SS 。 |
|
TIMESTAMP | 记录时间戳,表示从1970年1月1日到现在的秒数。 | |
布尔类型 | BOOLEAN | 存储布尔值,通常取值为0(假)或1(真)。 |
约束
约束用于限制列中可以存储的数据类型,确保数据的完整性和一致性。常见的约束包括:
约束 | 描述 |
---|---|
PRIMARY KEY | 唯一标识每条记录,值必须唯一且不为空。 |
FOREIGN KEY | 定义表之间的关系,确保引用的有效性。 |
NOT NULL | 确保列的值不能为空,强制要求提供该列的值。 |
UNIQUE | 确保列中的每个值都是唯一的,不允许重复。 |
CHECK | 确保列中的值满足特定条件。 |
DEFAULT | 为列指定默认值,如果插入时未提供该列的值,则使用默认值。 |
完整示例
sql
CREATE TABLE customers (
customer_id INT AUTO_INCREMENT PRIMARY KEY, -- 主键,唯一标识每条记录
first_name VARCHAR(50) NOT NULL, -- 姓名,不能为空
last_name VARCHAR(50) NOT NULL, -- 姓氏,不能为空
email VARCHAR(100) UNIQUE, -- 电子邮件,必须唯一
age INT CHECK (age >= 0), -- 年龄,必须大于或等于0
signup_date DATE DEFAULT CURRENT_DATE, -- 注册日期,默认值为当前日期
status ENUM('active', 'inactive') DEFAULT 'active', -- 状态,取值为 'active' 或 'inactive',默认值为 'active'
country_id INT, -- 国家ID,用于外键约束
FOREIGN KEY (country_id) REFERENCES countries(country_id) -- 外键,引用国家表的主键
);
约束说明
-
主键 (
PRIMARY KEY
):customer_id INT AUTO_INCREMENT PRIMARY KEY
- 该列是主键,确保每条记录的
customer_id
是唯一的,并且不能为NULL。AUTO_INCREMENT
表示每次插入新记录时,该值会自动增加。
-
非空约束 (
NOT NULL
):first_name VARCHAR(50) NOT NULL
- 该列存储客户的名字,不能为空,确保用户在插入数据时提供此信息。
-
唯一约束 (
UNIQUE
):email VARCHAR(100) UNIQUE
- 确保每个客户的电子邮件地址是唯一的,防止重复的电子邮件。
-
检查约束 (
CHECK
):age INT CHECK (age >= 0)
- 确保客户的年龄必须大于或等于0,以防止输入不合理的数据。
-
默认值约束 (
DEFAULT
):signup_date DATE DEFAULT CURRENT_DATE
- 如果在插入时未指定注册日期,则自动使用当前日期作为默认值。
-
枚举约束 (
ENUM
):status ENUM('active', 'inactive') DEFAULT 'active'
- 状态列只能取两个值之一('active' 或 'inactive'),默认值为 'active'。
-
外键约束 (
FOREIGN KEY
):FOREIGN KEY (country_id) REFERENCES countries(country_id)
- 该列用于引用其他表(如
countries
表)的主键country_id
,确保引用的有效性,防止孤立记录。
查看表结构
使用DESCRIBE 表名; 或 SHOW COLUMNS FROM; 表名; 命令查看表的结构。
sql
DESCRIBE 表名;
SHOW COLUMNS FROM 表名;
删除表
使用 DROP TABLE 表名; 语句删除指定的表。此操作将永久删除表及其所有数据。
sql
DROP TABLE 表名;
2.3 其他表管理操作
添加列
sql
ALTER TABLE 表名 ADD 列名 数据类型;
删除列
sql
ALTER TABLE 表名 DROP 列名;
修改列
sql
ALTER TABLE 表名 MODIFY 列名 新数据类型;
查看所有表
使用 SHOW TABLES 命令可以列出当前数据库中的所有表。
3. 数据操作语言(DML)
DML用于在数据库中操作数据,包括插入、查询、更新和删除记录。
3.1 数据的插入(INSERT INTO)
INSERT INTO语句用于向表中添加新记录。可以指定插入的列,也可以省略列名,默认插入所有列。
基本语法
sql
INSERT INTO 表名 (列1, 列2, ...) VALUES (值1, 值2, ...);
省略列名: 如果插入的值与表中的所有列顺序一致,可以省略列名。
sql
INSERT INTO customers
VALUES (NULL, 'Jane', 'Smith', 'jane.smith@example.com', 25, CURRENT_DATE, 'active', NULL);
批量插入: 可以一次插入多条记录。
java
INSERT INTO customers (first_name, last_name, email)
VALUES ('Alice', 'Johnson', 'alice.johnson@example.com'),
('Bob', 'Brown', 'bob.brown@example.com');
示例
sql
INSERT INTO customers (first_name, last_name, email, age)
VALUES ('John', 'Doe', 'john.doe@example.com', 30);
3.2 数据查询
SELECT 语句
SELECT
语句用于从表中查询数据。可以选择特定的列,也可以选择所有列。
基本语法
sql
SELECT 列1, 列2, ... FROM 表名;
选择所有列 : 使用*
表示选择所有列。
sql
SELECT * FROM customers;
示例
sql
SELECT first_name, last_name FROM customers;
使用 WHERE 进行条件查询
WHERE
子句用于过滤结果集,根据指定条件返回满足条件的记录。
基本语法
sql
SELECT 列1, 列2, ... FROM 表名 WHERE 条件;
多条件查询 : 可以使用逻辑运算符AND
、OR
连接多个条件。
sql
SELECT * FROM customers WHERE age >= 30 AND status = 'active';
示例
sql
SELECT * FROM customers WHERE age > 25;
3.3 数据更新与删除
UPDATE 的使用
UPDATE
语句用于修改表中已有的记录。使用SET
指定要修改的列及其新值。
基本语法
sql
UPDATE 表名 SET 列1 = 新值1, 列2 = 新值2 WHERE 条件;
不带 WHERE 的更新 : 如果省略WHERE
子句,则所有记录都会被更新,这通常是不建议的操作。
sql
UPDATE customers SET status = 'inactive';
示例
sql
UPDATE customers SET age = 31 WHERE first_name = 'John' AND last_name = 'Doe';
DELETE 的使用
DELETE
语句用于从表中删除记录。也可以使用WHERE
子句来指定删除的条件。
基本语法
sql
DELETE FROM 表名 WHERE 条件;
不带 WHERE 的删除 : 如果省略WHERE
子句,则会删除表中的所有记录。
sql
DELETE FROM customers;
示例
sql
DELETE FROM customers WHERE customer_id = 1;
3.4 条件判断语句
CASE 语句
CASE
语句允许在查询中进行条件判断,根据不同条件返回不同的结果。
示例:
sql
SELECT first_name,
CASE
WHEN age < 18 THEN '未成年'
WHEN age BETWEEN 18 AND 65 THEN '成年'
ELSE '老年'
END AS age_group
FROM customers;
IF 语句
在存储过程或函数中,可以使用 IF
语句进行条件判断。
示例:
sql
IF (条件) THEN
-- 执行某些操作
ELSE
-- 执行其他操作
END IF;
WHERE 子句
在查询中,WHERE
子句用于筛选符合特定条件的记录。
示例:
sql
SELECT * FROM orders WHERE order_status = '已完成';
4. 查询进阶
查询进阶主要涉及联接(JOIN)操作和子查询。这些技术使得从多个表中获取数据变得更加灵活和高效。
4.1 联接(JOIN)操作
内联接(INNER JOIN)
只返回两个表中匹配的记录。适用于需要基于共同字段进行数据比较的情况。
基本语法
sql
SELECT 列1, 列2, ...
FROM 表1
INNER JOIN 表2 ON 表1.列名 = 表2.列名;
选择字段:
SELECT 列1, 列2, ...
指定了要返回的字段(不一定要是表1中的字段,也可以是表一中的记录所对应表二中的字段),这些字段是基于查询结果中的记录而选择的。- 这一步并不涉及与表2的比较,而是决定最终结果中要展示哪些表1的字段。
连接条件:
ON 表1.列名 = 表2.列名
定义了如何比较表1和表2中的记录。- 只有当表1和表2中对应字段的值相等时,才会将这些匹配的记录包括在最终结果中。
结果展示:
- 最终展示的内容由
SELECT
指定的字段决定,展示的是符合连接条件的表1中的记录及其相关字段。
示例
sql
SELECT customers.first_name, orders.order_id
FROM customers
INNER JOIN orders ON customers.customer_id = orders.customer_id;
这里会首先去将表customers中的每一条记录的customer_id字段与表orders中的每一条记录的customer_id字段去比较,当两个表中的某两个记录的这两个字段相等了之后,就会将表customers的first_name字段与表orders的order_id字段记录下来,并将这一条记录的这两个字段显示在一行之中。
外联接(OUTER JOIN)
返回两个表中匹配的记录及不匹配的记录。外联接分为左外联接、右外联接和全外联接。
左外联接(LEFT JOIN)
返回左表的所有记录以及右表中匹配的记录。如果右表没有匹配,则返回NULL。
sql
SELECT customers.first_name, orders.order_id
FROM customers
LEFT JOIN orders ON customers.customer_id = orders.customer_id;
这种外连接跟上面内连接的唯一区别就是:
内连接所返回的字段是只有二者匹配上的记录的相应字段才会返回,而外连接则不管有没有匹配上,就比如说左外连接,左边的表customers的所有记录都会返回,只不过有些记录不存在所匹配的orders.order_id,会显示NULL。
first_name | order_id |
---|---|
John | 101 |
Jane | 103 |
Alice | NULL |
加入最后返回了以上的结果,就证明以上三条记录都在表customers之中,但是记录一和二中的相应字段由表orders中的相应字段与之匹配,找到了匹配,但是记录三Alice的customer_id字段没有在表orders中找到匹配上的customer_id,故也就没有对应的表orders中的order_id字段,所以最后显示NULL
右外联接(RIGHT JOIN)
返回右表的所有记录以及左表中匹配的记录。如果左表没有匹配,则返回NULL。
sql
SELECT customers.first_name, orders.order_id
FROM customers
RIGHT JOIN orders ON customers.customer_id = orders.customer_id;
全外联接(FULL JOIN)
返回两个表中的所有记录,包括不匹配的记录。如果一边没有匹配,返回NULL。
MySQL不直接支持FULL JOIN,可以通过UNION实现。
java
SELECT customers.first_name, orders.order_id
FROM customers
LEFT JOIN orders ON customers.customer_id = orders.customer_id
UNION
SELECT customers.first_name, orders.order_id
FROM customers
RIGHT JOIN orders ON customers.customer_id = orders.customer_id;
4.2 子查询
子查询是指在一个查询中嵌套另一个查询。可以用在SELECT、INSERT、UPDATE和DELETE语句中。
基本语法
sql
SELECT 列1, 列2
FROM 表名
WHERE 列名 = (SELECT 列名 FROM 另一表 WHERE 条件);
示例
假设我们有两个表,customers
和orders
,我们想查找下过订单的客户的名字。
sql
SELECT first_name, last_name
FROM customers
WHERE customer_id IN (SELECT customer_id FROM orders);
这个查询会选择所有在
customers
表中customer_id
存在于orders
表中的客户。(会在表orders中去检查每一条记录的customer_id
,然后去检查这些customer_id
是否在表customers的某一条记录的customer_id
字段中出现过,一旦出现过,就锁定表customers中的这些记录,然后把这些记录的first_name字段、last_name字段在一行中显示。
使用子查询的更新: 例如,更新所有下过订单的客户的状态为"active"。
sql
UPDATE customers
SET status = 'active'
WHERE customer_id IN (SELECT customer_id FROM orders);
使用子查询的删除: 删除没有下过订单的客户。
sql
DELETE FROM customers
WHERE customer_id NOT IN (SELECT customer_id FROM orders);
5. 事务管理
5.1 事务的基本概念
- 事务:一个事务是一个逻辑操作单元,由一组 SQL 语句组成,这些语句要么全部执行成功,要么全部不执行。事务通常用于确保数据库操作的完整性和一致性。
- 特点:事务在处理过程中涉及多个操作,可能包括插入、更新或删除等。当这些操作要么成功完成,要么在遇到错误时完全回滚。
5.2 ACID特性
ACID 是确保数据库事务可靠性的重要特性,包含以下四个方面:
-
原子性 (Atomicity):
- 事务中的所有操作要么全部完成,要么全部不执行。即使出现错误,系统也会确保事务不部分完成。
-
一致性 (Consistency):
- 事务的执行必须使数据库从一个一致的状态变更为另一个一致的状态。任何事务在完成后,都必须满足所有数据约束和规则。
-
隔离性 (Isolation):
- 事务的执行是相互独立的,一个事务的执行不应受到其他事务的影响。即使多个事务并发执行,它们的结果也应是合理的。
-
持久性 (Durability):
- 一旦事务被提交,其结果是永久的,即使系统崩溃,也不会丢失。数据库会确保已提交的数据被保留。
5.3 使用事务
在 MySQL 中,事务可以通过以下 SQL 命令管理:
- BEGIN:用于开始一个新事务。
- COMMIT:用于提交当前事务,使所有变更生效。
- ROLLBACK:用于撤销当前事务,回滚到事务开始前的状态。
5.3.1 BEGIN
开始一个新的事务,可以通过 START TRANSACTION
或 BEGIN
语句。
sql
START TRANSACTION;
5.3.2 COMMIT
提交事务,将所有在该事务中执行的操作保存到数据库中。
sql
COMMIT;
5.3.3 ROLLBACK
撤销事务,将数据库状态回滚到事务开始前的状态。
sql
ROLLBACK;
例子
下面是一个简单的事务管理示例:
sql
START TRANSACTION;
INSERT INTO accounts (account_id, balance) VALUES (1, 1000);
UPDATE accounts SET balance = balance - 500 WHERE account_id = 1;
-- 假设这里有一个条件判断
IF (某个条件) THEN
COMMIT; -- 提交事务
ELSE
ROLLBACK; -- 撤销事务
END IF;
6. 性能优化与索引
6.1 索引的基本概念
- 索引是一个数据结构,它允许数据库快速查找和访问表中的数据。就像书籍的目录,索引能快速定位信息的位置,减少数据检索的时间。
- 通常,索引会在特定列上创建,以加速基于这些列的查询。
- 使用索引可以显著提高查询效率,尤其是在大数据集上。没有索引的情况下,数据库可能需要扫描整个表以找到匹配的记录,这会消耗大量时间。
例子
假设 customers
表的 last_name
列包含如下数据:
customer_id | last_name |
---|---|
1 | Smith |
2 | Johnson |
3 | Smith |
4 | Williams |
创建索引后,索引可能看起来像这样:
last_name | pointer |
---|---|
Johnson | row 2 |
Smith | row 1, row 3 |
Williams | row 4 |
第二个表格就是为第一个表创建的索引,当你需要检索last_name字段为Smith的记录时,就可以通过索引帮你快速定位到 row1和row3。
6.2 索引的优势
6.2.1 数据结构优化
- 高效的数据结构:索引通常使用如 B 树或哈希表等高效的数据结构。这些结构允许数据库在对数时间内查找元素,而不是线性时间。例如,使用 B 树时,查找时间复杂度为 O(log n),而全表扫描的复杂度为 O(n)。
6.2.2 直接定位
- 直接访问记录:当你查询某个唯一值时,索引可以直接定位到该值在表中的位置,而不需要检查每一行。例如:
sql
SELECT * FROM customers WHERE customer_id = 123;
- 如果
customer_id
列有索引,数据库会直接根据索引找到customer_id = 123
的位置,而不需要遍历整张表。
6.2.3 减少 I/O 操作
- 减少磁盘 I/O:索引能够减少从磁盘读取的数据量。对于大表,减少读取的行数和字节数可以显著提高性能。即使数据的值是唯一的,读取索引中的信息要比读取整个表要少得多。
6.2.4 更好的支持范围查询
- 范围查询 :索引还可以优化范围查询。假设有一个唯一的索引在
customer_id
上,当执行类似于:
sql
SELECT * FROM customers WHERE customer_id BETWEEN 100 AND 200;
- 数据库可以迅速定位到范围的开始和结束,而不是逐行扫描。
6.2.5 排序和连接
- 排序优化:索引可以帮助数据库在排序时更快地定位数据。例如,如果你在某列上有索引,执行的排序操作将变得更快。
- 连接操作:在执行连接查询时,索引也能大幅提高效率,因为数据库能够快速找到连接条件所需的行。
总结来说,索引是凭借着其数据结构(通常是 B 树或哈希表)的优势,能够快速查询到所需的记录。(并不是因为单独建立了个表格就能够快速查询,关键还是因为独特的数据结构。
6.3 创建和使用索引
创建索引
单列索引:适用于只查询单一列的场景。
通过 SQL 语句在表的一个或多个列上创建索引,语法如下:
sql
CREATE INDEX index_name ON table_name (column1, column2, ...);
复合索引:适用于对多列的查询
这个复合索引将在 last_name
和 age
列上创建,以支持同时基于这两列的查询。
sql
CREATE INDEX idx_name_age ON customers (last_name, age);
你不需要知道这个索引的结构,你只需要知道它能够帮助你自动提升查询效率就可以了。
示例:
sql
CREATE INDEX idx_customer_name ON customers (last_name);
- 这行代码在
customers
表的last_name
列上创建了一个名为idx_customer_name
的索引。 - 当执行基于
last_name
列的查询时,数据库将使用这个索引来加速检索。
使用索引
创建索引后,数据库查询引擎会自动使用它来优化查询。示例:
sql
SELECT * FROM customers WHERE last_name = 'Smith';
- 在执行此查询时,如果
last_name
列上存在索引,数据库将首先查找索引,而不是全表扫描。这将大大提高查询速度。
注意事项
- 更新成本:每次数据插入、更新或删除时,索引也需更新,过多的索引会影响写入性能,因此应谨慎创建。
- 选择性:索引的选择性越高,即列中不同值的比例越高,索引的效果越好。例如,性别列的选择性较低(只有"男"和"女"两个值),而电子邮件列的选择性较高(每个值几乎都是唯一的)。
6.4 使用 EXPLAIN 了解查询性能
EXPLAIN 语句 :在查询前加上 EXPLAIN
可以帮助分析数据库如何执行该查询,包括索引的使用情况、行数估算等信息。
示例
sql
EXPLAIN SELECT * FROM customers WHERE last_name = 'Smith';
输出解读:
列名 | 描述 |
---|---|
id | 查询的标识符,用于区分多个查询。 |
select_type | 查询的类型: |
- SIMPLE:简单查询,不包含子查询或联合。 | |
- PRIMARY:主要查询。 | |
- UNION:联合查询。 | |
table | 正在访问的表名。 |
type | 连接类型,表示访问表的方式: |
- ALL:全表扫描,性能较差。 | |
- index:使用索引扫描,性能较好。 | |
- ref:使用非唯一索引查找,性能较好。 | |
- eq_ref:使用唯一索引查找,性能最佳。 | |
possible_keys | 可能使用的索引列表。 |
key | 实际使用的索引。 |
rows | 预估需要扫描的行数,数字越小,查询性能通常越好。 |
就是系统会帮你去模拟这一次的查询,并给你返回关于这次模拟查询的各种信息,返回格式大致如下:
id | select_type | table | type | possible_keys | key | rows |
---|---|---|---|---|---|---|
1 | SIMPLE | customers | ref | idx_last_name | idx_last_name | 5 |
7. 数据备份与恢复
7.1 使用 mysqldump
备份数据
mysqldump
工具是 MySQL 提供的一个命令行工具,用于备份数据库。它将数据库中的数据导出为 SQL 脚本文件,该文件包含创建数据库和插入数据的 SQL 语句。
备份命令示例
sql
mysqldump -u 用户名 -p 数据库名 > 备份文件.sql
- -u 用户名:指定连接数据库的用户名。
- -p:提示输入密码。
- 数据库名:要备份的数据库的名称。
- 备份文件.sql:导出数据的文件名。
备份所有数据库
sql
mysqldump -u 用户名 -p --all-databases > all_databases_backup.sql
7.2 从备份中恢复数据
要恢复数据,通常使用 MySQL 的命令行工具将备份文件中的 SQL 脚本导入到数据库中。
恢复命令示例
sql
mysql -u 用户名 -p 数据库名 < 备份文件.sql
- 数据库名:要恢复数据的目标数据库。
- 备份文件.sql:包含备份数据的文件。
恢复所有数据库
如果备份了所有数据库,可以使用:
sql
mysql -u 用户名 -p < all_databases_backup.sql
重要注意事项
- 备份频率:根据业务需求定期备份,以防数据丢失。
- 备份文件安全:确保备份文件存储在安全的位置,防止未经授权访问。
- 测试恢复:定期测试备份恢复过程,确保在需要时能成功恢复数据。
8. 用户权限管理
8.1 创建用户
sql
CREATE USER 'username'@'host' IDENTIFIED BY 'password';
-
固定格式:
- CREATE USER:关键字,用于创建新用户。
- IDENTIFIED BY:关键字,用于指定用户的密码。
-
可更改字段:
- 'username' :表示要创建的用户名,例如
'john_doe'
。 - 'host' :表示用户可以从哪个主机连接。常见的值有:
'localhost'
:仅允许从本地连接。'%'
:允许从任何主机连接。
- 'password' :表示用户的密码,例如
'securePassword123'
。
- 'username' :表示要创建的用户名,例如
示例
sql
CREATE USER 'john_doe'@'localhost' IDENTIFIED BY 'securePassword123';
8.2 删除用户
sql
DROP USER 'username'@'host';
-
固定格式:
- DROP USER:关键字,用于删除用户。
- 'username'@'host':表示要删除的用户名及其连接主机。
-
可更改字段:
- 'username':表示要删除的用户名。
- 'host':表示要删除的用户可以从哪个主机连接。
8.3 查看所有用户
sql
SELECT User, Host FROM mysql.user;
8.4 修改用户密码
sql
SET PASSWORD FOR 'username'@'host' = PASSWORD('new_password');
-
固定格式:
- SET PASSWORD FOR:关键字,用于修改用户密码。
- PASSWORD:关键字,表示后面的内容是用户的新密码。
-
可更改字段:
- 'username':表示要修改密码的用户名。
- 'host':表示要修改密码的用户可以从哪个主机连接。
- 'new_password':表示用户的新密码。
示例
sql
SET PASSWORD FOR 'john_doe'@'localhost' = PASSWORD('newSecurePassword456');
8.5 授予权限
sql
GRANT ALL PRIVILEGES ON database_name.* TO 'username'@'host';
-
固定格式:
- GRANT:关键字,用于授予权限。
- ON:关键字,指明权限的适用范围。
- TO:关键字,指定授予权限的用户。
-
可更改字段:
- ALL PRIVILEGES :可以更改为具体权限,例如:
SELECT
:读取权限。INSERT
:插入权限。UPDATE
:更新权限。DELETE
:删除权限。
- database_name:表示要授予权限的数据库名。
- 'username':表示要授予权限的用户名。
- 'host':表示要授予权限的用户可以从哪个主机连接。
- ALL PRIVILEGES :可以更改为具体权限,例如:
示例
sql
REVOKE INSERT ON my_database.* FROM 'john_doe'@'localhost';
8.6 撤销权限
sql
REVOKE ALL PRIVILEGES ON database_name.* FROM 'username'@'host';
-
固定格式:
- REVOKE:关键字,用于撤销权限。
- ON:关键字,指明权限的适用范围。
- FROM:关键字,指定撤销权限的用户。
-
可更改字段:
- ALL PRIVILEGES :可以更改为具体权限,例如:
SELECT
、INSERT
、UPDATE
、DELETE
。
- database_name:表示要撤销权限的数据库名。
- 'username':表示要撤销权限的用户名。
- 'host':表示要撤销权限的用户可以从哪个主机连接。
- ALL PRIVILEGES :可以更改为具体权限,例如:
示例
sql
REVOKE INSERT ON my_database.* FROM 'john_doe'@'localhost';
8.7 显示用户权限
sql
SHOW GRANTS FOR 'username'@'host';
-
固定格式:
- SHOW GRANTS:关键字,用于显示用户权限。
- FOR:关键字,指定要查看权限的用户。
-
可更改字段:
- 'username':表示要查看权限的用户名。
- 'host':表示要查看权限的用户可以从哪个主机连接。
示例
sql
SHOW GRANTS FOR 'john_doe'@'localhost';
8.8 刷新权限
sql
FLUSH PRIVILEGES;
- 固定格式 :
- FLUSH PRIVILEGES:关键字,用于刷新权限,使更改生效。
9. 视图
9.1 视图的定义
视图可以看作是一个"窗口",通过这个窗口,你可以查看数据库中数据的某种特定视图。它并不是存储实际的数据,而是存储一个查询的定义。当你通过视图查询数据时,数据库会根据这个定义自动生成结果。
视图的特点
-
虚拟表:
- 视图并不存储数据,只是保存了一段 SQL 查询。当你查询视图时,数据库实时运行这个查询来获取数据。
-
简化查询:
- 你可以把复杂的 SQL 查询封装成一个视图。这样,你只需要通过视图名称来获取结果,不必每次都写复杂的查询语句。
-
增强安全性:
- 通过视图,你可以限制用户对某些敏感数据的访问。只让他们看到视图中定义的内容,而不直接访问基础表。
-
便于维护:
- 如果基础表的结构发生变化,只要视图的查询逻辑依然有效,就不需要修改使用这个视图的地方。
使用场景
-
汇总数据:比如,你想查看每个产品的总销售额,可以创建一个视图来展示这些信息,而不必每次都写聚合查询。
-
多表联合:如果你需要从多个表中获取数据,可以把这个联合查询封装在视图中,方便后续使用。
-
限制数据访问:你可以为不同的用户角色创建不同的视图,确保他们只能看到自己需要的数据
视图的存在就像将一系列复杂的查询条件封装成一个函数。下次你需要使用这个查询逻辑时,只需调用这个视图,而无需再写一大堆语句,便能获得相同的查询结果。
9.2 创建视图
创建视图的语法
sql
CREATE VIEW 视图名 AS
SELECT 列1, 列2, ...
FROM 表名
WHERE 条件;
-
固定格式:
- CREATE VIEW:关键字,用于创建视图。
- AS:关键字,表示后面是视图的定义。
-
可更改字段:
- 视图名 :表示视图的名称,例如
customer_orders
。 - 列1, 列2, ...:表示需要从表中选择的列。
- 表名:表示从哪个表中选择数据。
- 条件:表示过滤数据的条件(可选)。
- 视图名 :表示视图的名称,例如
示例
sql
CREATE VIEW customer_orders AS
SELECT customers.first_name, orders.order_id
FROM customers
INNER JOIN orders ON customers.customer_id = orders.customer_id
WHERE orders.order_date > '2024-01-01';
此示例创建一个名为 customer_orders
的视图,显示2024年之后所有客户及其订单信息。
9.3 查询视图
创建视图后,可以像查询普通表一样查询视图:
sql
SELECT * FROM 视图名;
示例
sql
SELECT * FROM customer_orders;
此示例查询 customer_orders
视图中的所有数据。
9.3 修改视图
如果需要修改视图,可以使用 CREATE OR REPLACE VIEW
:
sql
CREATE OR REPLACE VIEW 视图名 AS
SELECT 列1, 列2, ...
FROM 表名
WHERE 新的条件;
示例
sql
CREATE OR REPLACE VIEW customer_orders AS
SELECT customers.first_name, orders.order_id, orders.order_date
FROM customers
INNER JOIN orders ON customers.customer_id = orders.customer_id
WHERE orders.order_date > '2024-01-01';
此示例修改 customer_orders
视图,新增了订单日期。
9.4 删除视图
如果不再需要视图,可以使用 DROP VIEW
删除:
sql
DROP VIEW 视图名;
示例
sql
DROP VIEW customer_orders;
此示例删除 customer_orders
视图。
10.存储过程
定义
存储过程是一个存储在数据库中的SQL语句集合,可以封装复杂的业务逻辑并通过调用来执行。存储过程使得代码更加模块化,易于重用和维护。
存储过程和视图类似,都是对SQL语句进行封装然后调用,区别是视图只对查询的SQL语句进行封装,而存储过程可以封装所有类型的SQL语句。
10.1 存储过程的特点
-
预编译:
- 存储过程在创建时进行编译,后续调用时直接执行,无需再次编译,从而提高执行效率。
-
参数化:
- 可以接收输入参数(
IN
)、输出参数(OUT
)和输入输出参数(INOUT
),使其更加灵活。
- 可以接收输入参数(
-
封装业务逻辑:
- 可以将复杂的操作和逻辑封装在存储过程中,简化调用。
-
安全性:
- 用户可以被授予执行存储过程的权限,而不必直接访问底层表,增强了数据的安全性。
-
事务控制:
- 存储过程可以包含事务处理,确保数据一致性。
10.2 创建存储过程
创建语法
使用 CREATE PROCEDURE
语句创建存储过程。基本语法如下:
sql
DELIMITER //
CREATE PROCEDURE procedure_name ([parameter_list])
BEGIN
-- SQL statements
END //
DELIMITER ;
procedure_name
:存储过程的名称。parameter_list
:参数列表,可以包含多个参数,使用IN
、OUT
或INOUT
指定参数类型。BEGIN ... END
:包裹存储过程的逻辑。
示例
以下是一个简单的存储过程示例,用于根据客户ID查询订单:
sql
DELIMITER //
CREATE PROCEDURE GetCustomerOrders(IN customerId INT)
BEGIN
SELECT * FROM orders WHERE customer_id = customerId;
END //
DELIMITER ;
- 该存储过程名为
GetCustomerOrders
,接收一个输入参数customerId
。
10.3 调用存储过程
使用 CALL
语句调用存储过程,语法如下:
sql
CALL procedure_name([parameter_values]);
示例
调用上述存储过程查询客户ID为1的订单:
sql
CALL GetCustomerOrders(1);
10.4 更新存储过程
如果需要修改存储过程,可以使用 DROP
删除原有存储过程,然后重新创建。例如:
sql
DROP PROCEDURE IF EXISTS GetCustomerOrders;
DELIMITER //
CREATE PROCEDURE GetCustomerOrders(IN customerId INT)
BEGIN
SELECT * FROM orders WHERE customer_id = customerId;
END //
DELIMITER ;
10.5 使用输出参数
存储过程也可以使用输出参数。以下是一个示例:
sql
DELIMITER //
CREATE PROCEDURE GetOrderCount(IN customerId INT, OUT orderCount INT)
BEGIN
SELECT COUNT(*) INTO orderCount FROM orders WHERE customer_id = customerId;
END //
DELIMITER ;
调用时:
sql
CALL GetOrderCount(1, @count);
SELECT @count;
@count
是一个用户定义的会话变量,MySQL 允许使用以@
开头的变量,这些变量在整个会话中有效。- SELECT COUNT(*):这是一个查询,用于计算满足条件的记录数量。
- INTO orderCount :将计算的结果(在这里是记录的数量)存储到变量
orderCount
中。INTO
关键字常用于存储过程和函数中,以便将查询结果直接赋值给变量。这种方法避免了使用临时表或额外的查询步骤,简化了数据处理流程。 - 在调用时传入的参数@count,在原来的存储过程中代表了形参orderCount,然后INTO orderCount又把值传入了orderCount,相当于就是传入了@count,所以最后SELECT @count;才会返回相应的值。
11. 触发器
触发器(Trigger)是一种特殊的存储程序,自动在特定事件发生时执行。它们可以用于确保数据完整性、自动维护审计日志、实现复杂的业务逻辑等。
触发器的基本概念
- 自动执行:触发器在特定事件(如插入、更新或删除操作)发生时自动执行,而无需显式调用。
- 关联表:触发器与特定的表关联,只有在该表上执行操作时,触发器才会被触发。
触发器的类型
- BEFORE 触发器:在插入、更新或删除操作之前执行。适用于数据验证或修改。
- AFTER 触发器:在插入、更新或删除操作之后执行。适用于日志记录或更新相关表。
触发器的创建
创建触发器时,需要指定触发条件(如 BEFORE INSERT
或 AFTER DELETE
)、触发器名称、关联的表以及触发时执行的操作。
示例
sql
CREATE TRIGGER before_insert_customers
BEFORE INSERT ON customers
FOR EACH ROW
BEGIN
SET NEW.created_at = NOW(); -- 设置当前时间为创建时间
END;
在这个示例中,创建了一个名为 before_insert_customers
的触发器,在每次向 customers
表插入新记录之前,会自动设置 created_at
字段为当前时间。
触发器的使用场景
- 数据验证:在插入或更新数据之前进行检查,确保数据符合一定的标准。
- 自动生成值:自动设置某些字段的值,如创建时间或更新时间。
- 审计日志:记录数据变更历史,例如在某个表中插入记录时,同时在审计表中添加一条记录。
- 复杂的业务逻辑:实现一些复杂的业务规则,例如自动更新相关表的数据。
注意事项
- 性能影响:触发器会增加额外的处理时间,特别是在涉及大量数据操作时。
- 调试困难:调试触发器比调试普通的 SQL 查询更复杂,因为触发器在背景中执行。
- 递归调用:要小心避免触发器之间的递归调用,这可能导致无限循环。