MySql数据操作-连接方式、索引、事务、函数(二)

13 篇文章 0 订阅
3 篇文章 0 订阅

1. 关于MySQL数据库连接方式介绍【笛卡尔积连接、内连接、外连接-左连接、外连接-右连接 】

笛卡尔积连接:

SELECT article.id AS id, article.title AS title, article.state as state, article_cate.title as cate 
FROM article,article_cate WHERE article.cate_id = article_cate.id ORDER BY id ; 

注:order by id 是指代,搜索后以id为标准排序。

内连接:

SELECT * FROM lesson  INNER JOIN lesson_student ON lesson.id = lesson_student.lesson_id AND lesson_student.student_id =1


#### from 条件后面的跟的+ 表名称  inner josn + 表名称  on + 查询条件 出来的图就是对应顺序不一致

SELECT * FROM lesson_student INNER JOIN lesson ON lesson.id = lesson_student.lesson_id AND lesson_student.student_id =1

注: 内连接 inner join 关联表名称  on 表连接条件 ------通过 ORDER BY 排序字段名称 id ;

外连接 (左连接)

SELECT * FROM lesson LEFT JOIN lesson_student ON lesson.id = lesson_student.lesson_id AND lesson_student.student_id =1

执行结果:  

外连接(右连接)

SELECT * FROM lesson RIGHT JOIN lesson_student ON lesson.id = lesson_student.lesson_id AND lesson_student.student_id =1

执行结果: 

注:使用左右链接,具体表现 以那一个表作为参照对象,那么数据的呈现方式也不一样。具体看图……

2.MySQL 索引

索引的建立,对于mysql的高效运行是很重要的,索引可以大大提高 MySQL 的检索速度。

其中MySQL的常见的索引类型有 普通索引、唯一索引、全文索引、空间索引Sqatial

以50万条数据为例,进行演示操作:

insert into users(`username`) select username from users

步骤一、创建一个有50万条数据的数据库;


SET NAMES utf8mb4;
set names utfmb4;设置字符集


## 第一个语句SET NAMES utf8mb4;用于设置会话字符集为utf8mb4。这是为了确保数据的正确存储和检索,特别是对于包含表情符号等特殊字符的文本数据。


SET FOREIGN_KEY_CHECKS = 0;
set foreign_key_checks = 1; 默认值为1,同时表示打开外键约束检查。
 
##第二个语句SET FOREIGN_KEY_CHECKS = 0;用于关闭外键约束检查。在插入或更新数据时,外键约束会检查关联表中是否存在匹配的键。关闭这个检查可以避免在插入或更新数据时引发外键约束错误。注意,在完成插入或更新操作后,应该重新打开外键约束检查,即将SET FOREIGN_KEY_CHECKS设置回默认值1。


备注: 这两语句应该在执行表创建语句之前执行

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `balance` decimal(10, 2) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;


-- ----------------------------
-- Records of user
-- ----------------------------

###  collate 排列规律 MySQL 8.0版本上以上用  上面的语句;如果是8.0版本以前的本地就用下满语句 创建表

-- ----------------------------
-- Table structure for user
-- ----------------------------

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `balance` decimal(10, 2) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;


-- ----------------------------
-- Records of user
-- ----------------------------

INSERT INTO `user` VALUES (1, '张三', 0.00);
INSERT INTO `user` VALUES (2, '李四', 200.00);
INSERT INTO `user` VALUES (5, 'wangwu', 100.00);

关于如何创建 50万条数据;

### 如何插入50万条数据步骤,如下:
(-- 查看mysql是否允许创建函数:)
1. SHOW VARIABLES LIKE 'log_bin_trust_function_creators'; 

show variables like 'log_bin_trust_function_createors'

(-- 查看mysql是否允许创建函数:)
1. SHOW VARIABLES LIKE 'log_bin_trust_function_creators'; 


(-- 命令开启:允许创建函数设置:(global-所有session都生效)
2. SET GLOBAL log_bin_trust_function_creators=1;  

set global log_bin_trust_function_creators=1;

(-- 命令开启:允许创建函数设置:(global-所有session都生效)
2. SET GLOBAL log_bin_trust_function_creators=1;  
-- 检查存储过程是否存在
SHOW PROCEDURE STATUS WHERE name = 'insert_user';

-- 如果存在,删除存储过程
DROP PROCEDURE IF EXISTS `insert_user`;

DELIMITER $$
CREATE PROCEDURE  insert_user(START INT, max_num INT)
BEGIN  
	DECLARE i INT DEFAULT 0;   
	#set autocommit =0 把autocommit设置成0  
	SET autocommit = 0;    
	REPEAT  
		SET i = i + 1;  
		INSERT INTO `user` ( id , username, balance ) VALUES ((START+i) ,rand_string(6), rand_num(1,10000));  
		UNTIL i = max_num  
	END REPEAT;  
	COMMIT;  
END$$

-- 插入50万条数据
CALL insert_user(100000,500000); 

具体详情
下面是代码段的详细解读:

```sql
DELIMITER $$
```
`DELIMITER` 语句用于更改 MySQL 的语句分隔符,将其设置为 `$$`。这是为了避免与存储过程中的分号冲突。

```sql
CREATE PROCEDURE insert_emp(START INT, max_num INT)
```
`CREATE PROCEDURE` 语句用于创建一个存储过程,名为 `insert_emp`。它接受两个参数:`START` 和 `max_num`,都是整数类型。

```sql
BEGIN
```
`BEGIN` 标记了存储过程的开始。

```sql
DECLARE i INT DEFAULT 0;
```
`DECLARE` 语句声明了一个名为 `i` 的局部变量,并初始化为0。该变量用于存储循环迭代的计数器。

```sql
SET autocommit = 0;
```
`SET` 语句将 `autocommit` 的值设置为0。这可以禁用自动提交事务,使得事务会在显式提交之前保持打开状态。

```sql
REPEAT
    SET i = i + 1;
    INSERT INTO `user` (id, username, balance) VALUES ((START + i), rand_string(6), rand_num(1, 10000));
UNTIL i = max_num END REPEAT;
```
这是一个重复循环块(REPEAT...UNTIL),在每次循环中,`i` 的值递增,并将根据指定的规则生成的数据插入到 `user` 表中。循环迭代直到 `i` 的值等于 `max_num`。

```sql
COMMIT;
```
`COMMIT` 语句用于提交当前事务,将之前所有的插入操作持久化到数据库。

```sql
END$$
```
`END` 标记了存储过程的结束。

综上所述,该存储过程的功能是将从 `START` 开始递增的编号、随机生成的用户名和随机生成的余额插入到 `user` 表中,直到插入的记录数达到 `max_num`,然后提交事务。同时,为了确保事务的一致性,禁用了自动提交和手动提交事务。

请注意,在执行这段代码之前,需要保证 `rand_string` 和 `rand_num` 函数的存在,并在代码上下文中正确使用该存储过程。

在其他详情的操作可自行操作查看文档,进行实验。

3. Mysql 事务:

事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不
执行。

1.创建user表作为演示

## 由于版本不一样创建数据库执行语句有一点差异化:
## collate =  utf8mb4_0900_ai_ci 可能需要改成:utf8mb4_general_ci
## 也就是说排序规则改成 utf8mb4_general_ci 是因为本地的navicat for mysql 工具导致的
## 作者本地使用的工具是 v11.0.10 -企业版本


SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ---------------------------- 
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(0) NOT NULL AUTO_INCREMENT, 
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL
DEFAULT NULL, 
`balance` decimal(10, 2) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;


-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '熊大', 100.00);
INSERT INTO `user` VALUES (2, '光头强', 100.00);
SET FOREIGN_KEY_CHECKS = 1;


---------------------------------分界线-------------------------------------------

## 高级版本执行以下语句
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ---------------------------- 
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(0) NOT NULL AUTO_INCREMENT, `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL
DEFAULT NULL, `balance` decimal(10, 2) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;


-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '熊大', 100.00);
INSERT INTO `user` VALUES (2, '光头强', 100.00);
SET FOREIGN_KEY_CHECKS = 1;

2.案例:熊大账户转账转出 100 元到光头强的账户

如何实现思路:
1、熊大 的账户减去 100
UPDATE user set balance = balance-100 WHERE id=1
2、光头强 的账户增加 100
UPDATE user set balance = balance+100 WHERE id=2
方法执行:
UPDATE user set balance = balance-100 WHERE id=1

UPDATE user set balance = balance+100 WHERE id=2



反馈结果:

[Err] 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'UPDATE user set balance = balance+100 WHERE id=2' at line 3
反馈的结果,分析问题:
1. 如果是单句执行sql语句 updata数据更新肯定是可行并且无明显报错信息;
2. 两句sql 更新 updata 不同id 值下的其他属性值,这个时候反馈的结果就是err的mysql语法错误然后告诉你报错的点在哪里几行。
如果我们更新完熊大的账户后,准备更新光头强账户的时候出现了错误(比如程序错误,或者数据库没
法连接、或者异常断电等错误)。这样的话就导致了数据不一致。为了保证数据的一致性,这个时候
我们就可以使用事务。

3. 引入使用事务的概念,或者说使用事务的时机

 事务的主要概念:

        BEGIN (begin) 开始一个事务

        ROLLBACK(rollback) 事务回滚

        COMMIT (commit) 事务确认

实际操作的使用时机:
  

引入上一个问题的深入思考
1. 熊大 的账户减少100
2. 光头强的账户增加100

那么是不是相对的事务使用就是这样:


begin;

update user set balance = balance-100 where id=1;

rollback; 只有执行报错时候才可以使用 回滚操作;



以上就是 那时候使用rollback 回滚事务的时机演示案例
同时附上对应代码:
begin;
update user set balance = balance-100 where id=1;
update user set balance = balance+100 where id=2;
commit;

以上就是对应的事务,

4. mysql 锁

Mysql 中的锁有 表级锁 行级锁;
最常用的表级锁

1.添加读锁

所谓读锁,可以并发读,但是不能并发写,读锁期间,没有释放锁之前不能进行写操作;
使用场景:
        读取结果集的最新版本,同时防止其他事务产生更新该结果集
主要用在需要数据依存关系时确认某行记录是否存在,并确保没有人对这个记录进行
UPDATE 或者 DELETE 操作
对表user添加读锁:lock table user read;
解除user的读锁: unlock tables;

2. 添加 写锁

只有锁表的用户可以进行读写操作,其他用户不行 (并发下对商品库存的操作)
使用场景:
lock table user write;
unlock tables;
使用读写锁 根据实际业务需求进行使用,以上就是表级锁的简单操作。

补充:

行级锁:每次操作锁住对应的行数据。

行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。

InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类:

  1. 行锁〈Record Lock)∶锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持。
  2. 间隙锁(Gap Lock):锁定索引记录间腺(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在R隔离级别下都支持。
  3. 临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。

InnoDB实现了以下两种类型的行锁:

  • 共享锁(S)∶允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁。
  • 排他锁(X)∶允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。 公

行锁中,共享锁与共享锁不互斥,共享锁与排它锁之间互斥;排它锁与排它锁之间互斥

InnoDB引擎中,执行SQL时的加行锁情况:

SQL行锁类型说明
INSERT ...排他锁自动加锁
UPDATE ...排他锁自动加锁
DELETE ...排他锁自动加锁
SELECT (正常)不加任何锁
SELECT ... LOCK IN SHARE MODE共享锁需要手动在SELECT之后加LOCK IN SHARE MODE
SELECT ...FOR UPDATE排他锁需要手动在SELECT之后加FOR UPDATE

默认情况下,InnoDB在REPEATABLE READ(RR)事务隔离级别运行,InnoDB使用next-key(临键)锁进行搜索和索引扫描,以防止幻读。

  • 针对唯一索引进行检索时,对已存在的记录进行等值匹配时,将会自动优化为行锁。
  • InnoDB的行锁是针对于索引加的锁不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁

有关间隙锁锁临键锁的说明(防止幻读):

  1. 索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁。
  2. 索引上的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock退化为间隙锁。
  3. 索引上的范围查询(唯一索引)--会访问到不满足条件的第一个值为止。

具体说明:

 -- 首先,准备一张表,id为主键,id分别为 1,3,10,15
 mysql> select * from user_innodb;
 +----+----------+------+
 | id | username | age  |
 +----+----------+------+
 |  1 | 张三     |    1 |
 |  3 | 李四     |    4 |
 | 10 | 王五     |    9 |
 | 15 | 赵六     |   15 |
 +----+----------+------+
 4 rows in set (0.00 sec)
 ​
 ​
 -- 对于1、索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁。
 ​
 -- 说明,当开启事务后,如果 此时执行 下面update语句
 update user_innodb set username = '王五' where id = 7
 -- 因为不存在 id = 7 的数据,这是就会给 id = 2 到 id = 9(不包含 4和 10)之间加间隙锁,此时其他客户端想insert (或 update 或 delete)id在2到9之间的数据就会进入阻塞状态。
 ​
 ​
 ​
 -- 对于2、索引上的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock退化为间隙锁。
 -- 首先创建一个 非唯一的普通索引 比如 age
 mysql> create index idx_user_age on user_innodb(age);
 Query OK, 0 rows affected (0.09 sec)
 Records: 0  Duplicates: 0  Warnings: 0
 ​
 -- 查看索引
 mysql> show index from user_innodb;
 +-------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
 | Table       | Non_unique | Key_name     | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible |
 +-------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
 | user_innodb |          0 | PRIMARY      |            1 | id          | A         |           3 |     NULL |   NULL |      | BTREE      |         |               | YES     |
 | user_innodb |          1 | idx_user_age |            1 | age         | A         |           4 |     NULL |   NULL | YES  | BTREE      |         |               | YES     |
 +-------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
 2 rows in set (0.09 sec)
 ​
 -- 首先开启事务
 -- 当执行下列语句时,若执行的是 select * from user_innodb where age = 4; 不会加锁
 select * from user_innodb where age = 4 lock in share mode; 
 ​
 ​
 -- 查看锁的情况,与表锁中查看意向锁的SQL一致
 mysql> select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
 mysql> select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
 +---------------+-------------+--------------+-----------+-----------+-----------+
 | object_schema | object_name | index_name   | lock_type | lock_mode | lock_data |
 +---------------+-------------+--------------+-----------+-----------+-----------+
 | heima_learn   | user_innodb | NULL         | TABLE     | IS        | NULL      |
 | heima_learn   | user_innodb | idx_user_age | RECORD    | S         | 4, 3      |
 | heima_learn   | user_innodb | PRIMARY      | RECORD    | S         | 3         |
 | heima_learn   | user_innodb | idx_user_age | RECORD    | S,GAP     | 9, 10     |
 +---------------+-------------+--------------+-----------+-----------+-----------+
 ​
 -- 因为 age 是一个普通索引,可能会出现重复的数据,所以说此时会加 3 个共享锁
 -- 第一个  | S         | 4, 3      | 
 -- 这个index_name =idx_user_age,lock_mode中没有GAP  (这里的 4, 3 指的是主键 age 的值为 4,id的值为 3) 的临键锁,表示数据 id = 3 的临键锁,值id = 2,3
 ​
 ​
 -- 第二个 | S         | 3         | 
 -- 第二个的 index_name 为 PRIMARY,表示 给id = 3 (age = 4)的数据加了行锁
 ​
 -- 第三个 | S,GAP     | 9, 10     | 
 -- 这里(9 代表 age = 9, 10 代表 id = 10) index_name = idx_user_age,在 lock_mode 存在 GAP,表示是一个间隙锁,锁住的数据为 id = 4 到 id = 9。
 ​
 -- 这么做的操作就是为了防止出现 幻读
 ​
 ​
 ​
 ​
 -- 对于3、索引上的范围查询(唯一索引)--会访问到不满足条件的第一个值为止。
 ​
 -- 重新开启事务
 -- 执行下列语句
  select * from user_innodb where id >= 10 lock in share mode;
  
  -- 查看锁的情况
 mysql> select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
 +---------------+-------------+------------+-----------+-----------+------------------------+
 | object_schema | object_name | index_name | lock_type | lock_mode | lock_data              |
 +---------------+-------------+------------+-----------+-----------+------------------------+
 | heima_learn   | user_innodb | NULL       | TABLE     | IS        | NULL                   |
 | heima_learn   | user_innodb | PRIMARY    | RECORD    | S         | 10                     |
 | heima_learn   | user_innodb | PRIMARY    | RECORD    | S         | supremum pseudo-record |
 | heima_learn   | user_innodb | PRIMARY    | RECORD    | S         | 15                     |
 +---------------+-------------+------------+-----------+-----------+------------------------+
 4 rows in set (0.00 sec)
 ​
 -- 可以看出加了,id = 10的行锁,id = 15及id > 10 到 id = 15的临键锁,id = 15 及id > 15 的临键锁
 ​
 ​
 -- 当继续执行下面SQL时 
 select * from user_innodb where id >= 9 lock in share mode;
 ​
  
  -- 查看锁的情况
 mysql> select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
 +---------------+-------------+------------+-----------+-----------+------------------------+
 | object_schema | object_name | index_name | lock_type | lock_mode | lock_data              |
 +---------------+-------------+------------+-----------+-----------+------------------------+
 | heima_learn   | user_innodb | NULL       | TABLE     | IS        | NULL                   |
 | heima_learn   | user_innodb | PRIMARY    | RECORD    | S         | 10                     |
 | heima_learn   | user_innodb | PRIMARY    | RECORD    | S         | supremum pseudo-record |
 | heima_learn   | user_innodb | PRIMARY    | RECORD    | S         | 10                     |
 | heima_learn   | user_innodb | PRIMARY    | RECORD    | S         | 15                     |
 +---------------+-------------+------------+-----------+-----------+------------------------+
 --可以看出,此时的锁的情况是 id = 10的行锁,id = 10 到 id > 10 的临键锁,id = 15 的行锁
希望我的笔记给与你学习中的你有帮助!加油!
引用文章地址:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

匠造一知识

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值