MySQL

基本术语

列:具有相同数据类型的数据的集合

行:每一行用来描述某条记录的具体信息

冗余:存储两倍数据。冗余降低了性能,但提高了数据的安全性

主键:主键是唯一的,一个数据表只能由一个主键。可以用主键查询信息

外键: 一个表中的一列或多列,这些列的值必须与另一个表(通常称为主表)中的主键或唯一键的值相匹配,或者为空值。包含外键的表称为从表(或子表),被引用的表称为主表(或父表)。

复合键:复合键(组合键)将多个列组成一个索引键,一般用于复合索引

索引:使用索引可快速访问数据表中的特定信息,索引是数据库中由一列或多列的值进行排序的结构。相当于书籍的目录。

参照完整性:参照的完整性要求关系中不允许引入不存在的实体。

值:行的具体信息,每个值必须与该列的数据类型相同

键:键的值在当前列中具有唯一性

1737622524881

管理

Windows:

启动/关闭

去找 services.msc

或者使用命令net start/stop mysql

Linux:

启动/关闭

sudo systemctl start/stop mysql

在一些老旧的版本:

sudo service mysql start/stop

重启

sudo systemctl restart mysql

或者

sudo service mysql restart

检查状态

sudo systemctl status mysql

或者

sudo service mysql status

用户设置

创建

CREATE USER 'username'@'host' IDENTIFIED BY 'password';
  • username:用户名。
  • host:指定用户可以从哪些主机连接。例如,localhost 仅允许本地连接,% 允许从任何主机连接。
  • password:用户的密码。

例如:

CREATE USER 'john'@'localhost' IDENTIFIED BY 'password123';

删除

DROP USER 'username'@'host';

修改密码

ALTER USER 'username'@'host' IDENTIFIED BY 'new_password';

修改用户主机

-- 删除旧用户
DROP USER 'john'@'localhost';

-- 重新创建用户并指定新的主机
CREATE USER 'john'@'%' IDENTIFIED BY 'password123';

授权

GRANT privileges ON database_name.* TO 'username'@'host';
  • privileges:所需的权限,如 ALL PRIVILEGESSELECTINSERTUPDATEDELETE 等。
  • database_name.*:表示对某个数据库或表授予权限。database_name.* 表示对整个数据库的所有表授予权限,database_name.table_name 表示对指定的表授予权限。
  • TO 'username'@'host':指定授予权限的用户和主机。

例如:

GRANT ALL PRIVILEGES ON test_db.* TO 'john'@'localhost';

授权或者撤权都要刷新才能生效:

FLUSH PRIVILEGES;

查权

SHOW GRANTS FOR 'username'@'host';

撤权

REVOKE ALL PRIVILEGES ON test_db.* FROM 'john'@'localhost';

创建并指定权限

CREATE USER 'john'@'localhost' IDENTIFIED BY 'password123' WITH GRANT OPTION;  //加上这句
GRANT ALL PRIVILEGES ON test_db.* TO 'john'@'localhost';

/etc/my.cnf 文件配置

1737720361989

1737720387003

管理命令

1.use 数据库名

​ 使用该命令后所有Mysql命令都只针对该数据库

2.SHOW DATABASES

​ 显示所有数据库列表

3.SHOW TABLES

​ 显示某数据库的所有表(所以要先use)

4.SHOW COLUMNS FROM 数据表

​ 显示数据表的属性,属性类型,主键信息 ,是否为 NULL,默认值等其 他信息

5.SHOW INDEX FROM 数据表

​ 显示索引,包括主键

6.SHOW TABLE STATUS [FROM db_name] [LIKE ‘pattern’] \G

​ 该命令将输出Mysql数据库管理系统的性能及统计信息。

例如:

SHOW TABLE STATUS FROM 数据库名;
SHOW TABLE STATUS from RUNOOB LIKE 'runoob%';
#显示以runoob%开头的表的信息
SHOW TABLE STATUS from RUNOOB LIKE 'runoob%'\G;
#\G,查询结果按列打印

PHP语法

1.连接

<?php
$servername = "localhost";
$username = "your_username";
$password = "your_password";
$database = "your_database";

// 创建连接
$conn = mysqli_connect($servername, $username, $password, $database);

// 检测连接,可见mysqli_connect是有返回值的
if (!$conn) {
die("连接失败: " . mysqli_connect_error());
}
echo "连接成功";
?>

其中的函数格式和部分功能:

函数定义:mysqli_function(value,value,...);
mysqli_connect($connect);
mysqli_query($connect,"SQL 语句");
mysqli_fetch_array()
mysqli_close()
//都是内置的函数

二进制连接

mysql -u your_username -p
//-u 指定用户名,-p 要输入密码

使用脚本连接

mysqli_connect(host, username, password, dbname,port, socket);  
//都是可选参数,没有就不写。其中只有port是int,其他都是字符串
/*套接字(socket)是一种进程间通信机制。当脚本就在服务器上时,使用这个
不用经过网络套接字(通过 TCP/IP 协议),而是使用UNIX套接字(所以在windows系统上,这个参数会被忽略)。*/
其实就是不用网络,在本机进程间通信连接
注意:这是个指向.socket文件的路径
所以,如果用这玩意儿,就不用指定port了,可以直接设置为null

在PHP8.0以上,可以使用命名参数来保证正确传参,参数名就是上面那几个

mysqli_close() 函数来断开与 MySQL 数据库的链接。

函数定义:bool mysqli_close ( mysqli $link )

该函数只有一个参数为 mysqli_connect() 函数创建连接成功后返回的 MySQL 连接标识符(mysqli_connect的返回值)。

例如:

$link = mysqli_connect("localhost", "username", "password", "database");
if (!$link) {
die("连接失败: ". mysqli_connect_error());
}
// 执行一些数据库操作

// 显式关闭连接
mysqli_close($link);
//不给参数,就关闭最近打开的一个

2.退出

EXIT;或者QUIT;或者快捷键ctrl +D

操作数据库

0.查看已有数据库

SHOW DATABASES;

1.创建

CREATE DATABASE [IF NOT EXISTS] 数据库名;
如果要指定选项,可以加上其他参数:
CREATE DATABASE mydatabase
CHARACTER SET utf8mb4
COLLATE utf8mb4_general_ci;
指定字符集和排列顺序。
如果不加IF NOT EXISTS并且存在该数据库,会报错

使用 mysqladmin

注:mysqladmin是个命令

mysqladmin -u your_username -p create your_database
如果要指定选项,可以加上特定参数:
mysqladmin -u your_username -p create your_database \
--default-character-set=utf8mb4 \
--default-collation=utf8mb4_general_ci
作用同上

如果是普通用户,可能要特定权限

这个命令行可以使用SQL的命令:

mysqladmin -u your_username -p your_command
例如:
mysqladmin -u your_username -p status

使用PHP脚本

mysqli_query(connection,query,resultmode);
也有返回值,也是用于判断是否成功

除了最后一个,都是必须项。

connection:就是$connt,也就是mysqli_connet(…)

query:查询字符串,也就是SQL语句。

resultmode:这是常量,只能填以下两个之一:

​ MYSQLI_USE_RESULT (检索大量数据时用)

​ MYSQLI_STORE_RESULT (默认)

2.删除

DROP DATABASE [IF EXISTS] <database_name>;
同样,如果不加IF EXISTS,删除不存在的,会报错

//这是不可逆的

使用 mysqladmin

mysqladmin -u your_username -p drop your_database

使用PHP脚本

还是用:

mysqli_query(connection,query,resultmode);

显而易见:

此时的$query就是DROP DATABASE [IF EXISTS] ;

3.选择

使用命令行

mysql -u root -p; //执行完这个,就会进入mysql提示符
use <database name>;
....; //其他操作命令

use语句可以换成命令中的-D参数,变成:

mysql -u your_username -p -D your_database

使用php脚本

mysqli_select_db(connection,dbname);
//都是必填项
一个实例
<?php
$dbhost = 'localhost'; // mysql服务器主机地址
$dbuser = 'root'; // mysql用户名
$dbpass = '123456'; // mysql用户名密码
$conn = mysqli_connect($dbhost, $dbuser, $dbpass);
if(! $conn )
{
die('连接失败: ' . mysqli_error($conn));
}
echo '连接成功';
mysqli_select_db($conn, 'RUNOOB' );
mysqli_close($conn);
?>

数据类型

数值类型

几个不太一样的地方:

int分为:tinyint(1Bytes),smallint(2),mediumint(3),int(4),bigint(8)

多了一个

DECIMAL类型,用于表示小数:

对于DECIMAL(M,D) ,如果M>D,为M+2否则为D+2

日期和时间类型

DATE (3):YYYY-MM-DD

TIME(3):HH:MM:SS

YEAR(1):YYYY

DATATIME(8):YYYY-MM-DD hh:mm:ss

TIMESTAMP(4):同上 //这就是时间戳

字符串类型

类型 大小 用途
CHAR 0-255 bytes 定长字符串
VARCHAR 0-65535 bytes 变长字符串
TINYBLOB 0-255 bytes 不超过 255 个字符的二进制字符串
TINYTEXT 0-255 bytes 短文本字符串
BLOB 0-65 535 bytes 二进制形式的长文本数据
TEXT 0-65 535 bytes 长文本数据
MEDIUMBLOB 0-16 777 215 bytes 二进制形式的中等长度文本数据
MEDIUMTEXT 0-16 777 215 bytes 中等长度文本数据
LONGBLOB 0-4 294 967 295 bytes 二进制形式的极大文本数据
LONGTEXT 0-4 294 967 295 bytes 极大文本数据

others

枚举与集合类型

  • ENUM: 枚举类型,用于存储单一值,可以选择一个预定义的集合。
  • SET: 集合类型,用于存储多个值,可以选择多个预定义的集合。

空间数据类型

GEOMETRY, POINT, LINESTRING, POLYGON, MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, GEOMETRYCOLLECTION: 用于存储空间数据(地理信息、几何图形等)。

使用数据库

0.查看已有数据表/列

SHOW TABLES; 
SHOW TABLE STATUS; //这是看表类型(ENGING等)
SHOW COLUMNS FROM table_name; //这是看表结构,不是数据

不加S的话,要写具体名称或者模糊匹配。\G以竖排显示

1.创建数据表

必须提供:

  • 表名
  • 表字段名
  • 定义每个表字段的数据类型

SQL语法:

CREATE TABLE table_name (
column1 datatype,
column2 datatype,
...
);
//column是列名

可以加子句:

CREATE TABLE mytable (
id INT PRIMARY KEY,
name VARCHAR(50)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

SQL实例

CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
birthdate DATE,
is_active BOOLEAN DEFAULT TRUE
);
//AUTO_INCREMENT PRIMARY KEY,设定该列自增长(1,2,3...),且是主键
(这两者一般同时出现)
注意:每个表只能有一个 AUTO_INCREMENT 列,必须为整数类型
即使删除了表中最大自增值的记录,下一次插入新记录时,自增值仍然会继续
递增,不会复用已删除的自增值。
起始值默认为1,但可以用`ALTER TABLE users AUTO_INCREMENT = 100;`改
//NOT NULL,这项必填
//DEFAULT TRUE,设定默认值为TRUE
以上三行都是附加的属性

通过命令行

mysql -u root -p -D <database name>
//接下来进了SQL命令行,当然就是写SQL语句。

注意:mySQL命令行中,回车不是结束,;才是。

因此,写不下时可以随意换行

通过PHP脚本

还是 mysqli_query(connection,query,resultmode);

2.删除数据表[数据]

DROP TABLE [IF EXISTS] table_name; //这是直接删表
TRUNCATE TABLE table_name; //这是删表数据(保留结构,数据全删)
DELETE FROM table_name WHERE condition; //这也是删表数据(保留结构,加条件可不全删)
注意:TURUNCATE是直接释放数据页的空间,没有详细的删除的日志,因此不能触发任何触发器,也不能事务回滚
但是更加高效,delete是逐行删除,会触发,能回滚,但更低效

使用命令行和PHP脚本,同上

3.插入表数据

INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);

//value若是字符型,要写成'value'

//如果插入所有列,可以省略列名。此时第一个要写NULL,因为它是那个自增长列的占位符。当然也可以第一行写0(接下来同理),要不然会变来变去。

插入多行

实例:

INSERT INTO users (username, email, birthdate, is_active)
VALUES
('test1', 'test1@runoob.com', '1985-07-10', true),
('test2', 'test2@runoob.com', '1988-11-25', false),
('test3', 'test3@runoob.com', '1993-05-03', true);

使用命令行和PHP脚本,同上,这之后的也都是同上

但是,如果插入的数据中有中文,必须先执行

mysqli_query($conn , "set names utf8");
或者使用PHP函数
mysqli_set_charset($conn, "utf8");
//它会同时设置:
character_set_client:客户端发送数据时使用的字符编码。
character_set_results:服务器返回结果集时使用的字符编码。
character_set_connection:连接层使用的字符编码。

获取自增的值

$last_id=mysqli_insert_id($conn)

4.修改表数据

进行重要的结构修改时,建议先备份数据

UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition; //where子句可选,没有就会更新所有行

替换某个字段中的某个字符

当我们需要将字段中的特定字符串批量修改为其他字符串时,可使用以下操作:

UPDATE table_name SET field=REPLACE(field, 'old-string', 'new-string') 
[WHERE Clause];

5.修改表名/结构/类型

添加列/主键

ALTER TABLE table_name
ADD COLUMN new_column_name datatype;
以及
ALTER TABLE table_name
ADD PRIMARY KEY (column_name);
以及
ALTER TABLE testalter_tbl ENGINE = MYISAM;

添加外键

ALTER TABLE child_table
ADD CONSTRAINT fk_name
FOREIGN KEY (column_name)
REFERENCES parent_table (column_name);

修改数据类型

ALTER TABLE TABLE_NAME
MODIFY COLUMN column_name new_datatype;

修改列/表名

ALTER TABLE table_name
CHANGE COLUMN old_column_name new_column_name datatype;
-- 可以顺手改数据类型
以及
ALTER TABLE old_table_name
RENAME TO new_table_name;

删除列

ALTER TABLE table_name
DROP COLUMN column_name;

修改字段类型及名称

可以使用上面的CHANGE子句

也可以用MODIFY子句

ALTER TABLE testalter_tbl MODIFY c CHAR(10);
等同于
ALTER TABLE testalter_tbl CHANGE c c CHAR(10);
只不过MODIFY子句不能顺便改名

这两者修改时都可以附带上NOT NULL,DEFAULT等

例如:

ALTER TABLE testalter_tbl MODIFY j BIGINT NOT NULL DEFAULT 100;

修改/删除默认值等

ALTER TABLE testalter_tbl ALTER i SET DEFAULT 1000;
以及
ALTER TABLE testalter_tbl ALTER i DROP DEFAULT/PRIMARY KEY;

6.读取/查询表数据

读取

select * from 数据表名;   //可以多表`,`分开

查询

SELECT column1, column2, ...
FROM table_name //可以多表`,`分开
[WHERE condition]
[ORDER BY column_name [ASC | DESC]]
[LIMIT N][offset M];
  • 使用 * 表示选择所有列。
  • WHERE condition 是一个可选的子句,用于指定过滤条件,只返回符合条件的行。
  • ORDER BY column_name [ASC | DESC] 可选子句,指定结果集的排序顺序,默认升序(ASC)。
  • LIMIT N 可选子句,返回N条记录
  • offset N 可选子句,跳过M条记录

limit M offset N;

相当于

limit N,M;

获取数据
1.使用 mysqli_fetch_array MYSQLI_ASSOC

按行获取,一次只从返回的数据中获取一行

MYSQLI_ASSOC是返回关联数组

也可以用 MYSQLI_NUM ,返回数字数组。区别不大,只不过是把下面的$row[‘id’]改成$row[0]

...
$sql = "SELECT id, name, age FROM users";
$result = mysqli_query($conn, $sql);
if (mysqli_num_rows($result) > 0) {
// 使用 mysqli_fetch_array 结合 MYSQLI_ASSOC 获取数据
while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
echo "ID: ". $row['id']. ", 姓名: ". $row['name']. ", 年龄: ". $row['age']. "<br>";
}//没行了会返回false
} else {
echo "没有找到记录";
}
...
2.使用 mysqli_fetch_assoc

没行返回null,但是也可以结束while

mysqli_fetch_assoc($result),返回的直接是关联数组

相当于

mysqli_fetch_array($result,MYSQLI_ASSOC)

3.内存释放
...
$retval = mysqli_query( $conn, $sql );
...
mysqli_free_result($retval);
where子句

类似于if语句,判断真假,使用ANDOR组合,LIKE模糊匹配

一般的:

...WHERE username = 'test';
...salary >= 50000;

对于日期时间类型:

...WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31';

对于可空的:

...WHERE email IS [NOT] NULL;

默认的字符串比较不区分大小写(非like子句),如果要,写成:

...WHERE BINARY...
LIKE子句

代替等号用于where子句(或者having子句)

可使用的通配符包括:

% 表示没有或者有任意个任意字符

_ 表示一个任意字符

其他匹配方式:

[]/[^] 类似正则表达式,查询内容包含通配符时,可以用这个括起

可以组合使用,比如:a%o_

like子句是否区分大小写取决于字符集

不区分是:

SELECT * FROM employees WHERE last_name LIKE ‘smi%’ COLLATE utf8mb4_general_ci;

合并查询数据(UNION)

SELECT column1, column2, ... FROM table1 WHERE condition1
UNION //加中间就行,可以叠叠乐
SELECT column1, column2, ... FROM table2 WHERE condition2
[ORDER BY column1, column2, ...]; //可选,排序合并后的数据

注意:1.会自动去除重复行(使用UNION ALL则不会,性能还会更好)。

​ 2.每个 SELECT 语句的列数和对应位置的数据类型必须相同(即使来自不同表)。

排序

ORDER BY子句,排序查询后的数据

SELECT column1, column2, ...
FROM table_name
ORDER BY column1 [ASC | DESC], column2 [ASC | DESC], ...;

默认ASC(升序)。DESC是降序

像上面那个多列排序,就是先按column1,再按column2

可以不用列名而用数字表示列的位置,例如:

ORDER BY 3 DESC, 1 ASC;

8.0以上的版本,末尾有可选参数:

NULLS FIRSTNULLS LAST ,表示排序时NULL的位置

注意:

如果字符集是utf-8,而不是GBK,排序时要先转码:

SELECT * 
FROM runoob_tbl
ORDER BY CONVERT(runoob_title using gbk);

分组

GROUP BY 语句根据一个或多个列对结果集进行分组。

在分组的列上我们可以使用 COUNT, SUM, AVG,等函数。

SELECT column1, column2, aggregate_function(column3)
FROM TABLE_NAME
WHERE condition
GROUP BY column1, column2;
实例
SELECT customer_id, SUM(order_amount) AS total_amount
FROM orders
GROUP BY customer_id;
//计算每个客户的订单总金额
  • GROUP BY 子句通常与聚合函数一起使用,因为分组后需要对每个组进行聚合操作。
  • SELECT 子句中的列通常要么是分组列GROUP BY使用的列),要么是聚合函数的参数
  • 可以使用多个列进行分组,只需在 GROUP BY 子句中用逗号分隔列名即可。

聚合函数:

SUMAVGCOUNTMINMAX 等,用于对每个分组的数据进行汇总计算。

使用WITH ROLLUP

可选子句,实现在分组统计数据基础上再进行相同的统计

对比一下:

mysql> SELECT name,SUM(signin) as signin_count FROM employee_tbl GROUP BY name WITH ROLLUP;
+--------+--------------+
| name | signin_count |
+--------+--------------+
| 小丽 | 2 |
| 小明 | 7 |
| 小王 | 7 |
| NULL | 16 | //NULL表示所有人的登录次数,因为这列相加没有意义
+--------+--------------+
4 rows in set (0.00 sec)

以及

mysql> SELECT name, COUNT(*) FROM employee_tbl GROUP BY name;
+--------+----------+
| name | COUNT(*) |
+--------+----------+
| 小丽 | 1 |
| 小明 | 3 |
| 小王 | 2 |
+--------+----------+
3 rows in set (0.01 sec)

为了避免NULL,可以使用

select coalesce(a,b,c);
//按顺序判断是否为NULL,返回遇到的第一个非NULL值。(全NULL就返回NULL)

于是可改成:

SELECT coalesce(name, '总数'), SUM(signin) as signin_count FROM  employee_tbl GROUP BY name WITH ROLLUP;

多表查询(JOIN)

  • INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录,可以简写为JOIN。
  • LEFT JOIN(左连接):获取左表所有记录,即使右表没有对应匹配的记录。
  • RIGHT JOIN(右连接): 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录。
SELECT column1, column2, ...
FROM table1
INNER JOIN table2
ON table1.column_name = table2.column_name;
//只返回两表中column_name内容相同的行
//SELECT中的列也要写成类似于table.column的形式,不必包含ON使用的那列

可以叠叠乐:

SELECT orders.order_id, customers.customer_name, products.product_name
FROM orders
INNER JOIN customers ON orders.customer_id = customers.customer_id
INNER JOIN order_items ON orders.order_id = order_items.order_id
INNER JOIN products ON order_items.product_id = products.product_id;
//此时只有在四个表的所选列中都存在的才会返回对应的SELECT

LEFT/RIGHT JOIN

以LEFT JOIN为例,RIGHT JOIN反之

SELECT column1, column2, ...
FROM table1
LEFT JOIN table2
ON table1.column_name = table2.column_name;
[WHERE 条件]
//返回左表中所有行的情况下。当右表中没有匹配的行时,相关列将显示为 NULL。

NULL值处理

WHERE子句

is (not) null

ORDER BY子句:

NULLS FIRST/LAST

用于比较

SELECT * FROM employees WHERE commission <=> NULL;
`<=>`是比较表达式是否相等的特殊操作符,可以直接替换`=`
都是NULL就返回TRUE
注意:回值始终是NULL

聚合函数

会被忽略

如果想指定为特定值,可以套个COALESCE或者它的下位IFNULL(只接受俩参数)

例如:AVG(COALESCE(salary, 0))或者AVG(IFNULL(salary, 0))

正则表达式

模式 描述
^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 ‘\n’ 或 ‘\r’ 之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 ‘\n’ 或 ‘\r’ 之前的位置。
. 匹配除 “\n” 之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符,请使用像 ‘[.\n]’ 的模式。
[…] 字符集合。匹配所包含的任意一个字符。例如, ‘[abc]’ 可以匹配 “plain” 中的 ‘a’。
[^…] 负值字符集合。匹配未包含的任意字符。例如, ‘[^abc]’ 可以匹配 “plain” 中的’p’。
p1|p2|p3 匹配 p1 或 p2 或 p3。例如,’z|food’ 能匹配 “z” 或 “food”。’(z|f)ood’ 则匹配 “zood” 或 “food”。
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,’zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,’o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。

正则表达式匹配的字符类

  • .:匹配任意单个字符。
  • ^:匹配字符串的开始。
  • $:匹配字符串的结束。
  • *:匹配零个或多个前面的元素。
  • +:匹配一个或多个前面的元素。
  • ?:匹配零个或一个前面的元素。
  • [abc]:匹配字符集中的任意一个字符。
  • [^abc]:匹配除了字符集中的任意一个字符以外的字符。
  • [a-z]:匹配范围内的任意一个小写字母。
  • [0-9]:匹配一个数字字符。
  • \w:匹配一个字母数字字符(包括下划线)。
  • \s:匹配一个空白字符。

REGEXP/RLIKE

这两者等同

SELECT column1, column2, ...
FROM table_name
WHERE column_name REGEXP [BINARY] 'pattern';
//'pattern'是一个正则表达式模式,例如:'^[aeiou]|ok$','item[0-9]+'等
//使用BINARY关键字,匹配区分大小写

事务

要么全部执行,要么都不执行

用来管理 insert、update、delete 语句

控制语句

  • BEGIN 或 START TRANSACTION 显式地开启一个事务;
  • COMMIT/COMMIT WORK,二者等价。提交事务,此时才真正修改,不可逆;
  • ROLLBACK/ROLLBACK WORK,二者等价。结束用户的事务,并撤销正在进行的所有未提交的修改;
  • SAVEPOINT identifier,在事务中创建保存点,可以有多个;
  • RELEASE SAVEPOINT identifier 删除保存点,不存在时抛出异常;
  • ROLLBACK TO identifier 把事务回滚到保存点;
  • SET TRANSACTION 用来设置事务的隔离级别。
  • SET AUTOCOMMIT=0 禁止自动提交
  • SET AUTOCOMMIT=1 开启自动提交

实例:

START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2;
-- 判断是否要提交还是回滚
IF (条件) THEN
COMMIT; -- 提交事务
ELSE
ROLLBACK; -- 回滚事务
END IF;

隔离级别

InnoDB 存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE。

读未提交

READ UNCOMMITTED

可以读取到其他事务尚未提交的数据,最低级别

可能脏读(被读取的事务最终回滚导致所读的数据无效)

读已提交

READ COMMITTED

只能读取到其他事务已经提交的数据

不会脏读

但由于不可重复读,可能导致多次读取同一数据的值不一样

可重复读

REPEATABLE READ

多次读取同一数据始终保持一致 ,被读事务进行的修改和提交不会改变读取结果

不会脏读

可能幻读

原理:

可重复读默认对读取行加一个快照,此后该事务读取该行都是用快照(可以改为加共享锁)

所以不脏读。

但是如果其他事务增加满足查询条件的行,查询会多出一条

导致幻读。

//非串行化都可能幻读

//行级锁分两种:共享锁(S锁),其他事务可读不写;排他锁(X锁),其他事务不

读不写。一行同时只能一个锁。带有锁的行被其他事务读取时,必须等待其释放(上锁

的事务提交或回滚)

可以自行加锁:

SELECT price FROM products WHERE id = 1 LOCK IN SHARE MODE;   -- 加共享锁
SELECT price FROM products WHERE id = 1 FOR UPDATE; -- 加排他锁

如果太久不释放行级锁,等待释放的事务会收到超时报错

例如:

在可重复读下

-- 事务 A 开始
START TRANSACTION;
-- 事务 A 读取数据
SELECT price FROM products WHERE id = 1; -- 假设读取到价格为 100
-- 如果改用共享锁,事务B会等待事务A释放锁

-- 事务 B 开始
START TRANSACTION;
-- 事务 B 修改数据
UPDATE products SET price = 200 WHERE id = 1;
-- 事务 B 提交
COMMIT;

-- 事务 A 再次读取数据
SELECT price FROM products WHERE id = 1;
-- 仍然会读取到价格为 100,因为事务 A 读取的是快照中的数据
-- 事务 A 提交
COMMIT;

容易死锁(互相等释放)

-- 事务 A
START TRANSACTION;
SELECT price FROM products WHERE id = 1 LOCK IN SHARE MODE;
SELECT price FROM products WHERE id = 2 FOR UPDATE; -- 尝试获取 id = 2 的排他锁

-- 事务 B
START TRANSACTION;
SELECT price FROM products WHERE id = 2 LOCK IN SHARE MODE;
SELECT price FROM products WHERE id = 1 FOR UPDATE; -- 尝试获取 id = 1 的排他锁

串行化

串行执行,最高级别

不是并发,所以不会幻读,但导致事务之间会相互阻塞,会极大地降低系统的吞吐量。

事务实例

<?php
$dbhost = 'localhost'; // mysql服务器主机地址
$dbuser = 'root'; // mysql用户名
$dbpass = '123456'; // mysql用户名密码
$conn = mysqli_connect($dbhost, $dbuser, $dbpass);
if(! $conn )
{
die('连接失败: ' . mysqli_error($conn));
}
// 设置编码,防止中文乱码
mysqli_query($conn, "set names utf8");
mysqli_select_db( $conn, 'RUNOOB' );
mysqli_query($conn, "SET AUTOCOMMIT=0"); // 设置为不自动提交,因为MYSQL默认立即执行
mysqli_begin_transaction($conn); // 开始事务定义

if(!mysqli_query($conn, "insert into runoob_transaction_test (id) values(8)"))
{
mysqli_query($conn, "ROLLBACK"); // 判断当执行失败时回滚
}

if(!mysqli_query($conn, "insert into runoob_transaction_test (id) values(9)"))
{
mysqli_query($conn, "ROLLBACK"); // 判断执行失败时回滚
}
mysqli_commit($conn); //执行事务
mysqli_close($conn);
?>