MySQL
从零开始的MySQL学习
第一节 数据库
重点:数据库通过SQL等标准语言进行动作,数据库的概念、分类,数据管理系统(操纵和管理数据库的大型软件)
数据库(Database)
是按照数据结构来组织、存储和管理数据的仓库。它允许用户高效地访问、插入、更新和删除数据。
数据库的相关概念
1. 数据(Data)
数据是描述事物的符号记录,可以是数字、文字、图形、声音等。在数据库中,数据是被组织和存储的基本对象。
2. 数据库管理系统(DBMS)
数据库管理系统(Database Management System,DBMS)是位于用户和操作系统之间的一层数据管理软件。它为用户或应用程序提供访问数据库的方法,包括数据定义、数据操纵、数据控制等功能。
3. 数据库系统(DBS)
数据库系统(Database System,DBS)是指在计算机系统中引入数据库后的系统。它由数据库、数据库管理系统、数据库管理员、硬件平台和软件平台组成。
4. 数据模型(Data Model)
数据模型是数据库中数据特征的描述,是数据库系统的核心和基础。常见的数据模型包括:
- 层次模型(Hierarchical Model):数据以树形结构组织,每个节点有且仅有一个父节点(根节点除外)。
- 网状模型(Network Model):数据以网状结构组织,允许一个节点有多个父节点。
- 关系模型(Relational Model):数据以表格形式组织,每个表称为一个关系,表中的每一行称为一个元组,每一列称为一个属性。
5. 关系数据库(Relational Database)
关系数据库是基于关系模型的数据库。它使用表格来存储数据,每个表格由行(记录)和列(字段)组成。关系数据库是最常用的一种数据库类型,具有简单、易懂、易于维护等优点。
6. SQL(Structured Query Language)
SQL 是一种用于管理和操作关系数据库的标准编程语言。它提供了数据定义(DDL)、数据操纵(DML)、数据控制(DCL)等功能。常见的 SQL 语句包括:
- 数据定义:
CREATE TABLE
、ALTER TABLE
、DROP TABLE
等。 - 数据操纵:
INSERT
、SELECT
、UPDATE
、DELETE
等。 - 数据控制:
GRANT
、REVOKE
等。
7. 数据库设计(Database Design)
数据库设计是创建一个满足用户需求的数据库的过程。它包括需求分析、概念设计(如 E-R 图)、逻辑设计(如关系模式)、物理设计(如存储结构)等步骤。
8. 事务(Transaction)
事务是数据库中一系列的操作,这些操作要么全部成功,要么全部失败。事务具有四个特性,即 ACID 特性:
- 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不完成。
- 一致性(Consistency):事务执行前后,数据库的状态保持一致。
- 隔离性(Isolation):并发执行的事务之间相互隔离,互不干扰。
- 持久性(Durability):事务一旦提交,其结果就是永久的,即使系统故障也不会丢失。
9. 索引(Index)
索引是数据库中用于加速数据检索的一种数据结构。它类似于书籍的目录,通过索引可以快速定位到数据的位置,从而提高查询效率。
10. 视图(View)
视图是基于一个或多个表的虚拟表,其内容由 SQL 查询定义。视图不存储数据,而是存储查询语句,当查询视图时,数据库会动态生成数据。
数据库的类型
-
关系数据库(Relational Database)
- 代表:MySQL、PostgreSQL、Oracle、SQL Server。
- 特点:使用表格存储数据,支持 SQL 语言,具有强大的事务处理能力。
-
非关系数据库(NoSQL Database)
- 代表:MongoDB、Redis、Cassandra。
- 特点:不使用表格存储数据,支持灵活的数据模型,适合处理大规模分布式数据。
-
内存数据库(In-Memory Database)
- 代表:Redis、Memcached。
- 特点:数据存储在内存中,访问速度极快,适合需要快速读写的应用。
-
对象关系数据库(Object-Relational Database)
- 代表:PostgreSQL。
- 特点:结合了关系数据库和面向对象数据库的特点,支持复杂数据类型和方法。
MySQL数据模型:
- 客户端—>DBMS—>数据库—>表
第二节 SQL
SQL分类
前四个分类为基础
1. 数据定义语言(DDL,Data Definition Language)
DDL 提供了定义和修改数据库结构的语句,包括创建、修改和删除数据库对象(如表、索引、视图、触发器等)。
CREATE
:创建新的数据库对象。ALTER
:修改现有数据库对象的结构。DROP
:删除数据库对象。TRUNCATE
:快速删除表中的所有行,但不删除表本身。
2. 数据操纵语言(DML,Data Manipulation Language)
DML 提供了访问和修改数据库中数据的语句,包括插入、更新和删除数据。
INSERT
:向表中插入新数据。UPDATE
:修改表中的现有数据。DELETE
:从表中删除数据。
3. 数据查询语言(DQL,Data Query Language)
DQL 主要包括 SELECT
语句,用于查询数据库中的数据。
SELECT
:从数据库中检索数据。
4. 数据控制语言(DCL,Data Control Language)
DCL 提供了控制数据库访问权限的语句,包括授予和撤销用户对数据库对象的访问权限。
GRANT
:授予用户或角色对数据库对象的特定权限。REVOKE
:撤销用户或角色的权限。
5. 事务控制语言(TCL,Transaction Control Language)
TCL 提供了管理数据库事务的语句,确保数据的一致性和完整性。
BEGIN
或START TRANSACTION
:开始一个新的事务。COMMIT
:提交事务,使自事务开始以来对数据库的所有更改成为永久性更改。ROLLBACK
:回滚事务,撤销自事务开始以来对数据库的所有更改。SAVEPOINT
:设置事务的保存点,允许部分回滚。
6. 数据访问语言(DAL,Data Access Language)
虽然不是 SQL 标准的一部分,但 DAL 通常指的是用于访问数据库的编程接口和工具,如 JDBC、ODBC、OLE DB 等。
7. 存储过程和函数
存储过程和函数是一组为了完成特定功能的 SQL 语句,它们被预先编写并存储在数据库中,可以通过一个调用语句来执行。
CREATE PROCEDURE
:创建存储过程。CREATE FUNCTION
:创建函数。
8. 触发器(Triggers)
触发器是数据库中的一种特殊类型的存储过程,它们在特定的数据库事件(如 INSERT、UPDATE 或 DELETE)发生时自动执行。
CREATE TRIGGER
:创建触发器。
具体操作
一.DDL
1. 数据查询(SELECT)
SELECT column1, column2, ...
FROM table_name
WHERE condition
ORDER BY column;
2. 数据插入(INSERT)
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);
3. 数据更新(UPDATE)
UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition;
4. 数据删除(DELETE)
DELETE FROM table_name
WHERE condition;
5. 创建表(CREATE TABLE)
CREATE TABLE table_name (column1 datatype,column2 datatype,...
);
6. 修改表结构(ALTER TABLE)
ALTER TABLE table_name
ADD column_name datatype;
7. 删除表(DROP TABLE)
DROP TABLE table_name;
8. 创建索引(CREATE INDEX)
CREATE INDEX index_name
ON table_name (column);
9. 删除索引(DROP INDEX)
DROP INDEX index_name;
10. 创建视图(CREATE VIEW)
CREATE VIEW view_name AS
SELECT column1, column2, ...
FROM table_name
WHERE condition;
11. 删除视图(DROP VIEW)
DROP VIEW view_name;
12. 创建存储过程(CREATE PROCEDURE)
CREATE PROCEDURE procedure_name (parameter_list)
BEGIN-- SQL statements
END;
13. 调用存储过程(CALL)
CALL procedure_name(value1, value2, ...);
14. 删除存储过程(DROP PROCEDURE)
DROP PROCEDURE procedure_name;
15. 创建触发器(CREATE TRIGGER)
CREATE TRIGGER trigger_name
BEFORE|AFTER INSERT|UPDATE|DELETE ON table_name
FOR EACH ROW
BEGIN-- SQL statements
END;
16. 删除触发器(DROP TRIGGER)
DROP TRIGGER trigger_name;
17. 设置权限(GRANT)
GRANT SELECT, INSERT ON database_name.table_name TO 'username'@'host';
18. 撤销权限(REVOKE)
REVOKE SELECT, INSERT ON database_name.table_name FROM 'username'@'host';
19. 创建数据库(CREATE DATABASE)
CREATE DATABASE database_name;
20. 删除数据库(DROP DATABASE)
DROP DATABASE database_name;
列出数值类型
类型 | 大小(字节) | 有符号范围 | 无符号范围 | 精度(小数点后位数) | 用途 |
---|---|---|---|---|---|
TINYINT | 1 | -128 到 127 | 0 到 255 | 不适用 | 小范围整数 |
SMALLINT | 2 | -32768 到 32767 | 0 到 65535 | 不适用 | 中等范围整数 |
MEDIUMINT | 3 | -8388608 到 8388607 | 0 到 16777215 | 不适用 | 大范围整数 |
INT | 4 | -2147483648 到 2147483647 | 0 到 4294967295 | 不适用 | 通用整数 |
BIGINT | 8 | -9223372036854775808 到 9223372036854775807 | 0 到 18446744073709551615 | 不适用 | 极大范围整数 |
FLOAT | 4 | ±1.17549435 × 10^−38 到 ±3.40282347 × 10^38 | ±1.17549435 × 10^−38 到 ±3.40282347 × 10^38 | 不适用 | 单精度浮点数 |
DOUBLE | 8 | ±2.2250738585072014 × 10^−308 到 ±1.7976931348623157 × 10^308 | ±2.2250738585072014 × 10^−308 到 ±1.7976931348623157 × 10^308 | 不适用 | 双精度浮点数 |
DECIMAL | 动态 | 取决于数值和精度 | 取决于数值和精度 | 可指定 | 精确的十进制表示 |
BIT | 动态 | 不适用 | 不适用 | 不适用 | 位字段 |
BOOLEAN | 1 | 不适用 | 不适用 | 不适用 | 布尔值 |
字符串类型
类型 | 大小限制 | 描述 |
---|---|---|
CHAR(n) | 0 到 255 字节 | 固定长度字符串。不足部分用空格填充。 |
VARCHAR(n) | 0 到 65,535 字节 | 可变长度字符串。存储时只占用实际需要的空间加上一个长度字节。 |
TINYTEXT | 0 到 255 字节 | 短文本字符串。 |
TEXT | 0 到 65,535 字节 | 长文本字符串。 |
MEDIUMTEXT | 0 到 16,777,215 字节 | 较长文本字符串。 |
LONGTEXT | 0 到 4,294,967,295 字节 | 非常长的文本字符串。 |
ENUM | 1 或 2 字节 | 枚举类型,由一组预定义的字符串值组成。 |
SET | 1、2、3、4 或 8 字节 | 集合类型,由一组预定义的字符串值组成,可以存储多个值。 |
BINARY(n) | 0 到 255 字节 | 固定长度的二进制字符串。 |
VARBINARY(n) | 0 到 65,535 字节 | 可变长度的二进制字符串。 |
TINYBLOB | 0 到 255 字节 | 短二进制数据。 |
BLOB | 0 到 65,535 字节 | 长二进制数据。 |
MEDIUMBLOB | 0 到 16,777,215 字节 | 较长二进制数据。 |
LONGBLOB | 0 到 4,294,967,295 字节 | 非常长的二进制数据。 |
VALCHAR相对于CHAR性能差一些 |
日期类型
类型 | 格式/范围 | 描述 |
---|---|---|
DATE | YYYY-MM-DD | 存储日期,格式为年-月-日。 |
范围:0001-01-01 至 9999-12-31 | ||
TIME | HH:MM:SS | 存储时间,格式为小时:分钟:秒。 |
范围:-838:59:59 至 838:59:59 | ||
DATETIME | YYYY-MM-DD HH:MM:SS | 存储日期和时间,格式为年-月-日 时:分:秒。 |
范围:0001-01-01 00:00:00 至 9999-12-31 23:59:59 | ||
TIMESTAMP | YYYY-MM-DD HH:MM:SS | 存储日期和时间,通常用于自动生成的时间戳。 |
范围:1970-01-01 00:00:01 UTC 至 2038-01-19 03:14:07 UTC | ||
范围:0001-01-01 00:00:00 至 9999-12-31 23:59:59(有符号) | ||
YEAR | YYYY | 存储年份,格式为四位数字。 |
范围:1901 至 2155 |
举例
现在我们创建一张表举例
CREATE TABLE EMP (ID INT,WORKNO VARCHAR(10) COMMENT '工号',NAME VARCHAR(10) COMMENT '姓名',GENDER CHAR(1) COMMENT '性别',AGE TINYINT UNSIGNED COMMENT '年龄',IDCARD CHAR(18) COMMENT '身份证',ENTRYDATE DATE COMMENT '入职时间'
) COMMENT '员工表';
--一些操作
ALTER TABLE EMP ADD NICKNAME VARCHAR(20) COMMENT '昵称';
ALTER TABLE EMP MODIFY ;
ALTER TABLE EMP CHANGE NICKNAME USERNANE VARCHAR (30) COMMENT'用户名';
ALTER TABLE EMP DROP USERNAME;
ALTER TABLE EMP RENAME TO EMPLOY;
DROP TABLE IF EXISTS EMPLOY;//删除表,所有数据也跟着被删除
TRUNCATE TABLE EMPLOY;//留下空表,删除所有数据
注意事项:
中英文的输入如引号分号等,括号不是大括号,COMMENT后面要有一个空格。
二.MySQL图形化
三.DML(增删改)
INSERT INTO EMP (ID,WORKNO,NAME,GENDER,AGE,IDCARD,ENTRYDATE) VALUES (1,'1','A','男',10,'123456789012345678','2000-01-01'); SELECT * FROM EMP;UPDATE EMP SET NAME = 'YOU',GENDER = '女' WHERE ID = 1;UPDATE EMP SET NAME = 'YOUNG' WHERE ID = 1;DELETE FROM EMP WHERE GENDER = '女';
四.DQL(查询)
核心SELECT语句
SELECT NAME,WORKNO,AGE FROM ENP;SELECT * FROM EMP WHERE ID = 1;实际中尽量不要用*,不直观,如此处直接把所有要查询的输出来就行SELECT NAME AS 'XINGMIN' FROM EMP;//AS可以省略SELECT DISTINCT NAME FROM EMP;//查询没有重复
比较运算符
-
等于 (
=
):- 检查两个值是否相等。
- 示例:
SELECT * FROM table_name WHERE column_name = 'value';
-
不等于 (
<>
或!=
):- 检查两个值是否不相等。
- 示例:
SELECT * FROM table_name WHERE column_name <> 'value';
-
大于 (
>
):- 检查左边的值是否大于右边的值。
- 示例:
SELECT * FROM table_name WHERE column_name > 10;
-
小于 (
<
):- 检查左边的值是否小于右边的值。
- 示例:
SELECT * FROM table_name WHERE column_name < 5;
-
大于等于 (
>=
):- 检查左边的值是否大于或等于右边的值。
- 示例:
SELECT * FROM table_name WHERE column_name >= 20;
-
小于等于 (
<=
):- 检查左边的值是否小于或等于右边的值。
- 示例:
SELECT * FROM table_name WHERE column_name <= 50;
-
IS NULL:
- 检查值是否为 NULL。
- 示例:
SELECT * FROM table_name WHERE column_name IS NULL;
-
IS NOT NULL:
- 检查值是否不为 NULL。
- 示例:
SELECT * FROM table_name WHERE column_name IS NOT NULL;
-
BETWEEN:
- 检查一个值是否在两个指定的值之间(包括边界值)。
- 示例:
SELECT * FROM table_name WHERE column_name BETWEEN 10 AND 20;
-
IN:
- 检查一个值是否包含在一组指定的值中。
- 示例:
SELECT * FROM table_name WHERE column_name IN ('value1', 'value2', 'value3');
-
LIKE:
- 检查一个值是否符合指定的模式(使用通配符
%
和_
)。 - 示例:
SELECT * FROM table_name WHERE column_name LIKE '%value%';
- 检查一个值是否符合指定的模式(使用通配符
-
REGEXP 或 RLIKE(取决于数据库系统):
- 检查一个值是否符合指定的正则表达式模式。
- 示例:
SELECT * FROM table_name WHERE column_name REGEXP '^value.*';
逻辑运算符
-
AND:
- 逻辑“与”运算符。只有当所有条件都为
true
时,结果才为true
。 - 示例:
SELECT * FROM table_name WHERE condition1 AND condition2;
- 逻辑“与”运算符。只有当所有条件都为
-
OR:
- 逻辑“或”运算符。只要有一个条件为
true
,结果就为true
。 - 示例:
SELECT * FROM table_name WHERE condition1 OR condition2;
- 逻辑“或”运算符。只要有一个条件为
-
NOT:
- 逻辑“非”运算符。用于反转条件的逻辑值,将
true
转换为false
,将false
转换为true
。 - 示例:
SELECT * FROM table_name WHERE NOT condition;
- 逻辑“非”运算符。用于反转条件的逻辑值,将
-
XOR:
- 逻辑“异或”运算符。当且仅当两个条件的逻辑值不同时,结果为
true
。 - 示例:
SELECT * FROM table_name WHERE condition1 XOR condition2;
(注意:并非所有数据库系统都支持 XOR 运算符。)
- 逻辑“异或”运算符。当且仅当两个条件的逻辑值不同时,结果为
使用逻辑运算符的注意事项:
- 优先级:逻辑运算符具有不同的优先级。通常,
NOT
的优先级最高,其次是AND
,然后是OR
。可以使用括号来明确指定运算顺序。 - 括号:在复杂的逻辑表达式中,使用括号来分组条件,确保逻辑运算的顺序符合你的预期。
- 性能:在某些情况下,逻辑运算符的使用可能会影响查询性能。合理地构建查询条件可以提高查询效率。
示例
假设有一个名为 employees
的表,包含员工的 age
和 salary
列,可以使用逻辑运算符来构建复杂的查询:
-- 选择年龄大于30且工资高于5000的员工
SELECT * FROM employees WHERE age > 30 AND salary > 5000;-- 选择年龄小于30或工资低于5000的员工
SELECT * FROM employees WHERE age < 30 OR salary < 5000;-- 选择工资高于5000但年龄不大于40的员工
SELECT * FROM employees WHERE salary > 5000 AND NOT (age > 40);-- 选择年龄大于30或工资高于5000,但不是两者都满足的员工
SELECT * FROM employees WHERE (age > 30 XOR salary > 5000);
基本查询
-
查询所有列:
SELECT * FROM table_name;
-
查询特定列:
SELECT column1, column2 FROM table_name;
-
查询符合条件的行:
SELECT * FROM table_name WHERE condition;
-
查询结果排序:
SELECT * FROM table_name ORDER BY column ASC; -- 升序 SELECT * FROM table_name ORDER BY column DESC; -- 降序
-
查询结果分页(MySQL):
SELECT * FROM table_name LIMIT 10 OFFSET 20; -- 从第21行开始,取10行
聚合查询
-
使用聚合函数:
SELECT COUNT(*) FROM table_name; -- 计算行数 SELECT SUM(column) FROM table_name; -- 计算列的总和 SELECT AVG(column) FROM table_name; -- 计算列的平均值 SELECT MAX(column), MIN(column) FROM table_name; -- 找出列的最大值和最小值
-
分组查询:
SELECT column, COUNT(*) FROM table_name GROUP BY column;
-
过滤分组后的结果(使用
HAVING
子句):SELECT column, COUNT(*) FROM table_name GROUP BY column HAVING COUNT(*) > 1;
连接查询
-
内连接(
INNER JOIN
):SELECT * FROM table1 INNER JOIN table2 ON table1.column_name = table2.column_name;
-
左连接(
LEFT JOIN
):SELECT * FROM table1 LEFT JOIN table2 ON table1.column_name = table2.column_name;
-
右连接(
RIGHT JOIN
):SELECT * FROM table1 RIGHT JOIN table2 ON table1.column_name = table2.column_name;
子查询
-
在
SELECT
语句中使用子查询:SELECT * FROM table_name WHERE column_name IN (SELECT column_name FROM other_table);
-
在
FROM
子句中使用子查询:SELECT * FROM (SELECT column_name FROM table_name) AS derived_table;
联合查询
-
合并多个查询结果(使用
UNION
):SELECT column_name FROM table1 UNION SELECT column_name FROM table2;
-
合并多个查询结果并去重(使用
UNION DISTINCT
或DISTINCT
):SELECT DISTINCT column_name FROM table1 UNION DISTINCT SELECT DISTINCT column_name FROM table2;
直接说似乎不易理解其执行顺序,这里给出样例
-
FROM:
- 首先,DBMS 处理
FROM
子句,这涉及到从指定的表或子查询中检索数据。
- 首先,DBMS 处理
-
JOIN:
- 如果查询中包含
JOIN
操作,DBMS 将执行表的连接操作,根据指定的连接条件合并表中的数据。
- 如果查询中包含
-
WHERE:
WHERE
子句在FROM
和JOIN
之后执行,用于过滤结果集,只保留满足条件的行。
-
GROUP BY:
- 如果查询包含
GROUP BY
子句,DBMS 将对WHERE
过滤后的结果集进行分组。
- 如果查询包含
-
HAVING:
HAVING
子句在GROUP BY
之后执行,用于过滤分组后的结果,只保留满足特定条件的组。
-
SELECT:
SELECT
子句指定了查询需要返回的列。在这个阶段,DBMS 会从处理过的数据中选择指定的列。
-
DISTINCT:
- 如果查询中包含
DISTINCT
关键字,DBMS 将从SELECT
列表中移除重复的行。
- 如果查询中包含
-
ORDER BY:
- 最后,
ORDER BY
子句对查询结果进行排序,按照指定的列和顺序返回最终结果。
- 最后,
-
LIMIT:
- 在某些数据库系统中,如 MySQL,
LIMIT
子句用于限制返回的行数,通常在ORDER BY
之后执行。
- 在某些数据库系统中,如 MySQL,
示例查询及其执行顺序
考虑以下查询:
SELECT DISTINCT column1, column2
FROM table1
INNER JOIN table2 ON table1.id = table2.id
WHERE column1 > 100
GROUP BY column2
HAVING COUNT(*) > 5
ORDER BY column1 DESC
LIMIT 10;
执行顺序如下:
- FROM:从
table1
和table2
中检索数据。 - INNER JOIN:根据
table1.id = table2.id
连接表。 - WHERE:过滤
column1 > 100
的行。 - GROUP BY:按
column2
分组。 - HAVING:过滤
COUNT(*) > 5
的组。 - SELECT:选择
column1
和column2
。 - DISTINCT:移除重复的行。
- ORDER BY:按
column1 DESC
排序。 - LIMIT:限制结果为前 10 行。
五.DCL
DCL 包括的常用 SQL 命令主要有 GRANT
和 REVOKE
。
当然,以下是一些使用 DCL(数据控制语言)的 GRANT
和 REVOKE
命令的例子,这些命令用于管理用户权限:
GRANT 命令的例子
-
授予用户对特定表的 SELECT 和 INSERT 权限:
GRANT SELECT, INSERT ON database_name.table_name TO 'username'@'host';
这条命令授予名为
username
的用户对database_name
数据库中table_name
表的查询(SELECT)和插入(INSERT)权限。 -
授予用户对所有表的 SELECT 权限:
GRANT SELECT ON database_name.* TO 'username'@'host';
这条命令授予用户对
database_name
数据库中所有表的查询(SELECT)权限。 -
授予用户对数据库的所有权限,并允许其将权限授予其他用户:
GRANT ALL PRIVILEGES ON database_name.* TO 'username'@'host' WITH GRANT OPTION;
这条命令授予用户所有权限,并允许该用户将这些权限授予其他用户。
-
授予用户对数据库中所有新创建的表和存储过程的权限:
GRANT SELECT, INSERT ON database_name.* TO 'username'@'host' WITH GRANT OPTION;
这条命令授予用户对现有和未来创建的所有表的 SELECT 和 INSERT 权限。
REVOKE 命令的例子
-
撤销用户对特定表的 INSERT 权限:
REVOKE INSERT ON database_name.table_name FROM 'username'@'host';
这条命令撤销了之前授予
username
用户对database_name
数据库中table_name
表的 INSERT 权限。 -
撤销用户对数据库的所有权限:
REVOKE ALL PRIVILEGES ON database_name.* FROM 'username'@'host';
这条命令撤销了用户对
database_name
数据库的所有权限。 -
撤销用户授予其他用户的权限:
REVOKE GRANT OPTION ON database_name.* FROM 'username'@'host';
这条命令撤销了用户
username
授予其他用户权限的能力。 -
级联撤销用户对特定表的所有权限:
REVOKE ALL PRIVILEGES ON database_name.table_name FROM 'username'@'host' CASCADE;
这条命令不仅撤销了用户对
table_name
表的所有权限,还撤销了该用户可能已经授予其他用户的权限。
在使用这些命令时,需要替换 database_name
、table_name
、username
和 host
为实际的数据库名、表名、用户名和主机名。此外,确保有足够的权限来授予或撤销权限,通常是数据库管理员(DBA)或具有相应权限的用户。
第三节 函数
字符串函数
1. CONCAT()
用于连接两个或多个字符串。
SELECT CONCAT('Hello', ' ', 'World') AS ConcatenatedString;
结果:Hello World
2. LENGTH() 或 CHAR_LENGTH()
返回字符串的长度。
SELECT LENGTH('Hello World') AS StringLength;
结果:11
3. SUBSTRING() 或 SUBSTR()
从字符串中提取子字符串。
SELECT SUBSTRING('Hello World', 1, 5) AS SubString;
结果:Hello
4. POSITION() 或 INSTR()
返回子字符串在字符串中的位置。
SELECT POSITION('World' IN 'Hello World') AS Position;
结果:7
5. REPLACE()
替换字符串中的某些字符。
SELECT REPLACE('Hello World', 'World', 'SQL') AS ReplacedString;
结果:Hello SQL
6.LTRIM() 和 RTRIM()
分别去除字符串左侧和右侧的空格。
SELECT LTRIM(' Hello World') AS LeftTrimmed, RTRIM('Hello World ') AS RightTrimmed;
结果:Hello World
和 Hello World
7. TRIM()
去除字符串两侧的空格。
SELECT TRIM(' Hello World ') AS TrimmedString;
结果:Hello World
8. UPPER() 和 LOWER()
分别将字符串转换为大写和转换为小写。
SELECT UPPER('Hello World') AS UpperString, LOWER('Hello World') AS LowerString;
结果:HELLO WORLD
和 hello world
9. LEFT() 和 RIGHT()
分别从字符串的左侧和右侧提取指定数量的字符。
SELECT LEFT('Hello World', 5) AS LeftString, RIGHT('Hello World', 5) AS RightString;
结果:Hello
和 World
10. REVERSE()
反转字符串。
SELECT REVERSE('Hello World') AS ReversedString;
结果:dlroW olleH
在 SQL 中,数值函数用于执行各种数值计算,包括数学运算、四舍五入、取整、随机数生成等。以下是一些常见的数值函数:
数值函数
1. ABS(x)
返回指定数值的绝对值。
SELECT ABS(-10) AS AbsoluteValue;
结果:10
2. CEILING(x)
返回大于或等于给定数值的最小整数。
SELECT CEILING(3.14) AS CeilingValue;
结果:4
3. FLOOR(x)
返回小于或等于给定数值的最大整数。
SELECT FLOOR(3.14) AS FloorValue;
结果:3
4. ROUND(x)
将给定数值四舍五入到最接近的整数。
SELECT ROUND(3.14159) AS RoundedValue;
结果:3
5. TRUNCATE(x, d)
截断一个数字到指定的小数位数。
SELECT TRUNCATE(3.14159, 2) AS TruncatedValue;
结果:3.14
6. POWER(x, y) 或 EXP(x)
返回 x 的 y 次幂。
SELECT POWER(2, 3) AS PowerValue;
结果:8
7. SQRT(x)
返回给定数值的平方根。
SELECT SQRT(9) AS SquareRoot;
结果:3
8. LOG(x)
返回给定数值的自然对数(以 e 为底)。
SELECT LOG(2.718281828) AS NaturalLogarithm;
结果:1
9. LN(x)
返回给定数值的自然对数(以 e 为底)。
SELECT LN(2.718281828) AS NaturalLogarithm;
结果:1
10. MOD(x, y)
返回 x 除以 y 的余数。
SELECT MOD(7, 3) AS Modulus;
结果:1
11. SIGN(x)
返回给定数值的符号,正数返回 1,负数返回 -1,零返回 0。
SELECT SIGN(-5) AS SignValue;
结果:-1
12. PI()
返回圆周率 π 的值。
SELECT PI() AS PiValue;
结果:3.141592653589793
13. RANDOM() 或 RAND()
返回一个随机浮点数,范围从 0 到 1。
SELECT RANDOM() AS RandomValue;
结果:0.123456789
(示例值,实际值随机)
14. TRUNCATETABLE(x, y)
截断数值 x 到 y 位小数(与 TRUNCATE 相似)。
SELECT TRUNCATETABLE(123.45678, 2) AS TruncatedValue;
结果:123.45
日期函数
1. CURRENT_DATE 或 CURDATE()
返回当前日期。
SELECT CURRENT_DATE;
2. CURRENT_TIME 或 CURTIME()
返回当前时间。
SELECT CURRENT_TIME;
3. NOW() 或 CURRENT_TIMESTAMP
返回当前日期和时间。
SELECT NOW();
4. DATE_ADD(date, INTERVAL expr type)
给定日期加上一个时间间隔。
SELECT DATE_ADD('2024-01-01', INTERVAL 10 DAY);
结果:2024-01-11
5. DATE_SUB(date, INTERVAL expr type)
从给定日期减去一个时间间隔。
SELECT DATE_SUB('2024-01-15', INTERVAL 10 DAY);
结果:2024-01-05
6. DATEDIFF(date1, date2)
返回两个日期之间的天数差异。
SELECT DATEDIFF('2024-01-05', '2024-01-01');
结果:4
7. DATE_FORMAT(date, format)
将日期格式化为字符串。
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s');
结果:格式化后的当前日期和时间字符串
8. STR_TO_DATE(string, format)
将字符串转换为日期,根据提供的格式。
SELECT STR_TO_DATE('2024-06-13', '%Y-%m-%d');
结果:2024-06-13
9. ADDDATE(date, interval)
给定日期加上一个时间间隔(年、月、日等)。
SELECT ADDDATE('2024-01-01', INTERVAL 1 MONTH);
结果:2024-02-01
10. LAST_DAY(date)
返回一个月份的最后一天。
SELECT LAST_DAY('2024-01-15');
结果:2024-01-31
11. MONTH(), DAY(), YEAR(), HOUR(), MINUTE(), SECOND()
从日期时间值中提取特定的部分。
SELECT YEAR('2024-06-13 12:30:00'), MONTH('2024-06-13 12:30:00'), DAY('2024-06-13 12:30:00');
结果:2024 6 13
流程函数
1. 控制流程函数
这些函数主要用于控制 SQL 语句的执行流程,虽然它们并不直接类似于编程语言中的流程控制结构。
-
CASE 表达式:类似于编程语言中的
if-else
语句,用于基于条件选择不同的值。SELECT column1,CASE WHEN condition1 THEN result1WHEN condition2 THEN result2ELSE result3END AS new_column FROM table_name;
-
IF() 函数(在某些数据库系统中可用):直接返回条件为真的结果。
SELECT IF(column1 > 0, 'Positive', 'Non-positive') AS sign FROM table_name;
2. 窗口函数
窗口函数允许你对一组行执行计算,这些行与当前行有某种关系(例如,它们可能是查询结果集中当前行的一部分)。
-
ROW_NUMBER():为结果集中的每行分配一个唯一的连续整数。
SELECT ROW_NUMBER() OVER (ORDER BY column1) AS row_num, column1 FROM table_name;
-
RANK():为结果集中的每行分配一个排名,相同值的行将获得相同的排名。
SELECT RANK() OVER (ORDER BY column1 DESC) AS rank, column1 FROM table_name;
-
DENSE_RANK():类似于
RANK()
,但排名之间没有间隔。SELECT DENSE_RANK() OVER (ORDER BY column1 DESC) AS dense_rank, column1 FROM table_name;
3. 递归查询
在支持递归查询的数据库系统中(如 PostgreSQL、SQL Server 等),可以使用递归公用表表达式(CTE)来实现递归。
- 递归 CTE:用于执行递归查询。
WITH RECURSIVE cte (column1, column2) AS (SELECT column1, column2 FROM table_name WHERE conditionUNION ALLSELECT t.column1, t.column2 FROM table_name t INNER JOIN cte ON condition ) SELECT * FROM cte;
第四节 约束
1. 主键约束(PRIMARY KEY)
确保列(或列的组合)中的每个值都是唯一的,并且不允许 NULL 值。
CREATE TABLE Employees (EmployeeID int NOT NULL,FirstName varchar(50),LastName varchar(50),Email varchar(100),PRIMARY KEY (EmployeeID)
);
2. 外键约束(FOREIGN KEY)
用于在两个表之间建立链接,并确保引用的数据的完整性。
CREATE TABLE Orders (OrderID int NOT NULL,EmployeeID int,OrderDate date,PRIMARY KEY (OrderID),FOREIGN KEY (EmployeeID) REFERENCES Employees(EmployeeID)
);
3. 唯一约束(UNIQUE)
保证列中的所有值都是不同的。
CREATE TABLE Students (StudentID int NOT NULL,FirstName varchar(50),LastName varchar(50),Email varchar(100) UNIQUE
);
4. 非空约束(NOT NULL)
确保列中的值不能为 NULL。
CREATE TABLE Products (ProductID int NOT NULL,ProductName varchar(100) NOT NULL,Price decimal(10, 2)
);
5. 检查约束(CHECK)
确保列中的值满足特定的条件。
CREATE TABLE Products (ProductID int NOT NULL,ProductName varchar(100) NOT NULL,Price decimal(10, 2),CHECK (Price > 0)
);
6. 默认值约束(DEFAULT)
当没有为列提供值时,将自动填充一个默认值。
CREATE TABLE Employees (EmployeeID int NOT NULL,FirstName varchar(50),LastName varchar(50),Email varchar(100),Salary decimal(10, 2) DEFAULT 50000,PRIMARY KEY (EmployeeID)
);
7. 级联约束(CASCADE)
通常与外键约束一起使用,用于定义当被引用的键被更新或删除时,应该对引用的键执行什么操作。
CREATE TABLE Orders (OrderID int NOT NULL,EmployeeID int,OrderDate date,PRIMARY KEY (OrderID),FOREIGN KEY (EmployeeID) REFERENCES Employees(EmployeeID) ON DELETE CASCADE ON UPDATE CASCADE
);
8. 索引(INDEX)
虽然不是标准的约束类型,但索引用于优化查询性能,可以强制数据的排序和查找。
CREATE INDEX idx_lastname ON Employees (LastName);
第五节 多表查询
一.多表查询关系
多表查询通常涉及到两个或多个表之间的关系,这些关系可以是显式的(如通过外键约束定义)或隐式的(在查询中指定)。
1. 一对一关系(One-to-One Relationship)
在一对一关系中,一个表中的每一行只与另一个表中的一行相关联。这种关系较少见,因为通常可以将这两个表合并为一个表。
示例:
SELECT a.column1, b.column2
FROM table1 a
INNER JOIN table2 b ON a.id = b.id;
2. 一对多关系(One-to-Many Relationship)
在一对多关系中,一个表中的一行可以与另一个表中的多行相关联。这是最常见的关系类型,例如,一个客户可以有多个订单。
示例:
SELECT customers.name, orders.order_date
FROM customers
INNER JOIN orders ON customers.id = orders.customer_id;
3. 多对多关系(Many-to-Many Relationship)
在多对多关系中,一个表中的多行可以与另一个表中的多行相关联。这种关系通常需要通过一个中间表(也称为联结表或关联表)来实现。
示例:
假设有两个表 students
和 courses
,它们通过中间表 enrollments
相关联。
SELECT students.name, courses.name
FROM students
INNER JOIN enrollments ON students.id = enrollments.student_id
INNER JOIN courses ON enrollments.course_id = courses.id;
二.连接类型
在多表查询中,可以使用不同类型的连接来检索数据:
- 内连接(INNER JOIN):只返回两个表中匹配的行。
- 左连接(LEFT JOIN):返回左表的所有行,即使右表中没有匹配的行。
- 右连接(RIGHT JOIN):返回右表的所有行,即使左表中没有匹配的行。
- 全外连接(FULL OUTER JOIN):返回两个表中任一表的所有行,如果某一侧没有匹配的行,则该侧的结果为
NULL
。 - 内连接(INNER JOIN)是 SQL 中最常用的连接类型之一,用于结合两个或多个表中相关的数据记录。内连接只返回两个表中匹配条件的行,即两个表中都有对应值的记录。
内连接
内连接的基本语法如下:
SELECT column_list
FROM table1
INNER JOIN table2
ON table1.common_field = table2.common_field;
column_list
:你想从连接的结果中选择的列列表。table1
、table2
:你想要连接的表名。common_field
:两个表中用于匹配行的共同列名。
注
不建议隐式内连接,无关键字而指代不清晰,如有需求再看
示例
假设有两个表:Employees
(员工表)和Departments
(部门表),它们通过 DepartmentID
相关联。
Employees 表:
- EmployeeID(员工ID)
- FirstName(名字)
- LastName(姓氏)
- DepartmentID(部门ID)
Departments 表:
- DepartmentID(部门ID)
- DepartmentName(部门名称)
要获取每位员工及其所在部门的名称,可以使用以下 SQL 语句:
SELECT Employees.FirstName, Employees.LastName, Departments.DepartmentName
FROM Employees
INNER JOIN Departments ON Employees.DepartmentID = Departments.DepartmentID;
操作说明
-
选择相关列:从两个表中选择需要显示的列。在这个例子中,选择了员工的名字、姓氏和部门名称。
-
指定连接条件:使用
ON
子句指定连接条件,即Employees.DepartmentID = Departments.DepartmentID
。这表示只返回两个表中DepartmentID
匹配的行。 -
返回匹配行:查询结果将只包含
DepartmentID
在两个表中都存在的行。
外连接
外连接(OUTER JOIN)是 SQL 中的一种连接类型,它用于返回两个表中满足连接条件的行,以及不满足条件的行。外连接包括左外连接(LEFT JOIN)、右外连接(RIGHT JOIN)和全外连接(FULL JOIN)三种类型。
左外连接(LEFT JOIN)返回左表(LEFT JOIN 左边的表)的所有行,即使右表中没有匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列为空值。例如,以下语句展示了如何使用左外连接来获取员工及其部门名称,即使某些员工没有分配部门:
SELECT e.name, d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id;
左外连接的结果特点是包含左表所有记录,右表无匹配时显示NULL。
右外连接(RIGHT JOIN)与左外连接相反,它返回右表(RIGHT JOIN 右边的表)的所有记录,即使左表中没有匹配的行。如果右表中的记录在左表中没有匹配,则左表的列会显示为 NULL。例如:
SELECT e.name, d.department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.department_id;
右外连接的结果特点是包含右表所有记录,左表无匹配时显示NULL。
全外连接(FULL JOIN)结合了左外连接和右外连接的结果,包含两个表中的所有记录,无论是否有匹配。无匹配的部分显示为 NULL。例如:
SELECT * FROM employees
LEFT JOIN departments USING(department_id)
UNION
SELECT * FROM employees
RIGHT JOIN departments USING(department_id);
全外连接的结果特点是包含两个表所有记录,无匹配部分显示NULL。
左外连接(LEFT JOIN)和右外连接(RIGHT JOIN)是 SQL 中用于执行多表查询的两种不同的连接类型。每种连接类型都有其特定的用途和结果集,但在某些情况下,一个左外连接可以通过调整为右外连接,反之亦然。这种转换可以通过改变连接的表和条件来实现。
左外连接转换为右外连接
左外连接返回左表的所有行,即使右表中没有匹配的行。如果右表中没有匹配的行,则结果中右表的列会是 NULL
。
要将左外连接转换为右外连接,需要交换两个表的位置,并调整 ON
子句中的条件。
示例:
假设有两个表 employees
和 departments
,其中 employees
表有一个外键 department_id
指向 departments
表的主键 id
。
左外连接:
SELECT employees.name, departments.department_name
FROM employees
LEFT JOIN departments ON employees.department_id = departments.id;
转换为右外连接:
SELECT departments.department_name, employees.name
FROM departments
RIGHT JOIN employees ON employees.department_id = departments.id;
自连接
自连接是一种特殊的数据库连接操作,其中一个表与自身进行连接。
SELECT a.column1, b.column2
FROM table_name AS a
JOIN table_name AS b ON a.common_field = b.common_field;
table_name
是进行自连接的表名。a
和b
是该表的别名,用于区分连接的两个部分。common_field
是用于连接的共同字段。
示例查询:查找每位员工及其经理的姓名
SELECT a.name AS Employee, b.name AS Manager
FROM employees AS a
JOIN employees AS b ON a.manager_id = b.employee_id;
在这个查询中:
a
和b
是employees
表的别名。a
代表下属员工,b
代表经理。- 连接条件
a.manager_id = b.employee_id
确保每位员工与其经理正确匹配。
- 别名:在自连接中,别名非常重要,因为它们帮助区分连接的两个部分。
- 连接条件:确保连接条件正确,以反映表中的实际关系。
- 性能:自连接可能会影响查询性能,特别是在大型数据集上。优化查询和使用索引可以提高效率。
联合连接
UNION
和 UNION ALL
是 SQL 中用于合并两个或多个查询结果集的关键词,它们之间的主要区别在于是否去除重复行。
UNION
UNION
用于合并两个或多个 SELECT
语句的结果集,并自动去除结果集中的重复行。每个 SELECT
语句必须具有相同数量的列,并且相应的列必须具有兼容的数据类型。
示例:
SELECT name FROM customers
UNION
SELECT name FROM suppliers;
这个查询会返回 customers
表和 suppliers
表中所有不重复的名字列表。
UNION ALL
UNION ALL
(或简单地写作 UNION
)与 UNION
类似,但它不会去除结果集中的重复行,即保留所有行,包括重复的行。
示例:
SELECT name FROM customers
UNION ALL
SELECT name FROM suppliers;
这个查询会返回 customers
表和 suppliers
表中的所有名字,包括重复的名字。
区别
- 去重:
UNION
会去除重复行,而UNION ALL
不会。 - 性能:如果不需要去重,使用
UNION ALL
可能会更快,因为数据库不需要额外处理来识别和去除重复行。
在实际使用中,选择 UNION
还是 UNION ALL
取决于是否需要去除结果集中的重复数据。如果需要一个不包含重复项的列表,使用 UNION
;如果需要包含所有项,包括重复的,使用 UNION ALL
。
三.子查询和派生表
- 子查询:在主查询中嵌套的查询,通常用于筛选或计算。
子查询(Subquery)是嵌套在另一个查询中的 SQL 查询,它可以在SELECT * FROM (SELECT column1, column2 FROM table1 WHERE condition ) AS derived_table;
SELECT
、INSERT
、UPDATE
、DELETE
语句中使用。子查询通常用于从数据库检索数据,然后将其作为条件或值传递给外部查询。
子查询的基本用法
子查询可以出现在 SQL 语句的 WHERE
子句、HAVING
子句、SELECT
列表、FROM
子句或 JOIN
子句中。
1. 在 WHERE
子句中使用子查询
SELECT column_name(s)
FROM table_name
WHERE column_name IN (SELECT column_name FROM other_table WHERE condition);
这个子查询从 other_table
中选择符合特定条件的 column_name
值,然后在外部查询中检查 table_name
中的 column_name
是否存在于这些值中。
2. 在 SELECT
列表中使用子查询
SELECT column_name, (SELECT MAX(column_name) FROM other_table) AS max_value
FROM table_name;
这个子查询为 table_name
的每一行检索 other_table
中 column_name
的最大值。
3. 在 FROM
子句中使用子查询(派生表)
SELECT a.column_name, b.column_name
FROM table_name a, (SELECT column_name FROM other_table WHERE condition) b;
或者使用 WITH
子句(公用表表达式,CTE)在 SQL 标准中:
WITH DerivedTable AS (SELECT column_name FROM other_table WHERE condition
)
SELECT a.column_name, b.column_name
FROM table_name a, DerivedTable b;
这个子查询创建了一个派生表,该表可以在外部查询中像普通表一样使用。
4. 在 JOIN
子句中使用子查询
SELECT a.column_name, b.column_name
FROM table_name a
JOIN (SELECT column_name FROM other_table WHERE condition) b ON a.common_field = b.common_field;
这个子查询在 JOIN
操作中使用,将 other_table
表中符合特定条件的行与 table_name
表进行连接。
注意
- 子查询必须用括号括起来。
- 子查询可以是任何有效的
SELECT
语句,包括使用聚合函数、GROUP BY
子句或HAVING
子句。 - 子查询的结果集必须与外部查询的相应部分兼容(例如,外部查询的
WHERE
子句中使用子查询时,子查询的结果集应该是一个单一的列)。
分类
1.标量子查询
标量子查询的常见用途包括:
-
作为条件:在
WHERE
子句中使用标量子查询来过滤结果。SELECT * FROM emp WHERE salary > (SELECT AVG(salary) FROM emp);
在这个例子中,标量子查询
(SELECT AVG(salary) FROM emp)
用于找出工资高于平均工资的员工。 -
作为列值:在
SELECT
列表中使用标量子查询来计算或获取额外的列值。SELECT empno, name, (SELECT deptno FROM dept WHERE deptno = emp.deptno) AS deptno FROM emp;
这里,标量子查询
(SELECT deptno FROM dept WHERE deptno = emp.deptno)
用于获取与员工部门表中相应部门编号匹配的部门编号。 -
作为排序依据:在
ORDER BY
子句中使用标量子查询来对结果进行排序。SELECT * FROM emp ORDER BY (SELECT COUNT(*) FROM orders WHERE orders.empno = emp.empno) DESC;
这个例子中,标量子查询
(SELECT COUNT(*) FROM orders WHERE orders.empno = emp.empno)
用于根据员工的订单数量对员工进行排序。
2.列子查询
列子查询通常用于在 SELECT
语句中计算派生列(Derived Column)的值。例如,你可能想要在查询结果中包含一个计算字段或从另一个表中检索的值。
SELECT column1, (SELECT column2 FROM other_table WHERE condition) AS alias
FROM table1;
在这个例子中,子查询 (SELECT column2 FROM other_table WHERE condition)
被用来为 table1
的每一行计算或检索一个值,并将这个值作为 alias
列返回。
假设有两个表:employees
和 departments
。employees
表包含员工信息,而 departments
表包含部门信息。每个员工属于一个部门,部门有一个 department_id
。
employees 表结构:
employee_id
name
department_id
departments 表结构:
department_id
department_name
要获取所有员工及其部门名称,可以使用列子查询:
SELECT e.name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;
实现相同的结果,可以这样做:
SELECT e.name, (SELECT d.department_name FROM departments d WHERE d.department_id = e.department_id) AS department_name
FROM employees e;
在这个例子中,子查询 (SELECT d.department_name FROM departments d WHERE d.department_id = e.department_id)
为 employees
表中的每个员工检索相应的部门名称。
注意事项
- 列子查询必须返回单个值,或者与外部查询的行数相匹配的值。
- 如果子查询返回多行,那么外部查询的每一行都会与子查询返回的每一行进行组合,这可能导致结果集的行数急剧增加(笛卡尔积)。
- 在使用列子查询时,确保子查询的逻辑正确,以避免产生错误的结果或性能问题。
表子查询
表子查询的基本语法如下:
SELECT column_list
FROM (SELECT column_listFROM table_nameWHERE condition
) AS alias_name
WHERE another_condition;
column_list
是你想选择的列名列表。table_name
是原始表的名称。condition
是用于筛选原始表中行的条件。alias_name
是子查询结果集的别名,用于在外部查询中引用。
要获取所有员工及其部门名称,可以使用表子查询:
SELECT e.name, d.department_name
FROM employees e
JOIN (SELECT department_id, department_nameFROM departments
) d ON e.department_id = d.department_id;
在这个例子中,子查询 SELECT department_id, department_name FROM departments
作为一个临时表,与 employees
表进行连接。
- 派生表:在 FROM 子句中定义的子查询,可以像普通表一样使用。
SELECT a.*, b.* FROM table1 a JOIN (SELECT * FROM table2 WHERE condition ) b ON a.common_field = b.common_field;
第六节 事务
事务(Transaction)是数据库管理系统中的一个核心概念,用于确保数据的完整性和一致性。在 SQL 中,事务是一组不可分割的操作序列,这些操作要么全部成功,要么全部不做,是一个原子操作单元。
事务的特性
事务具有 ACID 特性,即:
- 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不做,不能只执行其中的一部分。
- 一致性(Consistency):事务执行前后,数据库保持一致性状态。
- 隔离性(Isolation):事务的执行不受其他并发事务的干扰,事务之间是相互隔离的。
- 持久性(Durability):一旦事务提交,则其结果将永久保存在数据库中,即使系统发生故障也不会丢失。
事务的操作
事务的操作通常包括:
- 开始事务:标记事务的开始。
- 提交事务:将事务中的所有修改永久保存到数据库中。
- 回滚事务:撤销事务中的所有修改,保持数据库状态不变。
SQL 中的事务语句
在 SQL 中,可以使用以下语句来控制事务:
- BEGIN TRANSACTION 或 START TRANSACTION:开始一个新的事务。
- COMMIT:提交当前事务,将事务中的所有修改永久保存到数据库。
- ROLLBACK:回滚当前事务,撤销事务中的所有修改。
- SAVEPOINT:创建事务的保存点,可以在回滚时指定回滚到某个保存点。
事务的使用示例
-- 开始事务
START TRANSACTION;-- 执行一些操作
UPDATE account SET balance = balance - 100 WHERE account_id = 1;
UPDATE account SET balance = balance + 100 WHERE account_id = 2;-- 提交事务,使操作永久生效
COMMIT;-- 如果需要回滚
ROLLBACK;
- 性能:虽然事务确保了数据的安全性,但过多或过长的事务可能会影响数据库性能。
- 锁定:事务可能会导致数据库锁定,影响其他用户的访问。
- 错误处理:在事务中的错误需要被适当处理,以避免事务失败。
- 自动提交:许多数据库系统默认在每个单独的 SQL 语句后自动提交事务,可以通过设置数据库的自动提交行为来改变这一行为。
- 并发事务问题通常出现在多用户同时对数据库进行读写操作时,可能会导致数据的不一致性。这些问题包括:
- 脏读(Dirty Read):一个事务读取了另一个事务未提交的修改数据。
- 不可重复读(Non-repeatable Read):在同一事务中,多次读取同一数据集合时,由于其他事务的修改,导致读取结果不一致。
- 幻读(Phantom Read):在同一事务中,多次查询时,结果集的行数不一致,看起来像是出现了“幻影”的行。这通常是由其他事务插入或删除了符合查询条件的行导致的。
事务隔离级别是解决这些问题的关键机制,它定义了事务间的隔离程度。SQL标准定义了四个隔离级别:
- 读未提交(Read Uncommitted):最低隔离级别,允许脏读,因为事务可以读取未提交事务修改的数据。
- 读已提交(Read Committed):确保事务只能读取已提交的数据,避免了脏读,但可能会出现不可重复读和幻读。
- 可重复读(Repeatable Read):保证在同一事务中多次读取同一数据集合时,结果是一致的,避免了不可重复读,但幻读仍有可能发生。
- 串行化(Serializable):最高隔离级别,事务完全隔离,像事务是串行执行的一样,避免了脏读、不可重复读和幻读,但可能会牺牲一些并发性能。