-- ########## 01、集合逻辑 ########## -- MySQL中,只实现了一种集合逻辑:逻辑与,有两种用法:UNION 和 UNION ALL -- 临时表1 CREATE TABLE temp1 ( id INT NOT NULL ); -- 临时表2 CREATE TABLE temp2 ( id INT NOT NULL ); -- 模拟数据 INSERT INTO temp1 VALUES(1), (2); INSERT INTO temp2 VALUES(2), (3); SELECT * FROM temp1; SELECT * FROM temp2; -- UNION:进行逻辑与操作的集合中相同的元素在结果中出现一次 ( SELECT * FROM temp1 ) UNION ( SELECT * FROM temp2 ); -- UNION ALL:进行逻辑与操作的集合中相同的元素在结果中都出现 ( SELECT * FROM temp1 ) UNION ALL ( SELECT * FROM temp2 ); -- 使用UNION 或 UNION ALL进行逻辑与操作后得到的结果还是一个集合 SELECT * FROM ( ( SELECT * FROM temp1 ) UNION ( SELECT * FROM temp2 ) ) AS temp; SELECT * FROM ( ( SELECT * FROM temp1 ) UNION ALL ( SELECT * FROM temp2 ) ) AS temp; -- ########## 02、视图(View) ########## -- 视图:View,从其他表中抽取部分 或 全部字段形成的一种映射,在某种程度上可以理解为临时表 -- 使用场景:一般用于方便查询 -- 创建视图的格式: -- create view 视图名称 -- AS -- SQL语句 -- 1、基于单表的视图操作 -- 需求:查询学生信息(涉及学生姓名 和 学生性别),只给使用者查看这两列字段的内容 -- 创建视图 CREATE VIEW vw_studentinfo AS SELECT studentname AS 学生姓名, studentgender AS 学生性别 FROM studentinfo; -- 使用视图进行查询 SELECT * FROM vw_studentinfo; -- 使用视图进行新增 -- 错误代码: 1423 Field of view 'test.vw_studentinfo' underlying table doesn't have a default value INSERT INTO vw_studentinfo VALUES('孙权', '男'); SELECT * FROM studentinfo; -- 使用视图进行修改,可以执行成功 -- 注意:对视图中数据的修改最终反映在视图基于的原始表的数据中 UPDATE vw_studentinfo SET 学生性别 = '女' WHERE 学生姓名 = '关羽'; -- 使用视图进行查询 SELECT * FROM vw_studentinfo; -- 对视图抽取的原始表进行查询 SELECT * FROM studentinfo; -- 使用视图进行删除,可以执行成功 -- 注意:对视图中数据的删除最终反映在视图基于的原始表的数据中 DELETE FROM vw_studentinfo WHERE 学生姓名 = '关羽'; -- 使用视图进行查询 SELECT * FROM vw_studentinfo; -- 对视图抽取的原始表进行查询 SELECT * FROM studentinfo; -- 2、基于多表连接的视图操作 -- 需求:查询部门信息及部门对应的员工姓名 CREATE VIEW vw_dept_employee AS SELECT d.deptid AS 部门编号, d.deptname AS 部门名称, e.employeename AS 员工姓名 FROM dept AS d LEFT JOIN employee AS e ON d.deptid = e.deptid; -- 使用视图进行查询 SELECT * FROM vw_dept_employee; -- 使用视图进行新增 -- 错误代码: 1394 Can not insert into join view 'test.vw_dept_employee' without fields list INSERT INTO vw_dept_employee VALUES(NULL, '后勤部', '祝融'); -- 使用视图进行修改 UPDATE vw_dept_employee SET 部门名称 = '人力资源部' WHERE 部门编号 = 4; -- 使用视图进行查询 SELECT * FROM vw_dept_employee; -- 对视图抽取的原始表进行查询 SELECT * FROM dept; -- 使用视图进行修改 UPDATE vw_dept_employee SET 员工姓名 = '赵云' WHERE 部门编号 = 1 AND 员工姓名 = '张飞'; -- 使用视图进行查询 SELECT * FROM vw_dept_employee; -- 对视图抽取的原始表进行查询 SELECT * FROM employee; -- 使用视图进行修改 UPDATE vw_dept_employee SET 员工姓名 = '张飞' WHERE 员工姓名 = '赵云'; -- 使用视图进行查询 SELECT * FROM vw_dept_employee; -- 对视图抽取的原始表进行查询 SELECT * FROM employee; -- 使用视图进行修改 -- 错误代码: 1393 Can not modify more than one base table through a join view 'test.vw_dept_employee' -- 因为对连接视图来说,修改一条记录对于其他的部门名称也是开发部的记录无法一次都修改 UPDATE vw_dept_employee SET 员工姓名 = '赵云', 部门名称 = '通宵部' WHERE 员工姓名 = '张飞'; -- 变通的写法,可以执行成功 UPDATE vw_dept_employee SET 部门名称 = '通宵部' WHERE 部门编号 = 1; -- 使用视图进行查询 SELECT * FROM vw_dept_employee; -- 对视图抽取的原始表进行查询 SELECT * FROM dept; -- 使用视图进行删除,连接视图不给删除数据 -- 错误代码: 1395 Can not delete from join view 'test.vw_dept_employee' DELETE FROM vw_dept_employee WHERE 员工姓名 = '张飞'; -- 错误代码: 1395 Can not delete from join view 'test.vw_dept_employee' DELETE FROM vw_dept_employee WHERE 部门名称 = '通宵部'; -- 视图的意义:封装性的体现 -- 使用者不需要知道数据的内在逻辑,也不需要懂高深的SQL语法,只要拿着视图就可以使用,很方便,降低了开发难度,也在一定程度上保证了数据安全 -- ########## 03、事务(Transaction) ########## -- 事务的理解:一件事情的处理需要多个步骤,当多个步骤中的某一个步骤发生异常时, -- 考虑接着如果处理这个事情是恢复到最初的状态或是恢复到之前的某一个状态,或者继续做下去的机制 -- 从操作的简洁性上考虑,将这件事情恢复到最初的状态,再重新执行,是最简单的处理方式 -- 术语:在事情的处理过程中,从某一个后续的状态恢复至之前的莫一个状态的动作,称为事务的回滚(rollback) -- 类比:小学体育课上的翻垫子 -- MySQL是支持事务的关系型数据库(安装时选择支持事务的数据库引擎InnoDB) -- 事务通常用在增加、修改、删除(DML)操作中 CREATE TABLE test ( id INT ); -- MySQL中的事务处理有两种方式: -- 方式1、使用BEGIN、ROLLBACK、COMMIT关键字来实现 -- BEGIN: 开始事务 -- ROOLBACK: 事务回滚 -- COMMIT: 事务确认(提交事务) BEGIN; INSERT INTO test VALUES(1); INSERT INTO test VALUES(2); -- 先执行BEGIN及后面2条插入语句,再执行下句,可以查询出2条记录 SELECT * FROM test; BEGIN; INSERT INTO test VALUES(3); INSERT INTO test VALUES(4); COMMIT; -- 先执行BEGIN及后面2条插入语句以及COMMIT,再执行下句,可以查询出4条记录(前2条记录是上一步操作插入的) SELECT * FROM test; -- 发现插入后,写COMMIT语句 和 不写COMMIT语句效果一致 BEGIN; INSERT INTO test VALUES(5); ROLLBACK; -- 先执行BEGIN及后面1条插入语句以及ROLLBACK,再执行下句,可以查询出4条记录(前2条记录是上一步操作插入的) -- 即208行的插入操作是无效的 SELECT * FROM test; -- 方式2、对于MySQL的DML操作,默认都是自动提交(事务)的 -- 所以,想看到手工操作事务的效果,首先设置MySQL的自动提交参数为否 -- 设置MySQL的自动提交参数为否,也就是设置需要手动提交 SET AUTOCOMMIT = 0; INSERT INTO test VALUES(6); -- 在当前的SQLyog客户端中查询test表,显示数据6已经插入进去了,但是这是假象 -- 在命令行窗口中查询test表,数值6是不存在的 SELECT * FROM test; -- 既然设置了AUTOCOMMIT参数为0,禁止自动提交,那么就需要显式的进行手动提交 COMMIT; -- 手动提交后,不论在当前的SQLyog客户端中查看test表,还是在命令行窗口中查看test表,数值6真的插入进去了 SELECT * FROM test; -- 操作后恢复默认设置 SET AUTOCOMMIT = 1; -- ########## 04、变量 ########## -- 1、变量定义:declare 变量名 数据类型 [默认值]; DECLARE i INT DEFAULT 0; DECLARE j DATETIME; DECLARE k VARCHAR(10); -- 2、用户变量:使用@ + 英文字符 -- 3、用户变量赋值:set 变量名 = 表达式值; -- SELECT 直接量 INTO 用户变量 SELECT 'test' INTO @i; SELECT @i; -- SET 用户变量 = 直接量 SET @j = '中华人民共和国'; SELECT @j; SET @k = 1 + 2 + 3; SELECT @k; -- ########## 05、存储过程(Stored Procedure) ########## -- 存储过程:Stored Procedure -- 数据库中的SQL语句在执行时都是需要先编译再执行的,存储过程就是这样一组为了完成特定功能的SQL语句 -- 存储过程经过编译存储在数据库中,并不立即执行,而是使用者通过自定义的存储过程的名称的调用并赋予制定的参数来进行执行 -- 格式: -- create procedure 存储过程名([参数1, 参数2, ...]) -- [特性] -- 过程体 -- 存储过程的创建和使用: -- 1、首先需要使用DELIMITER关键声明分隔符,因为MySQL中默认以;作为分隔符 -- 所以如果没有再声明一个分隔符,MySQL编译器就会把存储过程当作SQL语句来进行处理,那样存储过程的编译过程就会出错 -- 注意:使用完成后需要把设置的分隔符再还原回去 -- 2、存储过程根据参数的有无分为:无参、有参输入、有参输出、有参输入输出 四种形式 SELECT * FROM userinfo; DESC userinfo; INSERT INTO userinfo VALUES(1, '诸葛亮', '888'), (2, '司马懿', '666'); -- 1、无参的存储过程 -- 声明分隔符为 $$ DELIMITER $$ CREATE PROCEDURE proc_select_userinfo() -- 使用BEGIN...END语句块 BEGIN SELECT * FROM userinfo; END -- 下句使用声明的分隔符,表明到这里都是需要编译的存储过程的内容,不需要执行 $$ -- 使用完毕后,再把分隔符还原为分号; DELIMITER ; -- 调用存储过程:CALL 存储过程名称(); CALL proc_select_userinfo(); -- 在无参的存储过程中使用用户变量 -- 声明分隔符为 $$ DELIMITER $$ CREATE PROCEDURE proc_test_var() -- 使用BEGIN...END语句块 BEGIN SELECT CONCAT(@i, 'MySQL'); END -- 下句使用声明的分隔符,表明到这里都是需要编译的存储过程的内容,不需要执行 $$ -- 使用完毕后,再把分隔符还原为分号; DELIMITER ; SET @i = '真简单'; CALL proc_test_var(); -- 显示结果为:真简单MySQL -- 2、输入参数的存储过程 -- 声明分隔符为 $$ DELIMITER $$ CREATE PROCEDURE proc_select_userinfo_by_id(IN _id INT) -- 使用BEGIN...END语句块 BEGIN SELECT * FROM userinfo WHERE userid = _id; END -- 下句使用声明的分隔符,表明到这里都是需要编译的存储过程的内容,不需要执行 $$ -- 使用完毕后,再把分隔符还原为分号; DELIMITER ; -- 调用存储过程:CALL 存储过程名(输入参数值); CALL proc_select_userinfo_by_id(1); -- 可以在其他窗口(比如:命令行窗口)中执行编译好的存储过程 CALL proc_select_userinfo_by_id(2); -- 3、输出参数的存储过程 -- 声明分隔符为 $$ DELIMITER $$ CREATE PROCEDURE proc_select_count_userinfo(OUT _number INT) -- 使用BEGIN...END语句块 BEGIN SELECT COUNT(*) INTO _number FROM userinfo; END -- 下句使用声明的分隔符,表明到这里都是需要编译的存储过程的内容,不需要执行 $$ -- 使用完毕后,再把分隔符还原为分号; DELIMITER ; -- 调用存储过程,使用用户变量作为输出参数 CALL proc_select_count_userinfo(@outparam); -- 查看输出参数用户变量的内容 SELECT @outparam AS `userinfo表的行数`; -- 4、输入输出参数的存储过程 -- 声明分隔符为 $$ DELIMITER $$ CREATE PROCEDURE proc_select_count_userinfo_by_id(INOUT _param INT) -- 使用BEGIN...END语句块 BEGIN SELECT COUNT(*) INTO _param FROM userinfo GROUP BY userid HAVING userid = _param; END -- 下句使用声明的分隔符,表明到这里都是需要编译的存储过程的内容,不需要执行 $$ -- 使用完毕后,再把分隔符还原为分号; DELIMITER ; -- 调用存储过程,定义并给用户变量赋值,调用存储过程并使用赋值的用户变量,最后获取取得输出参数的用户变量的内容 SET @inoutparam = 2; CALL proc_select_count_userinfo_by_id(@inoutparam); SELECT @inoutparam AS `userid为2的用户记录条数`; -- 5、存储过程中的变量作用域 -- 内部的变量在其作用域范围内可以进行访问,在执行到end时,内部变量消失,在其作用域外,变量不可以使用 -- 声明分隔符为 $$ DELIMITER $$ CREATE PROCEDURE proc_test_var_scope() -- 使用BEGIN...END语句块 BEGIN DECLARE i VARCHAR(10) DEFAULT '外层变量'; BEGIN DECLARE i VARCHAR(10) DEFAULT '内层变量'; SELECT i; END; SELECT i; END -- 下句使用声明的分隔符,表明到这里都是需要编译的存储过程的内容,不需要执行 $$ -- 使用完毕后,再把分隔符还原为分号; DELIMITER ; CALL proc_test_var_scope(); -- 使用 DROP PROCEDURE 存储过程名; 对存储过程进行删除 DROP PROCEDURE proc_test_var_scope; DESC test; SELECT * FROM test; -- 6、存储过程中使用控制语句(选择结构):if...then...elseif...else...end if -- 声明分隔符为 $$ DELIMITER $$ CREATE PROCEDURE proc_test_if(IN param INT) -- 使用BEGIN...END语句块 BEGIN DECLARE i INT DEFAULT 123; IF param = 1 THEN INSERT INTO test VALUES(i); ELSEIF param = 2 THEN UPDATE test SET id = i * 2; ELSE UPDATE test SET id = i * 3; END IF; END; -- 下句使用声明的分隔符,表明到这里都是需要编译的存储过程的内容,不需要执行 $$ -- 使用完毕后,再把分隔符还原为分号; DELIMITER ; DROP PROCEDURE proc_test_if; TRUNCATE TABLE test; CALL proc_test_if(1); SELECT * FROM test; CALL proc_test_if(2); SELECT * FROM test; CALL proc_test_if(3); SELECT * FROM test; -- 7、存储过程中使用控制语句(选择结构):case...when...then...else...end case -- 声明分隔符为 $$ DELIMITER $$ CREATE PROCEDURE proc_test_case(IN param INT) -- 使用BEGIN...END语句块 BEGIN DECLARE i INT DEFAULT 123; CASE param WHEN 1 THEN INSERT INTO test VALUES(i); WHEN 2 THEN UPDATE test SET id = i * 2; ELSE UPDATE test SET id = i * 3; END CASE; END; -- 下句使用声明的分隔符,表明到这里都是需要编译的存储过程的内容,不需要执行 $$ -- 使用完毕后,再把分隔符还原为分号; DELIMITER ; TRUNCATE TABLE test; CALL proc_test_case(1); SELECT * FROM test; CALL proc_test_case(2); SELECT * FROM test; CALL proc_test_case(3); SELECT * FROM test; -- 8、存储过程中使用控制语句(循环结构):while...do...end while -- 声明分隔符为 $$ DELIMITER $$ CREATE PROCEDURE proc_test_while() -- 使用BEGIN...END语句块 BEGIN DECLARE i INT; SET i = 0; WHILE i < 5 DO INSERT INTO test VALUES(i); SET i = i + 1; END WHILE; END; -- 下句使用声明的分隔符,表明到这里都是需要编译的存储过程的内容,不需要执行 $$ -- 使用完毕后,再把分隔符还原为分号; DELIMITER ; TRUNCATE TABLE test; CALL proc_test_while(); SELECT * FROM test; -- 9、存储过程中使用控制语句(循环结构):repeat...until...end repeat -- 声明分隔符为 $$ DELIMITER $$ CREATE PROCEDURE proc_test_repeat() -- 使用BEGIN...END语句块 BEGIN DECLARE i INT; SET i = 0; REPEAT INSERT INTO test VALUES(i); SET i = i + 1; UNTIL i >= 5 END REPEAT; END; -- 下句使用声明的分隔符,表明到这里都是需要编译的存储过程的内容,不需要执行 $$ -- 使用完毕后,再把分隔符还原为分号; DELIMITER ; TRUNCATE TABLE test; CALL proc_test_repeat(); SELECT * FROM test; -- 注意:while是在执行前进行检查,repeat在执行后进行检查 -- 10、存储过程中使用控制语句(循环结构):loop...end loop -- 声明分隔符为 $$ DELIMITER $$ CREATE PROCEDURE proc_test_loop() -- 使用BEGIN...END语句块 BEGIN DECLARE i INT; SET i = 0; LOOP_LABLE: LOOP INSERT INTO test VALUES(i); SET i = i + 1; IF i >= 5 THEN LEAVE LOOP_LABLE; END IF; END LOOP; END; -- 下句使用声明的分隔符,表明到这里都是需要编译的存储过程的内容,不需要执行 $$ -- 使用完毕后,再把分隔符还原为分号; DELIMITER ; TRUNCATE TABLE test; CALL proc_test_loop(); SELECT * FROM test;