之前我们学了mysql的查询与高级查询和存储过程今天我们来学习mysql的高级进阶
mysql的进阶操作就是多个表进行互相关联的查询操作,首先准备多个数据表。
建多个表
DROP TABLE IF EXISTS `class`;
CREATE TABLE `class` (
`cid` int(11) NOT NULL AUTO_INCREMENT,
`caption` varchar(32) NOT NULL,
PRIMARY KEY (`cid`)
) ENGINE=innoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
# innoDB 有外键约束 myisam 注释的作用
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`cid` int(11) NOT NULL AUTO_INCREMENT,
`cname` varchar(32) NOT NULL,
`teacher_id` int(11) NOT NULL,
PRIMARY KEY (`cid`),
KEY `fk_course_teacher` (`teacher_id`),
CONSTRAINT `fk_course_teacher` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`tid`)
) ENGINE=innoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `score`;
CREATE TABLE `score` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`student_id` int(11) NOT NULL,
`course_id` int(11) NOT NULL,
`num` int(11) NOT NULL,
PRIMARY KEY (`sid`),
KEY `fk_score_student` (`student_id`),
KEY `fk_score_course` (`course_id`),
CONSTRAINT `fk_score_course` FOREIGN KEY (`course_id`) REFERENCES `course` (`cid`),
CONSTRAINT `fk_score_student` FOREIGN KEY (`student_id`) REFERENCES `student` (`sid`)
) ENGINE=innoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`gender` char(1) NOT NULL,
`class_id` int(11) NOT NULL,
`sname` varchar(32) NOT NULL,
PRIMARY KEY (`sid`),
KEY `fk_class` (`class_id`),
CONSTRAINT `fk_class` FOREIGN KEY (`class_id`) REFERENCES `class` (`cid`)
) ENGINE=innoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`tid` int(11) NOT NULL AUTO_INCREMENT,
`tname` varchar(32) NOT NULL,
PRIMARY KEY (`tid`)
) ENGINE=innoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
正则表达式
选项 | 匹配说明 | 例子 | 匹配值 |
---|---|---|---|
^ | 文本开始字符 | '^b'匹配以字母b开头的字符串 | book, big,banana,bike |
. | 任何单个字符 | 'b.t'匹配任何b和t之间有一个字符 | bit, bat,but, bite |
* | 0个或多个在它前面的字符 | 'f*n'匹配字符n前面有任意n个字符f | fn, fan,faan, abcn |
+ | 前面的字符一次或多次 | 'ba+'匹配以b开头后面紧跟至少一个a | ba, bay,bare, battle |
字符串 | 包含指定字符串的文本 | 'fa' | fan, afa,faad |
[字符集合] | 字符集合中的任一个字符 | '[xz]'匹配x或者z | dizzy,zebra, x-ray, extra |
[^] | 不在括号中的任何字符 | '[^abc]'匹配任何不包含a、b或c的字符串 | desk, fox,f8ke |
字符串{n} | 前面的字符串至少n次 | b{2}匹配2个或更多的b | bbb, bbbb,bbbbbb |
字符串{n,m} | 前面的字符串至少n次,至多m次 | b{2,4}匹配最少2个,最多4个b | bb, bbb,bbbb |
SELECT * FROM `teacher` WHERE `tname` REGEXP '^yang';
视图
视图(view)是一种虚拟存在的表,是一个逻辑表,本身并不包含数据。其内容由查询定义。用来创建视图的表叫基表,通过视图,可以展现基表的部分数据。视图数据来自定义视图的查询中使用的表,使用视图动态生成。
视图的优点
- 简单。使用视图的用户完全不需要关心后面对应的表的结构、关联条件和筛选条件,对用户来说已经是过滤好的复合条件的结果集。
- 安全。使用视图的用户只能访问他们被允许查询的结果集,对表的权限管理并不能限制到某个行某个列,但是通过视图就可以简单的实现。
- 数据独立。一旦视图的结构确定了,可以屏蔽表结构变化对用户的影响,源表增加列对视图没有影响;源表修改列名,则可以通过修改视图来解决,不会造成对访问者的影响。
视图的作用
-
可复用,减少重复语句书写,类似程序中函数的作用。
-
重构代码,分表。假如因为某种需求,需要将 user 拆成表
usera
和表userb
,如果应用程序使用 sql 语句:select * from user
那就会提示该表不存在;若此时创建视图create view user as select a.name,a.age,b.sex from usera as a, userb as b where a.name=b.name;
,则只需要更改数据库结构,而不需要更改应用程序。 -
逻辑更清晰,屏蔽查询细节,关注数据返回。
-
权限控制,某些表对用户屏蔽,但是可以给该用户通过视图来对该表操作。
CREATE VIEW <视图名> AS <SELECT语句>
-- 创建视图
-- 查询 1 课程比 2 课程成绩高的所有学生的学
号;
CREATE VIEW view_test1 AS SELECT
A.student_id
FROM
(
SELECT
student_id,
num
FROM
score
WHERE
course_id = 1
) AS A -- 12
LEFT JOIN (
SELECT
student_id,
num
FROM
score
WHERE
course_id = 2
) AS B -- 11
ON A.student_id = B.student_id
WHERE
A.num >
IF (isnull(B.num), 0, B.num);
流程控制
-
IF 语句
IF condition THEN
...
ELSEIF condition THEN
...
ELSE
...
END IF -
CASE 语句
-- 相当于switch语句
CASE value
WHEN value THEN ...
WHEN value THEN ...
ELSE ...
END CASE -
WHILE 语句
WHILE condition DO
...
END WHILE; -
LEAVE 语句
-- 相当于break
LEAVE label;
使用例子如下:
-- LEAVE语句退出循环或程序块,只能和BEGIN ... END,LOOP,REPEAT,WHILE语句配合使用
-- 创建存储过程
DELIMITER //
CREATE PROCEDURE example_leave(OUT sum INT)
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE s INT DEFAULT 0;
while_label:WHILE i<=100 DO
SET s = s+i;
SET i = i+1;
IF i=50 THEN
-- 退出WHILE循环
LEAVE while_label;
END IF;
END WHILE;
SET sum = s;
END
//
DELIMITER ;
-- 调用存储过程
CALL example_leave(@sum);
SELECT @sum;
-
ITERATE语句
-- 相当于 continue
ITERATE label -
LOOP 语句
-- 相当于 while(true) {...}
LOOP
...
END LOOP
-- 可以通过LEAVE语句退出循环
使用例子如下:
-- 创建存储过程
DELIMITER
CREATE PROCEDURE example_loop(OUT sum INT)
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE s INT DEFAULT 0;
loop_label:LOOP
SET s = s+i;
SET i = i+1;
IF i>100 THEN
-- 退出LOOP循环
LEAVE loop_label;
END IF;
END LOOP;
SET sum = s;
END
DELIMITER ;
-- 调用存储过程
CALL example_loop(@sum);
SELECT @sum;
-
REPEAT 语句
-- 相当于 do .. while(condition)
REPEAT
...
UNTIL condition
END REPEAT
使用例子如下:
DELIMITER
CREATE PROCEDURE example_repeat(OUT sum INT)
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE s INT DEFAULT 0;
REPEAT
SET s = s+i;
SET i = i+1;
UNTIL i > 100
END REPEAT;
SET sum = s;
END
DELIMITER ;
-- 调用存储过程
CALL example_repeat(@sum);
SELECT @sum;
触发器
触发器(trigger)是 MySQL 提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程。它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行 DML 操作(insert,delete,update)时就会激活它执行。
构成触发器的4要素如下:
-
监视对象:table
-
监视事件:insert、update、delete
-
触发时间:before ,after
-
触发事件:insert、update、delete
CREATE TRIGGER trigger_name
trigger_time trigger_event
ON tbl_name FOR EACH ROW
[trigger_order]
trigger_body -- 此处写执行语句-- trigger_body: 可以一个语句,也可以是多个语句;多个语句写在 BEGIN ... END 间
-- trigger_time: { BEFORE | AFTER }
-- trigger_event: { INSERT | UPDATE | DELETE }
-- trigger_order: { FOLLOWS | PRECEDES } other_trigger_name
使用例子如下:
CREATE TABLE `work` (
`id` INT PRIMARY KEY auto_increment,
`address` VARCHAR (32)
) DEFAULT charset = utf8 ENGINE = innoDB;
CREATE TABLE `time` (
`id` INT PRIMARY KEY auto_increment,
`time` DATETIME
) DEFAULT charset = utf8 ENGINE = innoDB;
-- 创建触发器
CREATE TRIGGER trig_test1 AFTER INSERT
ON `work` FOR EACH ROW
INSERT INTO `time` VALUES(NULL,NOW());
触发器的 NEW 和 OLD
在 INSERT 型触发器中,NEW 用来表示将要(BEFORE)或已经(AFTER)插入的新数据。
在 DELETE 型触发器中,OLD 用来表示将要或已经被删除的原数据。
在 UPDATE 型触发器中,OLD 用来表示将要或已经被修改的原数据,NEW 用来表示将要或已经修改为的新数据。
NEW.columnName (columnName为相应数据表某一列名)
OLD.columnName (columnName为相应数据表某一列名)
触发器的使用案例
CREATE TABLE `goods` (
`id` INT PRIMARY KEY auto_increment,
`name` VARCHAR (32),
`num` SMALLINT DEFAULT 0
);
CREATE TABLE `order` (
`id` INT PRIMARY KEY auto_increment,
`goods_id` INT,
`quantity` SMALLINT COMMENT '下单数量'
);
INSERT INTO goods VALUES (NULL, '手机', 40);
INSERT INTO goods VALUES (NULL, '电脑', 63);
INSERT INTO goodS VALUES (NULL, '平板', 87);
INSERT INTO `order` VALUES (NULL, 1, 3);
INSERT INTO `order` VALUES (NULL, 2, 4);
需求1:客户修改订单购买的数量,在原来购买数量的基础上减少2个
- delimiter
-- delimiter是mysql分隔符,在mysql客户端中分隔符默认是分号 ;。如果一次输入的语句较多,并且语句中间有分号,这时需要重新指定一个特殊的分隔符。
delimiter
CREATE TRIGGER trig_order_1 AFTER INSERT
ON `order` FOR EACH ROW
BEGIN
UPDATE goods SET num = num - 2 WHERE id = 1;
END
delimiter ;
INSERT
需求2:客户修改订单购买的数量,商品表的库存数量自动改变
delimiter
CREATE TRIGGER trig_order_2 BEFORE UPDATE
ON `order` FOR EACH ROW
BEGIN
UPDATE goods SET num=num+old.quantity - new.quantity WHERE id = new.goods_id;
END
delimiter ;
-- 测试
UPDATE `order` SET quantity = quantity+2 WHERE id = 1;
数据库游标
游标是针对行操作的,对从数据库中 select 查询得到的结果集的每一行可以进行分开的、独立的、相同或者不相同的操作。对于取出多行数据集,需要针对每行操作,可以使用游标。
游标常用于存储过程、函数、触发器、事件,游标相当于迭代器。
-
定义游标
DECLARE cursor_name CURSOR FOR select_statement;
-
打开游标
OPEN cursor_name;
-
取游标数据
FETCH cursor_name INTO var_name[,var_name,......]
-
取游标数据
FETCH cursor_name INTO var_name[,var_name,......]
-
关闭游标
CLOSE curso_name;
-
释放
DEALLOCATE cursor_name;
-
设置游标结束标志
DECLARE done INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET done = 1; -- done 为标记为
使用例子如下:
CREATE PROCEDURE proc_while (IN age_in INT, OUT total_out INT)
BEGIN
-- 创建 用于接收游标值的变量
DECLARE p_id,p_age,p_total INT ;
DECLARE p_sex TINYINT ;
-- 注意:接收游标值为中文时,需要给变量 指定字符集utf8
DECLARE p_name VARCHAR (32) CHARACTER SET utf8 ;
-- 游标结束的标志
DECLARE done INT DEFAULT 0 ;
-- 声明游标
DECLARE cur_teacher CURSOR FOR SELECT
teacher_id,
teacher_name,
teacher_sex,
teacher_age
FROM
teacher
WHERE
teacher_age > age_in ; -- 指定游标循环结束时的返回值
DECLARE CONTINUE HANDLER FOR NOT found
SET done = 1 ;
-- 打开游标
OPEN cur_teacher ;
-- 初始化 变量
SET p_total = 0 ;
-- while 循环
WHILE done != 1 DO
FETCH cur_teacher INTO p_id,
p_name,
p_sex,
p_age ;
IF done != 1 THEN
SET p_total = p_total + 1 ;
END IF;
END WHILE ;
-- 关闭游标
CLOSE cur_teacher ;
-- 将累计的结果复制给输出参数
SET total_out = p_total ;
END//
delimiter ;
-- 调用
SET @p_age =20;
CALL proc_while(@p_age, @total);
SELECT @total;
数据库权限管理
-
创建用户
CREATE USER username@host IDENTIFIED BY password;
-- host 指定该用户在哪个主机上可以登陆,如果是本地用户可用localhost,如果想让该用户可以从任意远程主机登陆,可以使用通配符 % -
用户授权
GRANT privileges ON databasename.tablename TO 'username'@'host' WITH GRANT OPTION;
-- privileges:用户的操作权限,如SELECT,INSERT,UPDATE等,如果要授予所的权限则使用ALL
-- databasename.tablename 如果是 . 表示任意数据库以及任意表
-- WITH GRANT OPTION 这个选项表示该用户可以将自己拥有的权限授权给别人。
-- 在创建操作用户的时候不指定WITH GRANT OPTION 选项导致,该用户就不能使用 GRANT 命令创建用户或者给其它用户授权.
-- 如果不想这个用户有这个 grant 的权限,则不要加该 WITH GRANT OPTION 选项 -
对视图授权
GRANT select, SHOW VIEW ON
databasename
.tablename
to 'username'@'host'; -
刷新权限
-- 修改权限后需要刷新权限
FLUSH PRIVILEGES;
Mysql的远程连接
远程连接需要修改配置, 修改mysqld.cnf 中 bind-address 设置IP:
# mysqld.cnf
# vi /etc/mysql/mysql.conf.d/mysqld.cnf
bind-address=127.0.0.1
修改 mysql.user 表,可以修改用户配置.
-- 修改user表
select `user`, `host` from `mysql`.`user`;
update user set host='%' where user='root';
在MySQL的浩瀚宇宙中,我们已经穿梭于性能调优的星辰大海,遨游于复杂查询的逻辑迷宫,攀登过数据库架构设计的巍峨高峰,穿越了安全策略的迷雾森林,最终抵达了高可用性和可扩展性的广阔天地。然而,这趟旅程并非终点,而是一个新的起点。
随着技术的不断演进,MySQL的高级进阶之路充满了未知与挑战。想象一下,未来的数据库世界可能充满了人工智能的智能优化、量子计算的革命性突破,以及边缘计算的全新应用场景。在这样的背景下,我们不仅需要掌握现有的知识,更需要培养一种能够快速适应新技术、新方法的能力。
在追求技术卓越的同时,我们也不应忽视团队协作的力量。数据库专家、开发人员、系统管理员和业务分析师之间的紧密合作,是实现高效数据库解决方案的关键。通过有效的沟通和协作,我们能够将复杂的技术问题转化为实际可行的解决方案,为组织的成功奠定坚实的基础。
让我们以开放的心态迎接每一个挑战,以创新的精神探索每一个未知领域。通过不断的学习和实践,我们不仅能够提升自己的专业技能,还能够为整个数据库社区贡献自己的力量。在这个不断变化的世界里,让我们成为引领变革的先锋,用我们的智慧和热情,书写MySQL高级进阶的辉煌篇章。
记住,技术的世界没有终点,只有不断前进的旅程。让我们一起,拥抱变化,勇往直前,探索MySQL的无限可能。