#############################【mysql初级篇学习笔记】#######################################
#############################学校作业二:#####################################
#2.创建数据库[学生选课数据库](不需要代码,按照上次上机中所创建数据库步骤创建);
CREATE TABLE 课程表(课程号 CHAR(4) PRIMARY KEY,课程名 CHAR(40) NOT NULL,先行课程号 CHAR(4),学分 SMALLINT NOT NULL);
#3.创建[课程表]代码;课程表(课程号 char(4),课程名 char(40),先行课程号 char(4),学分 smallint)
# 要求设置:主键(课程号)、非空(课程名, 学分)
CREATE TABLE 学生表(学号 CHAR(9) PRIMARY KEY,姓名 CHAR(20),性别 CHAR(2),年龄 SMALLINT,专业 CHAR(20) NOT NULL);
#4.创建[学生表]代码;学生表(学号 char(9),姓名char(20),性别char(2),年龄smallint,专业char(20))
CREATE TABLE 选课表(学号 CHAR(9),课程号 CHAR(4),分数 SMALLINT,
FOREIGN KEY(学号) REFERENCES 学生表(学号),
FOREIGN KEY(课程号) REFERENCES 课程表(课程号));
#5.修改[课程表],将“学分”列的数据类型改为int;
ALTER TABLE 课程表 MODIFY 学分 INT;
#ALTER TABLE 课程表 alter column 学分 char(2); ==>错误的
#6.修改[学生表],在表中增加一列,属性名为“备注”,数据类型为char(20);
ALTER TABLE 学生表 ADD 备注 CHAR(20);
#7.修改[学生表],对“姓名”列增加唯一约束条件;
ALTER TABLE 学生表 ADD UNIQUE(姓名);
#############################################################################################
#having不可以单独使用,只能和group by一起使用;where 和 having后写过滤条件,建议方式一where,因为执行效率高
#结论:当过滤条件中有聚合函数时,必须写在having;若没有,则having和where均可,但建议使用where(必须)。
SELECT department_id,MAX(salary) FROM employees WHERE department_id IN (10,20,30,40,50) GROUP BY department_id HAVING MAX(salary)>10000;
SELECT department_id,MAX(salary) FROM employees GROUP BY department_id HAVING MAX(salary)>10000 AND department_id IN (10,30,40,50);
SELECT ...,...,... 包含聚合函数
FROM ... (LEFT / RIGHT)JOIN ... ON 多表连接条件
(LEFT / RIGHT)JOIN ... ON ...
WHERE 多表的连接条件 AND 不包含聚合函数的过滤条件
ORDER BY ...,... (ASC,DESC)
FROM ...,...->ON->(LEFT/RIGHT JOIN)->WHERE->GROUP BY->HAVING->SELECT->DISTINCT->ORDER BY->LIMIT
#2.查询公司员工工资的最大值,最小值,平均值,总和
SELECT MAX(salary) max_sal,MIN(salary) min_sal,AVG(salary) avg_sal,SUM(salary) sum_sal FROM employees;
#3.查询各job_id的员工工资的最大值,最小值,平均值,总和
SELECT job_id,MIN(salary),MAX(salary),AVG(salary),SUM(salary) FROM employees GROUP BY job_id;
SELECT job_id,COUNT(*) FROM employees GROUP BY job_id;
# 5.查询员工最高工资和最低工资的差距(DIFFERENCE)
SELECT MAX(salary),MIN(salary),MAX(salary)-MIN(salary) 'diff' FROM employees;
# 6.查询各个管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内
SELECT MIN(salary) FROM employees WHERE manager_id IS NOT NULL GROUP BY manager_id HAVING MIN(salary)>=6000;
# 7.查询所有部门的名字,location_id,员工数量和平均工资,并按平均工资降序
SELECT d.department_name,d.location_id,COUNT(*),AVG(e.salary)
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id
GROUP BY d.`department_name`,d.`location_id`
HAVING AVG(salary)IS NOT NULL
# 8.查询每个工种、每个部门的部门名、工种名和最低工资
SELECT department_name,job_id,MIN(salary)
FROM departments d JOIN employees e
ON d.`department_id` = e.`department_id`
GROUP BY department_name,job_id;
SELECT e2.last_name,e2.salary
FROM employees e1,employees e2
WHERE e2.`salary` > e1.`salary`#多表的连接条件
AND e1.last_name = 'Abel';
WHERE salary > (SELECT salary
WHERE last_name = 'Abel');
#2.称谓的规范:外查询(或者主查询),内查询(或内查询)
#注意事项:a.子查询要包含在括号内;b.将子查询放在比较条件的右侧;c.单行操作符对应单行子查询,多行操作符对应多行子查询
#单行子查询 单行操作符:<,>,=,<=,>=,<>不等于符号
SELECT employee_id,last_name,(CASE department_id WHEN (SELECT department_id FROM departments WHERE location_id=1800)
#多行子查询 多行操作符:in,【any,all,some(同any) 与单行操作符混合使用】
SELECT employee_id,last_name
WHERE salary IN (SELECT MIN(salary)FROM employees GROUP BY department_id);
SELECT employee_id,last_name,job_id,salary
WHERE salary < ANY(SELECT salary FROM employees WHERE job_id='IT_PROG')
SELECT employee_id,last_name,job_id,salary
WHERE salary < ALL(SELECT salary FROM employees WHERE job_id='IT_PROG')
#【难】题目:查询平均工资最低的部门id【注意:mysql中聚合函数不可以嵌套,其他的也许可以,比如oracle】
FROM (SELECT AVG(salary) avg_sal FROM employees GROUP BY department_id)t_dept_avg_sal
HAVING AVG(salary)=(SELECT MIN(avg_sal)FROM (SELECT AVG(salary) avg_sal FROM employees GROUP BY department_id)t_dept_avg_sal);
#题目:查询员工中工资大于本部门平均工资的员工的last_name,salary和其department_id
SELECT last_name,salary,department_id
WHERE salary > (SELECT AVG(salary)FROM employees e2 WHERE e2.`department_id`=e1.`department_id`);
#题目:查询员工的id,salary,按照department_name排序【难】
SELECT employee_id,salary
WHERE e.`department_id`=d.`department_id`
#结论:在select中,除了group by和limit之外,其他位置都可以声明子查询
SELECT employee_id,last_name,job_id
WHERE 2<=(SELECT COUNT(*) FROM job_history j WHERE e.`employee_id` = j.`employee_id`);
#题目:查询公司管理者的employee_id,last_name,job_id,department_id信息
SELECT DISTINCT mgr.employee_id,mgr.last_name,mgr.job_id,mgr.department_id
FROM employees emp JOIN employees mgr
ON emp.manager_id = mgr.employee_id;
SELECT employee_id,last_name,job_id,department_id
WHERE employee_id IN(SELECT DISTINCT manager_id FROM employees);
SELECT employee_id,last_name,job_id,department_id
WHERE EXISTS(SELECT * FROM employees e2 WHERE e1.`employee_id`=e2.`manager_id`);
#1.查询和Zlotkey相同部门的员工姓名和工资
SELECT last_name,salary,department_id
WHERE e1.`department_id` = (SELECT e2.department_id FROM employees e2 WHERE e2.`last_name`="Zlotkey")
AND e1.`last_name` <> "Zlotkey"
#2.查询工资比公司平均工资高的员工的员工号,姓名和工资。
SELECT employee_id,last_name,salary
WHERE salary > (SELECT AVG(salary) FROM employees);
#3.选择工资大于所有JOB_ID = 'SA_MAN'的员工的工资的员工的last_name, job_id, salary
SELECT last_name,job_id,salary
WHERE salary > ALL(SELECT salary FROM employees WHERE job_id="SA_MAN");
#4.查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名
SELECT employee_id,last_name
WHERE department_id = ANY (SELECT department_id FROM employees WHERE last_name LIKE "%u%");
SELECT employee_id,last_name
WHERE department_id IN (SELECT department_id FROM employees WHERE last_name LIKE "%u%");
#5.查询在部门的location_id为1700的部门工作的员工的员工号
WHERE department_id = SOME (SELECT department_id FROM departments WHERE location_id = 1700);
SELECT e1.last_name,e1.salary,e1.`manager_id`
FROM employees e1,employees e2
WHERE e1.`manager_id` = e2.`employee_id`
AND e2.`last_name` = "King";
WHERE manager_id IN (SELECT employee_id FROM employees WHERE last_name = "King");
#7.查询工资最低的员工信息: last_name, salary
WHERE salary =(SELECT MIN(salary) FROM employees);
WHERE department_id = (SELECT department_id
HAVING AVG(salary)=(SELECT MIN(dept_avgsal)
FROM (SELECT AVG(salary)dept_avgsal
FROM departments d,(SELECT department_id,AVG(salary) avg_sal
WHERE d.`department_id`=t_dept_avg_sal.`department_id`
#9.查询平均工资最低的部门信息和该部门的平均工资(相关子查询)
SELECT d.*,t_dept_avg_sal.`avg_sal`
FROM departments d,(SELECT department_id,AVG(salary) avg_sal
WHERE d.`department_id`=t_dept_avg_sal.`department_id`;
FROM jobs j,(SELECT job_id,AVG(salary) avg_sal
WHERE j.`job_id`=t_max_avg_sal.`job_id`;
HAVING AVG(salary)=(SELECT MAX(avg_sal)
FROM (SELECT AVG(salary) avg_sal
HAVING AVG(salary)>= ALL(SELECT AVG(salary) avg_sal
#11. 查询平均工资高于公司平均工资的部门有哪些?
FROM (SELECT department_id,AVG(salary)avg_sal FROM employees WHERE department_id IS NOT NULL GROUP BY department_id)t_dep_avg_sal
WHERE t_dep_avg_sal.`avg_sal` > (SELECT AVG(salary)FROM employees);
WHERE department_id IS NOT NULL
#12. 查询出公司中所有 manager 的详细信息
SELECT DISTINCT e2.employee_id, e2.last_name, e2.salary
FROM employees e1,employees e2
WHERE e1.`manager_id` = e2.`employee_id`;
SELECT employee_id,last_name,salary
WHERE employee_id IN (SELECT DISTINCT manager_id FROM employees WHERE manager_id IS NOT NULL);
SELECT employee_id, last_name, salary
WHERE e2.manager_id = e1.employee_id);
#13. 各个部门中,最高工资中最低的那个部门的最低工资是多少?
SELECT MAX(salary) max_sal
HAVING MAX(salary) <= ALL(
SELECT MAX(salary) max_sal
#14. 查询平均工资最高的部门的 manager 的详细信息: last_name,department_id, email, salary
SELECT last_name,department_id,email,salary
SELECT department_id,AVG(salary) avg_sal
WHERE d.`department_id` = t_emp_avg_sal.`department_id`
#15. 查询部门的部门号,其中不包括job_id是"ST_CLERK"的部门号
WHERE department_id NOT IN (
SELECT DISTINCT department_id
WHERE job_id = 'ST_CLERK'
WHERE d.`department_id` = e.`department_id`
#16. 选择所有没有管理者的员工的last_name
SELECT last_name,manager_id
WHERE manager_id IS NULL;
WHERE e1.manager_id = e2.employee_id
#17.查询员工号、姓名、雇用时间、工资,其中员工的管理者为 'De Haan'
SELECT employee_id,last_name,hire_date,salary
WHERE last_name = "De Haan"
SELECT employee_id,last_name,hire_date,salary
WHERE last_name = "De Haan"
#18.查询各部门中工资比本部门平均工资高的员工的员工号, 姓名和工资(难)
#19.查询每个部门下的部门人数大于 5 的部门名称
SELECT department_name,department_id
WHERE d.`department_id` = e.`department_id`
#20.查询每个国家下的部门个数大于 2 的国家编号
WHERE l.`location_id`=d.`location_id`
#####################################1.数据库的操作#################################
CREATE DATABASE dbtest3 CHARACTER SET 'gbk';
SHOW CREATE DATABASE dbtest3;
CREATE DATABASE IF NOT EXISTS dbtest5 CHARACTER SET 'utf8';
SELECT DATABASE() FROM DUAL;
SHOW TABLES FROM atguigudb;
SHOW CREATE DATABASE dbtest4;
ALTER DATABASE dbtest4 CHARACTER SET 'gbk';
#【注意】DATABASE 不能改名。一些可视化工具可以改名,它是建新库,
DROP DATABASE IF EXISTS dbtest4;
#####################################2.创建表#################################
SHOW CREATE DATABASE atguigudb;
CREATE TABLE IF NOT EXISTS myemp1(#需要用户具备创建表的权限
SHOW CREATE TABLE myemp1;#如果创建表时没有指定使用的字符集,则默认使用表所在的数据库字符集
#方式二:基于现有的表创建新表(属性+数据 全部copy)
CREATE TABLE myemp2 AS (SELECT employee_id,last_name,salary FROM employees);
SHOW CREATE TABLE myemp2;
#说明1:查询语句中字段的别名,可以作为新创建的表的字段的名称。
#说明2:此时的查询语句可以结构比较丰富,使用前面章节讲过的各种SELECT。
#练习:创建一个表employees_blank,实现对employees表的复制,不包括表数据
CREATE TABLE employees_blank
CREATE TABLE IF NOT EXISTS employees_blank
SELECT * FROM employees_blank;
#####################################3.修改表#################################
#3.1 添加一个字段 默认添加到表中的最后一个字段的位置
ADD salary DOUBLE(10,2);#共10位,小数点后2位
ADD phone_number VARCHAR(20) FIRST;#添加到第一个位置
ADD email VARCHAR(45) AFTER emp_name;#指定位置
#3.2修改一个字段:数据类型,长度,默认值 modify default
MODIFY emp_name VARCHAR(25);
MODIFY emp_name VARCHAR(30) DEFAULT 'aaa';#默认值 前面必须有 varchar
CHANGE salary monthly_salary DOUBLE(10,2);
CHANGE email my_email INT;#修改名字的同时,也可以修改数据类型、长度
#####################################4.重命名表#################################
#####################################5.删除表#################################
#将表结构删除掉,同时表中的数据也删除表,释放表空间
CREATE TABLE myemp2(id CHAR(10),my_name VARCHAR(20));
DROP TABLE IF EXISTS myemp2;
#####################################6.清除表#################################
#清空表,表示清空表中的所有数据,但是表结构保留。truncate
TRUNCATE TABLE employees_copy;
CREATE TABLE employees_copy AS SELECT * FROM employees;
SELECT * FROM employees_copy;
#############################7.DCL中的commit和rollback#################################
#COMMIT:提交数据。一旦执行CONMIT,则数据就被永久的保存在了数据库中,意味着数据不可以回滚(撤销)。
#ROLLBACK:回滚数据。一旦执行ROLLBACK,则可以实现数据的回滚。回滚到最近的一次COMMIT之后。
#8.对比TRUNCATE TABLE和DELETE FROM
#相同点:都可以实现对表中所有数据的删除,同时保留表结构。
# TRUNCATE TABLE:一旦执行此操作,表数据全部清除。同时,数据是不可以回滚的。
# DELETE FROM:一旦执行此操作”表数据可以全部清除。同时,数据是可以回滚的
# ①DDL的操作一旦执行,就不可回滚。指令SET autocommit = FALs山x寸DDL操作失效。
# ②DML的操作默认情况,一旦执行,也是不可回滚的。但是,如果在执行DNL之前,执行了
# SET autocommit = FALSE,则执行的DML操作就可以实现回滚。
###################################创建和管理表【练习题一】####################################
#1. 创建数据库test01_office,指明字符集为utf8。并在此数据库下执行下述操作
CREATE DATABASE IF NOT EXISTS test01_office CHARACTER SET 'utf8';
#3. 将表departments中的数据插入新表dept02中
SELECT * FROM atguigudb.departments;
MODIFY last_name VARCHAR(50);
SELECT * FROM atguigudb.employees;
DROP TABLE IF EXISTS emp01;
RENAME TABLE emp01 TO emp02;
#9. 在表dept02和emp01中添加新列test_column,并检查所作的操作
ALTER TABLE dept02 ADD test_column VARCHAR(10);
ALTER TABLE emp01 ADD test_column VARCHAR(10);
#10.直接删除表emp01中的列 department_id
DROP COLUMN department_id;
###################################创建和管理表【练习题二】####################################
CREATE DATABASE test02_market;
# 13、将 c_contact 字段移动到 c_birth 字段后面【注意此题】
ALTER TABLE customers MODIFY c_contact VARCHAR(50) AFTER c_birth;#必须加上VARCHAR(50)
# 14、将c_name字段数据类型改为 varchar(70)
ALTER TABLE customers MODIFY c_name VARCHAR(70);
# 15、将c_contact字段改名为c_phone【注意此题】
ALTER TABLE customers CHANGE c_contact c_phone VARCHAR(50);
# 16、增加c_gender字段到c_name后面,数据类型为char(1)
ALTER TABLE customers ADD c_gender CHAR(1) AFTER c_name;
RENAME TABLE customers TO customers_info;
ALTER TABLE customers_info DROP COLUMN c_city;
###################################创建和管理表【练习题三】####################################
CREATE DATABASE test03_company;
# 4、将表employees的mobile字段修改到code字段后面
ALTER TABLE employees MODIFY mobile VARCHAR(25) AFTER CODE;
# 5、将表employees的birth字段改名为birthday 【重要:change改名】
ALTER TABLE employees CHANGE birth birthday DATE;
ALTER TABLE employees MODIFY sex CHAR(1);
ALTER TABLE employees DROP COLUMN note;
# 8、增加字段名favoriate_activity,数据类型为varchar(100)
ALTER TABLE employees ADD favoriate_activity VARCHAR(100);
# 9、将表employees的名称修改为 employees_info
RENAME TABLE employees TO employees_info;
###################################数据处理之增删改###########################################
CREATE TABLE IF NOT EXISTS emp1(
#1.插入数据 insert into ... values (),()...
#①没有指明添加的字段,必须严格按照声明的字段的先后顺序
INSERT INTO emp1 VALUES(1,'Tom','2002-12-21',3400);#value也可以
INSERT INTO emp1 VALUES(2,'Jerry','2001-10-01',3500);
INSERT INTO emp1(id,hire_date,salary,`NAME`) VALUES(3,'1999-02-17',800,'Jack');
INSERT INTO emp1(id,salary,`NAME`) VALUES(4,800,'ZMJ');#可以不写全,NULL
INSERT INTO emp1(id,hire_date,salary,`NAME`)
VALUES (5,'2001-09-19',900,'李玟琪'),(6,'2003-10-20',1002,'张梦姣');
INSERT INTO emp1(id,NAME,salary,hire_date)
SELECT employee_id,last_name,salary,hire_date FROM employees WHERE department_id IN (60,70);
#说明: empl表中要添加数据的字段的长度不能低于employees表中查询的字段的长度。
#update ... set ... where ... 可以实现批量修改
WHERE id=4;#没有where,则全部修改
SET hire_date=CURDATE(),salary=10000
#3.删除数据 delete from ... where ...
#小结:DML操作默认情况下,执行完以后都会自动提交数据。
#如果希望执行完以后不自动提交数据,则需要使用SET autocommit = FALSE.
#计算列:简单来说就是某一列的值是通过别的列计算得来的。
c INT GENERATED ALWAYS AS (a+b)VIRTUAL
INSERT INTO test1(a,b)#必须指定a b
VALUES (1,2),(10,20),(100,200);
###################################增删改【练习题一】###################################
CREATE DATABASE IF NOT EXISTS dbtest11 CHARACTER SET 'utf8';
#2. 运行以下脚本创建表my_employees
CREATE TABLE my_employees(
SELECT * FROM my_employees;
#4. 向my_employees表中插入下列数据
VALUES (1,'patel','Ralph','Rpatel',895),
(2,'Dancs','Betty','Bdancs',860),
(3,'Biri','Ben','Bbiri',1100),
(4,'Newman','Chad','Cnewman',750),
(5,'Ropeburn','Audrey','Aropebur',1550);
SELECT * FROM my_employees;
#6. 将3号员工的last_name修改为“drelxer”
#7. 将所有工资少于900的员工的工资修改为1000
#8. 将userid为Bbiri的user表和my_employees表的记录全部删除【注意此题】
FROM users u JOIN my_employees e
ON u.`userid` = e.`userid`
WHERE u.`userid` = 'Bbiri';
#9. 删除my_employees、users表所有数据
DELETE FROM my_employees;
SELECT * FROM my_employees;
TRUNCATE TABLE my_employees;
###################################增删改【练习题二】###################################
INSERT INTO pet VALUES('Fluffy','harold','Cat','f','2013','2010');
INSERT INTO pet(`name`,`owner`,species,sex,Birth)
VALUES('Claws','gwen','Cat','m','2014');
INSERT INTO pet(`name`,species,sex,Birth) VALUES('Buffy','Dog','f','2009');
INSERT INTO pet(`name`,`owner`,species,sex,Birth)
VALUES('Fang','benny','Dog','m','2000');
INSERT INTO pet VALUES('bowser','diane','Dog','m','2003','2009');
INSERT INTO pet(`name`,species,sex,birth) VALUES('Chirpy','Bird','f','2008');
# 4. 添加字段:主人的生日owner_birth DATE类型
ADD COLUMN owner_birth DATE;
# 5. 将名称为Claws的猫的主人改为kevin
WHERE death IS NULL AND species = 'Dog';
# 8. 查询已经死了的cat的姓名,主人,以及去世时间
SELECT `name`,`owner`,death
WHERE species='Cat' AND death IS NOT NULL;
WHERE species = 'Dog' AND death IS NOT NULL;
###################################增删改【练习题三】###################################
INSERT INTO employee(id,`name`,sex,tel,addr,salary)
(10001,'张一一','男','13456789000','山东青岛',1001.58),
(10002,'刘小红','女','13454319000','河北保定',1201.21),
(10003,'李四','男','0751-1234567','广东佛山',1004.11),
(10004,'刘小强','男','0755-5555555','广东深圳',1501.23),
(10005,'王艳','男','020-1232133','广东广州',1405.16);
# 3. 查询出薪资在1200~1300之间的员工信息`dbtest11`
SELECT * FROM employee WHERE salary>=1200 AND salary<=1300;
# 4. 查询出姓“刘”的员工的工号,姓名,家庭住址
######################################数据类型精讲#####################################
CREATE DATABASE IF NOT EXISTS dbtest12 CHARACTER SET 'utf8';
NAME VARCHAR(10) CHARACTER SET 'gbk'
#【一、整数类型】tinyint 1字节、smallint 2字节、mediumint 3字节、int (integer) 4字节和bigint 8字节.
INSERT INTO test_int1(f1)
VALUES(127),(-128),(0),(1000);#超出范围报错
f2 INT(5),#括号中的数字表示显示宽度,不改变本身数值,只是显示
f3 INT(5) ZEROFILL#不足5位,用0向前填充
INSERT INTO test_int2(f1,f2)
VALUES (123,123),(123456,123456);
INSERT INTO test_int2(f3)
INSERT INTO test_int3(f1)
#float 4字节,double 8字节,real(real默认是double类型)
CREATE TABLE test_double1(
SELECT * FROM test_double1;
INSERT INTO test_double1(f1,f2)
INSERT INTO test_double1(f2)
VALUES(133.456);#整数部分超出,报错
INSERT INTO test_double1(f3,f4)
VALUES(123.45,123.456);#小数部分超出,则四舍五入
INSERT INTO test_double1(f4)
VALUES(1233.456);#整数部分超出,报错
#decimal(M,D) ==>存储空间M+2字节, dec numeric
#定点数在MySQL内部是以字符串的形式进行存储,这就决定了它一定是精准的。
#存储形式:二进制 bit(M)长度M,约为(M+7)/8字节
INSERT INTO test_bit1(f1,f2,f3)
VALUES (1,5,255);#超出指定位数表示的数据范围,报错
SELECT BIN(f1),BIN(f2),BIN(f3),HEX(f3),OCT(f3)
#year 年 1个字节,time 时间 3,date 日期 3,datetime 日期时间 8,timestamp 日期时间4.
#year YYYY或YY; time HH:MM:SS; date YYYY-MM-DD
#【5.1 year类型】默认就是year(4),不推荐使用year(2) 可以字符串【建议】,也可以数字
INSERT INTO test_year(f1,f2)
VALUES(1921,'2078'),('1999',2018);
#【5.2 date类型】 显示格式:"YYYY-MM-DD",插入格式:"YYYY-MM-DD"或者"YYYYMMDD" current_date()或now()返回当前系统时间
INSERT INTO test_date(f1)
VALUES ("2020-07-11"),(20200711),("20200711");#2020-07-11插入错误
INSERT INTO test_date(f1)
#【5.3 time类型】 3个字节 标准格式:"HH:MM:SS"或者"HHMMSS"或HHMMSS
#格式:"D HH:MM:SS" "HH:MM:SS" "HH:MM" "D HH:MM" "D HH" "SS" ==>D表示天,转换成小时D*24+HH
INSERT INTO test_time(f1)
VALUES ("12:20:19"),("122019");
INSERT INTO test_time(f1)
VALUES ("1 12:20:00"),("1 12:20"),("20");#最后一个是秒数,前两个一样
INSERT INTO test_time(f1)
INSERT INTO test_time(f1)
SELECT CURRENT_DATE(),CURRENT_TIME(),NOW() FROM DUAL;
#【5.4 datetime类型】 ==> 【常用】
#"YYYY-MM-DD HH:MM:SS" "YYYYMMDDHHMMSS"
CREATE TABLE test_datatime(
SELECT * FROM test_datatime;
INSERT INTO test_datatime(f1)
VALUES ("2020-10-21 12:12:12"),("20201021121212");#一模一样
INSERT INTO test_datatime(f1)
VALUES (CURRENT_TIMESTAMP()),(NOW()),(SYSDATE());
#格式:"YYYY-MM-DD HH:MM:SS"或者"YYYY@MM@DD@HH@MM@SS"
CREATE TABLE test_timestamp(
SELECT * FROM test_timestamp;
INSERT INTO test_timestamp(f1)
VALUES ("2020-10-21 12:12:12"),("2020@10@21@12@12@12");
INSERT INTO test_timestamp(f1)
VALUES (NOW()),(CURRENT_TIMESTAMP());
#char(M) varchar(M) tinytext text mediumtext longtext enum set
#【6.1 char或char(M)】: 不指定时,只能存储一位字符或一个汉字
#如果保存时,数据的实际长度比CHAR类型声明的长度小,则会在右侧填充空格以达到指定的长度。
#检索CHAR类型的数据时,CHAR类型的字段会去除尾部的空格。
INSERT INTO test_char(f1,f2)
VALUES ('a','你好帅呀朋'),('好','asfgg');
INSERT INTO test_char(f2)
VALUES ('abc '),('abcde');
SELECT CONCAT(f2,f2) FROM test_char;#自动去掉末尾的空格
#CHAR(M) 固定长度 浪费存储空间 效率高 存储不大,速度要求高
#VARCHAR(D) 可变长度 节省存储空间 效率低 非CHAR的情况
#【6.3 text类型】存多少就是多少,不像char会自动删去空格
CREATE TABLE test_text(f1 TEXT);
INSERT INTO test_text(f1)VALUES('abc'),('abc ');
SELECT CONCAT(f1,f1) FROM test_text;#不删除末尾空格,是什么就是什么
#【6.4 enmu类型】单选 只能从枚举值中选一个,忽略大小写
season ENUM('春','夏','秋','冬','unknown')
INSERT INTO test_enum(season) VALUES('春'),('秋'),('unknown');
INSERT INTO test_enum(season) VALUES('UNKNOWN'),('UnKnown');#忽略大小写
INSERT INTO test_enum(season) VALUES(1),('1');#可以使用索引
INSERT INTO test_enum(season) VALUES(NULL);#可以null
INSERT INTO test_set(s)VALUES('A'),('A,B');
INSERT INTO test_set(s)VALUES('A,B,A');#写重复的不报错,自动过滤掉
INSERT INTO test_set(s)VALUES('A,D');#写不在范围内的,报错
hobby SET('吃','喝','玩','乐')
INSERT INTO temp_mul(gender,hobby)VALUES('男','吃,喝');
#binary(M) varbinary(M) tinyblob blob mediumblob longblob
#【7.1 binary和varbinary类型】
#binary和varbinary类似于char和varchar,只是它们存储的是二进制字符串。
#binary(M)表示最多存储M个字节,而char是字符;未指定M,表示1个
#BLOB类型包括TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB4种类型,
#BLOB是一个二进制大对象,可以容纳可变数量的数据,比如图片,音频,视频
SELECT * FROM test_blob1;
INSERT INTO test_blob1(id)VALUES(1001);
INSERT INTO test_json(js)
VALUES('{"name":"zmj","age":18,"address":{"province":"河南","city":"郑州"}}');
SELECT js -> '$.name' AS NAME,js -> '$.age' AS age,js -> '$.address.province'AS province
################################################【约束】#################################################
CREATE DATABASE dbtest13;
SELECT * FROM information_schema.table_constraints
WHERE TABLE_NAME='employees';
#①默认,所有的类型的值都可以是NULL,包括INT、FLOAT等数据类型
#②非空约束只能出现在表对象的列上,只能某个列单独限定非空,不能组合非空,也就是只能列级约束
last_name VARCHAR(15) NOT NULL,
MODIFY email VARCHAR(25) NOT NULL;
MODIFY email VARCHAR(25);
#可以向声明为unique的字段上添加null值,可以重复,即使unique
id INT UNIQUE,#列级约束,在创建唯一约束的时候,如果不给唯一约束命名,就默认和列名相同.
salary DECIMAL(10,2),#注意此处有逗号
CONSTRAINT uk_test2_email UNIQUE(email)#起名字CONSTRAINT uk_test2_email
SELECT * FROM information_schema.table_constraints
WHERE TABLE_NAME='test2';
ALTER TABLE test2 ADD CONSTRAINT uk_test2_sal UNIQUE(salary);#起名字:CONSTRAINT uk_test2_sal
ALTER TABLE test2 MODIFY last_name VARCHAR(15) UNIQUE;
CONSTRAINT uk_user_name_pwd UNIQUE(`name`,`password`)#两个在一起建立的约束,两个不全一样就可以
VALUES(1,'Tom','abc'),(1,'Tom1','abc');#可以通过
#②主键约束特征:非空且唯一,用于唯一的标识表中的一条记录。
#③如果删除主键约束了,主键约束对应的索引就自动删除了。
#④MySQL的主键名总是PRIMARY,就算自己命名了主键约束名也没用。
#⑤如果是多列组合的复合主键约束,那么这些列都不允许为空值,并且组合的值不允许重复
CONSTRAINT pk_test4_id PRIMARY KEY(id)#没有必要起名字
SELECT * FROM information_schema.table_constraints
WHERE TABLE_NAME='test4';
PRIMARY KEY(`name`,`password`)#俩一起构成主键
#③自增长列约束的列必须是键列(主键列,唯一键列)
#⑥如果自增列指定了0和null,会在当前最大值的基础上自增;如果自增列手动指定了具体值,直接赋值为具体值。
id INT PRIMARY KEY AUTO_INCREMENT,
INSERT INTO test7(last_name)
VALUES('zmj'),('lwq'),('zym'),('zyn');#自动递增
INSERT INTO test7(id,last_name)
VALUES(-10,'tom');#自动设置id也可以,负数也可以,且按照id从小到大排序了
MODIFY id INT AUTO_INCREMENT;
#①主表(父表)︰被引用的表,被参考的表从表(子表):引用别人的表,参考别人的表
#②在创建外键约束时,如果不给外键约束命名,默认名不是列名,而是自动产生一个外键名
#③删表时,先删从表(或先删除外键约束),再删除主表
#④在“从表”中指定外键约束,并且一个表可以建立多个外键约束
#⑤当创建外键约束时,系统默认会在所在的列上建立对应的普通索引。但是索引名是列名,不是外键的约束名(根据外键查询效率很高)
emp_id INT PRIMARY KEY AUTO_INCREMENT,
CONSTRAINT fk_emp1_dept_id FOREIGN KEY(department_id) REFERENCES dept1(dept_id)
SELECT * FROM information_schema.table_constraints WHERE TABLE_NAME='emp1';
emp_id INT PRIMARY KEY AUTO_INCREMENT,
ADD CONSTRAINT kf_emp2_dept2_id FOREIGN KEY(department_id)REFERENCES dept2(dept_id);
#Cascade方式:在父表上update/delete记录时,同步update/delete掉子表的匹配记录
#Set null方式∶在父表上update/delete记录时,将子表上匹配记录的列设为null,但是要注意子表的外键列不能为not null
#No action方式∶如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作
#Restrict方式:同no action,都是立即检查外键约束
#Set default方式(在可视化工具sQLyog中可能显示空白)︰父表有变更时,子表将外键列设置成一个默认的值,但Innodb不能识别
#如果没有指定等级,就相当于Restrict方式。
#【结论】对于外键约束,最好是采用: ON UPDATE CASCADE ON DELETE RESTRICT的方式。
ALTER TABLE emp2 DROP FOREIGN KEY kf_emp2_dept2_id;
ALTER TABLE emp2 DROP INDEX kf_emp2_dept2_id;#外键约束名
#②建外键约束,你的操作(创建表、删除表、添加、修改、删除)会受到限制,从语法层面受到限制。
#③在MySQL里,外键约束是有成本的,需要消耗系统资源。对于大并发的sQL操作,有可能会不适
#作用:检查某个字段的值是否符号xx要求,一般指的是值的范围
salary DECIMAL(10,2) CHECK(salary>2000)
salary DECIMAL(10,2) DEFAULT 2000
MODIFY last_name VARCHAR(19) DEFAULT 'zmj';
MODIFY last_name VARCHAR(19);
#①面试1:为什么建表时,加not null default "或default 0? ==》不想让表中出现null值。
#②面试2:为什么不想要null的值?==》不好比较; 效率不高。
#③面试3:带AUTO_INCREMENT约束的字段值是从1开始的吗?==》在mysql时,是的。还可以指定。
#④面试4:并不是每个表都可以任意选择存储引擎? ==》外键约束不能跨引擎使用。
###########################################【约束练习题一】################################################
#已经存在数据库test04_emp,两张表emp2和dept2
CREATE DATABASE test04_emp;
#1.向表emp2的id列中添加PRIMARY KEY约束
ALTER TABLE emp2 MODIFY id INT PRIMARY KEY;
ALTER TABLE emp2 ADD PRIMARY KEY(id);
#2. 向表dept2的id列中添加PRIMARY KEY约束
ALTER TABLE dept2 ADD PRIMARY KEY(id);
#3. 向表emp2中添加列dept_id,并在其中定义FOREIGN KEY约束,与之相关联的列是dept2表中的id列。
ALTER TABLE emp2 ADD dept_id INT;
ALTER TABLE emp2 ADD CONSTRAINT fk_emp2_dep2_id FOREIGN KEY(dept_id) REFERENCES dept2(id);
################################################【视图view】#################################################
#表,数据字典,约束,视图,索引,存储过程,存储函数,触发器
#[作用]可以帮我们使用表的一部分数据而不是所有的表;也可以针对不同的用户制定不同的查询视图。
#[优点]简化查询;减少数据冗余;控制数据的访问;使用灵活多变的需求;能够分解复杂的查询逻辑
#[缺点]如果视图过多,会导致数据库维护成本的问题;及时维护,维护成本大。
#①视图是一种虚拟表,本身是不具有数据的,占用很少的内存空间。
#②视图建立在已有表的基础上,视图赖以建立的这些表称为基表。
#③视图的创建和删除只影响视图本身,不影响对应的基表。
# 但是当对视图中的数据进行增加、删除和修改操作时,数据表中的数据会相应地发生变化,反之亦然。
#④视图,是向用户提供基表数据的另一种表现形式。通常情况下,小型项目的数据库可以不使用视图。
CREATE DATABASE dbtest14;
CREATE TABLE emps AS SELECT * FROM atguigudb.employees;#数据复制过来了,但是约束没有
CREATE TABLE depts AS SELECT * FROM atguigudb.departments;
CREATE VIEW vu_emp1 AS SELECT employee_id,last_name,salary FROM emps;
SELECT employee_id 'emp_id',last_name 'l_name',salary 'sal'#查询语句中字段的别名会出现在视图的字段中
CREATE VIEW vu_emp3(emp_id,`name`,sal)#一次匹配,字段名字
SELECT employee_id,last_name,salary
CREATE VIEW vu_emp_sal(dep_id,avg_sal)#不在这里写,也可以
SELECT department_id,AVG(salary) avg_sal
WHERE department_id IS NOT NULL
SELECT * FROM vu_emp_sal;
SHOW TABLE STATUS LIKE 'vu_emp1';#命令行竖着显示,末尾加上\G
SHOW TABLE STATUS LIKE 'depts';
SHOW CREATE VIEW vu_emp1;
#③在定义视图的SELECT语句中使用了JOIN联合查询,视图将不支持更新操作。
#④虽然可以更新视图数居,但总的来说,视图作为虚拟表,主要用于方便查询,不建议更新视图的数据。
#⑤对视图数据的更改,都是通过对实际数据表里数据的操作来完成的。
SELECT employee_id,last_name,salary FROM emps;
WHERE employee_id=101;#更改视图,会同时更改原表
CREATE OR REPLACE VIEW vu_emp1#不存在就创建,存在就替换更新
SELECT employee_id,last_name,salary,email
SELECT employee_id,last_name,salary,email
DROP VIEW IF EXISTS vu_emp3,vu_emp4;#可同时删除多个
########################################【视图练习题一】##########################################
#1. 使用表employees创建视图employee_vu,其中包括姓名,员工号,部门号
SELECT last_name,employee_id,department_id
FROM atguigudb.employees;
SELECT * FROM emplouee_vu;
CREATE OR REPLACE VIEW employee_vu AS
SELECT last_name,employee_id,department_id FROM atguigudb.employees WHERE department_id=80;
########################################【视图练习题二】##########################################
CREATE TABLE IF NOT EXISTS emps AS SELECT * FROM atguigudb.employees;
#1. 创建视图emp_v1,要求查询电话号码以‘011’开头的员工姓名和工资、邮箱
CREATE OR REPLACE VIEW emp_v1 AS
SELECT last_name,salary,email FROM emps WHERE phone_number LIKE '011%';
#2. 要求将视图 emp_v1 修改为查询电话号码以‘011’开头的并且邮箱中包含 e 字符的员工姓名和邮箱、电话号码
CREATE OR REPLACE VIEW emp_v1 AS
SELECT last_name,email,phone_number FROM emps
WHERE phone_number LIKE '011%' AND email LIKE '%e%';
#3. 向 emp_v1 插入一条记录,是否可以?==》不可以
INSERT INTO emp_v1(last_name,salary,email,phone_number)
VALUES('Tom',2300,'tom@126.com','1322321312');#错误
#4. 修改emp_v1中员工的工资,每人涨薪1000
#6. 创建视图emp_v2,要求查询部门的最高工资高于 12000 的部门id和其最高工资
CREATE OR REPLACE VIEW emp_v2 AS
SELECT department_id,MAX(salary) max_sal FROM emps GROUP BY department_id HAVING MAX(salary)>12000;
#7. 向 emp_v2 中插入一条记录,是否可以?==》不可以
INSERT INTO emp_v2 VALUES(400,18000);
DROP VIEW IF EXISTS emp_v2,emp_v1;
###########################################【存储过程】############################################
#[定义]就是一组经过预先编译的SQL语句的封装。
#[优点]提高重用性;减少网络传输量;减少sql语句暴露在网络上的风险,提高安全性。
#[存储过程、视图对比]视图是虚拟表,一般不操控原表;存储过程是程序化的SQL可以直接操作底层数据表.
#[存储过程、函数对比]存储过程没有返回值,函数有返回值。
/*[分类]存储过程的参数类型是in,out,inout。参数类型可以是mysql中的任意类型。
2.仅仅带in/out类型(有参数无返回/无参数有返回)
注意:IN、OUT、INOUT都可以在一个存储过程中带多个。
/*characteristics表示创建存储过程时指定的对存储过程的约束条件,
DELIMITER $#定义结束符$,也可以其他符号,自定义delimiter
CREATE PROCEDURE select_all_date()
CREATE DATABASE dbtest15;
CREATE TABLE employees AS SELECT * FROM atguigudb.employees;
CREATE TABLE departments AS SELECT * FROM atguigudb.departments;
CREATE PROCEDURE select_all_data()
CALL select_all_data();#存储过程的调用用call,函数是select
CREATE PROCEDURE avg_employee_salary()
SELECT AVG(salary) FROM employees;
CALL avg_employee_salary();
CREATE PROCEDURE show_min_sal(OUT ms DOUBLE)#先写名字ms,再写类型double
SELECT MIN(salary) INTO ms FROM employees;
CALL show_min_sal(@ms);#变量ms
SELECT @ms;#查看变量值select即可
CREATE PROCEDURE show_someone_salary(IN empname VARCHAR(20))
SELECT salary FROM employees WHERE last_name=empname;
CALL show_someone_salary('Abel');
SET @empname='Abel';#定义变量,'='或者':='均为赋值符号
CALL show_someone_salary(@empname);#传入
CREATE PROCEDURE show_someone_sal2(IN empname VARCHAR(20),OUT empsalary DECIMAL(10,2))
SELECT salary INTO empsalary
CALL show_someone_sal2(@empname,@empsal);
CREATE PROCEDURE show_mgr_name(INOUT empname VARCHAR(25))#查询某个员工领导的姓名
SELECT last_name INTO empname
CALL show_mgr_name(@empname);
########################################【存储函数】##############################################
CREATE FUNCTION 函数名(参数名 参数类型,...)
#[参数列表]指定参数为IN、OUT或INOUT只对procedure是合法的,function中总是默认为IN参数
CREATE FUNCTION email_by_name()
RETURN(SELECT email FROM employees WHERE last_name='Abel');
#解决报错的方式二:创建函数前执行此语句,保证函数的创建会成功
SET GLOBAL log_bin_trust_function_creators=1;
CREATE FUNCTION email_by_id(emp_id INT)
RETURN(SELECT email FROM employees WHERE employee_id=emp_id);
存储过程:PROCEDURE CALL存储过程() 返回值理解为有0个或多个 一般用于更新
存储函数:FUNCTION SELECT函数() 返回值只能是一个 一般用于查询结果为一个值并返回时
存储过程的功能更加强大,包括能够执行对表的操作(比如创建表,删除表等)和事务操作,
#####################################【存储过程和函数的查看、修改、删除】####################################
#创建完之后,怎么知道我们创建的存储过程、存储函数是否成功了呢?
SHOW CREATE PROCEDURE show_mgr_name;
SHOW CREATE FUNCTION email_by_id;
SHOW PROCEDURE STATUS LIKE 'show_mgr_name';
#方式三:从information_schema.Routines表中查看存储过程和函数的信息
SELECT * FROM information_schema.Routines
WHERE ROUTINE_NAME LIKE 'email_by_id'
AND ROUTINE_TYPE='FUNCTION';#这一行不写也可以
#修改存储过程或函数,不影响存储过程或函数功能,只是修改相关特性。使用ALTER语句实现。
ALTER PROCEDURE show_min_sal
DROP PROCEDURE IF EXISTS show_min_sal;
#########################################【存储过程、函数练习题】#############################################
CREATE DATABASE test15_pro_func;
#1. 创建存储过程insert_user(),实现传入用户名和密码,插入到admin表中
CREATE TABLE IF NOT EXISTS ADMIN(
id INT PRIMARY KEY AUTO_INCREMENT,
user_name VARCHAR(15) NOT NULL,
CREATE PROCEDURE insert_user(IN username VARCHAR(20),IN loginPwd VARCHAR(20))
INSERT INTO `admin`(user_name,pwd)
VALUES(username,loginpwd);
CALL insert_user('zmj','abc123');
#2. 创建存储过程get_phone(),实现传入女神编号,返回女神姓名和女神电话
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(15) NOT NULL,
phone VARCHAR(15) UNIQUE,
INSERT INTO beauty(NAME,phone,birth)
('朱茵','13201233453','1982-02-12'),
('孙燕姿','13501233653','1980-12-09'),
('田馥甄','13651238755','1983-08-21'),
('邓紫棋','17843283452','1991-11-12'),
('刘若英','18635575464','1989-05-18'),
('杨超越','13761238755','1994-05-11');
CREATE PROCEDURE get_phone(IN id_ INT,OUT name_ VARCHAR(15),OUT phone_ VARCHAR(15))
SELECT `name`,phone INTO name_,phone_
CALL get_phone(2,@name_,@phone_);
#3. 创建存储过程date_diff(),实现传入两个女神生日,返回日期间隔大小
CREATE PROCEDURE date_diff(IN b1 DATE,IN b2 DATE,OUT diff_day INT)
SELECT DATEDIFF(b1,b2) INTO diff_day;
CALL date_diff(@b1,@b2,@diff_day);
#4. 创建存储过程format_date(),实现传入一个日期,格式化成xx年xx月xx日并返回
CREATE PROCEDURE format_date(IN d DATE,OUT strdate VARCHAR(50))
SELECT DATE_FORMAT(d,'%y年%m月%d日')INTO strdate;
CALL format_date('20221024',@res);
#5. 创建存储过程beauty_limit(),根据传入的起始索引和条目数,查询女神表的记录
CREATE PROCEDURE beauty_limit(IN start_index INT,IN size INT)
SELECT * FROM beauty LIMIT start_index,size;
CREATE PROCEDURE add_double(INOUT a INT,INOUT b INT)
CREATE TABLE employees AS SELECT * FROM atguigudb.`employees`;
CREATE TABLE departments AS SELECT * FROM atguigudb.`departments`;
#1. 创建函数get_count(),返回公司的员工个数
CREATE FUNCTION get_count()
RETURN(SELECT COUNT(*)FROM employees);
#2. 创建函数ename_salary(),根据员工姓名,返回它的工资
CREATE FUNCTION ename_salary(n VARCHAR(25))
RETURN(SELECT salary FROM employees WHERE last_name=n);
SELECT ename_salary('De Haan');#不能返回多行的
#3. 创建函数dept_sal() ,根据部门名,返回该部门的平均工资
CREATE FUNCTION dept_sal(dept_name VARCHAR(30))
RETURN(SELECT AVG(salary)
WHERE department_name=dept_name
#4. 创建函数add_float(),实现传入两个float,返回二者之和
CREATE FUNCTION add_float(f1 FLOAT,f2 FLOAT)
#########################################【变量、流程控制与游标】############################################
#系统变量(全局系统变量、会话系统变量),用户自定义变量
②系统变量分为:全局系统变量(需要添加global),会话系统变量(local变量)(需要添加session关键字)
③全局系统变量针对于所有会话(连接)有效,但不能跨重启
会话期间,当前会话对某个会话系统变量值的修改不会影响其他会话同一个会话系统变量的值。
⑤会话1对某个全局系统变量值的修改会导致会话2中同一个全局系统变量值的修改。
⑥ 有些系统变量只能是全局的,例如max_connections 用于限制服务器的最大连接数;
有些系统变量作用域既可以是全局又可以是会话,例如character_set_client用于设置客户端的字符集;
有些系统变量的作用域只能是当前会话,例如pseudo_thread_id 用于标记当前会话的MySQL连接ID。
SHOW VARIABLES;#默认的是会话系统变量
SHOW GLOBAL VARIABLES LIKE 'admin_%';
SHOW SESSION VARIABLES LIKE 'admin_%';
MysQL中的系统变量以“@@”开头,其中“@@global”仅用于标记全局系统变
量, @@session"仅用于标记会话系统变量。“@@"首先标记会话系统变量,如果
SELECT @@global.admin_ssl_ca;
SELECT @@session.character_set_client;
SELECT @@max_connections;#现在会话系统变量中找,再到全局中找
方式1:修改MySQL配置文件,继而修改MySQL系统变量的值(该方法需要重启MySQL服务) my.ini
方式2:在MySQL服务运行期间,使用“set”命令重新设置系统变量的值
#全局系统变量:针对于当前的数据库实例是有效的,一旦重启mysql服务,就失效了。
SET @@global.max_connections=161;
SELECT @@global.max_connections;#查看值
SET GLOBAL max_connections=171;
#针对于当前会话是有效的,一旦结束会话,重新建立起新的会话,就失效了。
SET @@session.character_set_client='gbk';
SELECT @@character_set_client;
SELECT @@global.character_set_client;#修改会话的,并不影响全局的
SET SESSION character_set_client='gbk';
/* MySQL中的会话用户变量以一个“@”开头,局部变量都懒得用了。
会话用户变量:作用域和会话变量一样,只对当前连接会话有效。
局部变量:只在BEGIN和END语句块中有效。局部变量只能在存储过程和函数中使用。
SELECT @count :=COUNT(*) FROM employees;#注意是':='
SELECT AVG(salary) INTO @avg_sal FROM employees;
①声明和使用只能在begin end之间,即存储过程和函数之中。
③declare的方式声明必须放在begin中首行的位置。
④赋值还是set,select ... into这一类。
声明格式:declare 变量名 类型 [default 值];#如果没有default,初始值为null
#注意:其他变量都没有类型,而这个局部变量声明时候有类型
CREATE PROCEDURE test_var()
DECLARE c,d INT DEFAULT 100;
DECLARE emp_name VARCHAR(25);
SELECT last_name INTO emp_name FROM employees WHERE employee_id=101;
————————————————————————————————————————————————————————————————————————————————————————
会话用户变量: 当前会话 会话的任何地方 加@符号,不用指定类型
局部变量: 定义它的BEGIN END中 BEGIN END的第一句话 一般不用加@,需要指定类型
处理程序:定义了在遇到问题时应当采取的处理方式,并且保证存储过程或函数在遇到警告或错误时能继续执行。
这样可以增强存储程序处理问题的能力,避免程序异常停止运行。
说明:定义条件和处理程序在存储过程、存储函数中都是支持的。
定义条件就是给MysQL中的错误码命名,这有助于存储的程序代码更清晰。
这个名字可以随后被用在定义处理程序的DECLARE HANDLER语句中。
declare 错误名称 condition for 错误码(或错误条件);
MySQL_error_code和sqlstate_value都可以表示MySQL的错误。
MySQL_error_code:是数值类型错误代码。
sqlstate_value:是长度为5的字符串类型错误代码。
#举例1:定义"Field_Not_Be_NULL"错误名与MysQL中违反非空约束的错误类型
#是"ERROR 1048 (23000)”对应。
DECLARE Field_Not_Be_NULL CONDITION FOR 1048;
DECLARE Field_Not_Be_NULL2 CONDITION FOR SQLSTATE '23000';
#举例2:定义"ERROR 1148 (42000)"错误,名称为command_not_allowed。
DECLARE command_not_allowed CONDITION FOR 1148;
DECLARE command_not_allowed CONDITION FOR SQLSTATE '42000';
DECLARE CONTINUE HANDLER FOR SQLSTATE '42s02' SET @info='NO_SUCH_TABLE';
DECLARE CONTINUE HANDLER FOR 1146 SET @info='NO_SUCH_TABLE';
DECLARE no_such_table CONTINUE FOR 1146;
DECLARE CONTINUE HANDLER FOR no_such_table SET @info='NO_SUCH_TABLE';
DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR';
DECLARE EXIT HANDLER FOR NOT FOUND SET @info='NO_SUCH_TABLE';
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info='ERROR';
CREATE PROCEDURE UpdateDataNoCondition()
DECLARE CONTINUE HANDLER FOR 1048 SET @proc_value=-1;
UPDATE employees SET email NULL WHERE last_name='Abel';
UPDATE employees SET email='aabbel' WHERE last_name='Abel';
DROP PROCEDURE UpdateDataNoCondition;
CREATE PROCEDURE test_if()
DECLARE stu_name VARCHAR(15) DEFAULT 'aaa';
THEN SELECT 'stu_name is null';
SELECT 'stu_name is not null';#注意:无then
CREATE PROCEDURE test_case()
DECLARE var1 INT DEFAULT 12;
WHEN var1 >= 100 THEN SELECT '三位数';
WHEN var1 >= 10 THEN SELECT '两位数';
CREATE PROCEDURE test_loop()
DECLARE num INT DEFAULT 1;
IF num>100 THEN LEAVE loop_label;
DROP PROCEDURE test_loop;
#举例:创建存储过程"get_count_by_limit_total_salary()",声明IN参数limit_total_salary,
#DOUBLE类型;声明ouT参数total_count,INT类型。函数的功能可以实现累加薪资最高的几个员工的薪资值,
#直到薪资总和达到limit_total_salary参数的值,返回累加的人数给total_count。
CREATE PROCEDURE get_count_by_limit_total_salary(IN limit_total_salary DOUBLE,OUT total_count INT)
DECLARE sum_sal DOUBLE DEFAULT 0.0;#记录累加的工资总额
DECLARE emp_sal DOUBLE;#记录每一个员工的工资
DECLARE emp_count INT DEFAULT 0;#记录累加的人数
DECLARE emp_cursor CURSOR FOR SELECT salary FROM employees ORDER BY salary DESC;
FETCH emp_cursor INTO emp_sal;
SET sum_sal=sum_sal+emp_sal;
SET emp_count=emp_count+1;
UNTIL sum_sal>=limit_total_salary
SET total_count=emp_count;
CALL get_count_by_limit_total_salary(200000,@total_count);
#####################################【变量练习题】############################################
#1. 创建函数get_count(),返回公司的员工个数
CREATE FUNCTION get_count() RETURNS INT
DECLARE c INT DEFAULT 0;#定义局部变量
SELECT COUNT(*) INTO c#赋值
#2. 创建函数ename_salary(),根据员工姓名,返回它的工资
CREATE FUNCTION ename_salary(emp_name VARCHAR(15))RETURNS DOUBLE
SELECT salary INTO @sal FROM employees WHERE last_name=emp_name;
SELECT ename_salary('Abel');
#3. 创建函数dept_sal() ,根据部门名,返回该部门的平均工资
CREATE FUNCTION dept_sal(dept_name VARCHAR(15))RETURNS DOUBLE
DECLARE avg_sal DOUBLE DEFAULT 0.0;
SELECT AVG(salary) INTO avg_sal
JOIN departments d ON e.`department_id`=d.`department_id`
WHERE d.department_name=dept_name;
SELECT dept_sal('Marketing');
#4. 创建函数add_float(),实现传入两个float,返回二者之和
CREATE FUNCTION add_float(f1 FLOAT,f2 FLOAT)RETURNS FLOAT
SELECT add_float(1.1,2.2);
#####################################【流程控制练习题】############################################
#1. 创建函数test_if_case(),实现传入成绩,如果成绩>90,返回A,如果成绩>80,返回B,如果成绩>60,返回C,否则返回D
CREATE FUNCTION test_if_case(sc INT)RETURNS CHAR
WHEN sc>90 THEN SET ch='A';
WHEN sc>80 THEN SET ch='B';
WHEN sc>60 THEN SET ch='C';
CREATE FUNCTION test_if_case2(score DOUBLE)
WHEN score>90 THEN SET ch='A';
WHEN score>80 THEN SET ch='B';
WHEN score>60 THEN SET ch='C';
SELECT test_if_case2(67);
#2. 创建存储过程test_if_pro(),传入工资值,如果工资值<3000,则删除工资为此值的员工,
#如果3000 <= 工资值 <= 5000,则修改此工资值的员工薪资涨1000,否则涨工资500
CREATE PROCEDURE test_if_pro(IN sal DOUBLE)
DELETE FROM employees WHERE salary=sal;
UPDATE employees SET salary=salary+1000;
UPDATE employees SET salary=salary+500;
#3. 创建存储过程insert_data(),传入参数为 IN 的 INT 类型变量 insert_count,
#实现向admin表中批量插入insert_count条记录。
CREATE PROCEDURE insert_data(IN insert_count INT)
INSERT INTO ADMIN(user_name,user_pwd)VALUE(CONCAT('Rose-',i),ROUND(RAND()*100000));
#####################################【游标的使用练习题】############################################
CREATE PROCEDURE update_salary(IN dept_id INT,IN change_sal_count INT)
DECLARE int_count INT DEFAULT 0;
DECLARE salary_rate DOUBLE DEFAULT 0.0;
DECLARE emp_hire_date DATE;
DECLARE emp_cursor CURSOR FOR SELECT employee_id,hire_date FROM employees
WHERE department_id = dept_id ORDER BY salary;
WHILE int_count<change_sal_count DO
FETCH emp_cursor INTO emp_id,emp_hire_date;
IF (YEAR(emp_hire_date)<1995) THEN
ELSEIF(YEAR(emp_hire_date)<1998)THEN
ELSEIF(YEAR(emp_hire_date) <= 2001)THEN
ELSE SET salary_rate = 1.05;
UPDATE employees SET salary = salary * salary_rate
WHERE employee_id = emp_id;
SET int_count=int_count+1;
CALL update_salary(50,2);
#######################################【触发器】#####################################################
CREATE TABLE test_trigger(
id INT PRIMARY KEY AUTO_INCREMENT,
CREATE TABLE test_trigger_log(
id INT PRIMARY KEY AUTO_INCREMENT,
SELECT * FROM test_trigger;
SELECT * FROM test_trigger_log;
CREATE TRIGGER before_insert_test_tri
BEFORE INSERT ON test_trigger
INSERT INTO test_trigger_log(t_log)
VALUES('before insert...');
INSERT INTO test_trigger(t_note) VALUES ('Tom...');
SELECT * FROM test_trigger;
SELECT * FROM test_trigger_log;
CREATE TRIGGER salary_check_trigger
BEFORE INSERT ON employees
SELECT salary INTO mgr_sal FROM employees
WHERE employee_id=new.manager_id;#要添加的记录用new表示,要删除的用old表示
IF new.salary>mgr_sal THEN
SIGNAL SQLSTATE 'HY000' SET MASSAGE_TEXT='薪资高于领导薪资错误';
#####################################【触发器练习题】#############################################
SELECT employee_id,last_name,salary
FROM atguigudb.`employees`;
#1. 复制一张emps表的空表emps_back,只有表结构,不包含任何数据
#3. 创建触发器emps_insert_trigger,每当向emps表中添加一条记录时,
CREATE TRIGGER emps_insert_trigger
INSERT INTO emps_back(employee_id,last_name,salary)
VALUES(NEW.employee_id,NEW.last_name,NEW.salary);
INSERT INTO emps(employee_id,last_name,salary)
##################################【窗口函数】############################################
id INT PRIMARY KEY AUTO_INCREMENT,
INSERT INTO sales(city,county,sales_value)
SELECT city '城市',county '区',sales_value '区销售额',
SUM(sales_value) OVER(PARTITION BY city) '市销售额',
sales_value/SUM(sales_value) OVER (PARTITION BY city) '市比率',
SUM(sales_value) OVER() '总销售额',
sales_value/SUM(sales_value) OVER() '总比率'
id INT PRIMARY KEY AUTO_INCREMENT,
INSERT INTO goods(category_id,category,NAME,price,stock,upper_time)
(1, '女装/女士精品', 'T恤', 39.90, 1000, '2020-11-10 00:00:00'),
(1, '女装/女士精品', '连衣裙', 79.90, 2500, '2020-11-10 00:00:00'),
(1, '女装/女士精品', '卫衣', 89.90, 1500, '2020-11-10 00:00:00'),
(1, '女装/女士精品', '牛仔裤', 89.90, 3500, '2020-11-10 00:00:00'),
(1, '女装/女士精品', '百褶裙', 29.90, 500, '2020-11-10 00:00:00'),
(1, '女装/女士精品', '呢绒外套', 399.90, 1200, '2020-11-10 00:00:00'),
(2, '户外运动', '自行车', 399.90, 1000, '2020-11-10 00:00:00'),
(2, '户外运动', '山地自行车', 1399.90, 2500, '2020-11-10 00:00:00'),
(2, '户外运动', '登山杖', 59.90, 1500, '2020-11-10 00:00:00'),
(2, '户外运动', '骑行装备', 399.90, 3500, '2020-11-10 00:00:00'),
(2, '户外运动', '运动外套', 799.90, 500, '2020-11-10 00:00:00'),
(2, '户外运动', '滑板', 499.90, 1200, '2020-11-10 00:00:00');
#1.1 row_number()函数:能够对数据中的序号进行顺序显示
SELECT ROW_NUMBER()OVER(PARTITION BY category_id ORDER BY price DESC)'row_num',
id,category_id,category,`name`,price,stock
FROM (SELECT ROW_NUMBER() OVER(PARTITION BY category_id ORDER BY price DESC)'row_num',
id,category_id,category,`name`,price,stock
#举例:使用RANK()函数获取 goods 数据表中各类别的价格从高到低排序的各商品信息
SELECT RANK()OVER(PARTITION BY category_id ORDER BY price DESC)'row_num',
id,category_id,category,`name`,price,stock
#举例:使用RANK()函数获取 goods 数据表中类别为“女装/女士精品”的价格最高的4款商品信息。
FROM (SELECT RANK() OVER(PARTITION BY category_id ORDER BY price DESC)'row_num',
id,category_id,category,`name`,price,stock
WHERE category='女装/女士精品' AND row_num<=4;
#举例:使用DENSE_RANK()函数获取 goods 数据表中各类别的价格从高到低排序的各商品信息。
SELECT DENSE_RANK()OVER(PARTITION BY category_id ORDER BY price DESC)'row_num',
id,category_id,category,`name`,price,stock
#举例:计算 goods 数据表中名称为“女装/女士精品”的类别下的商品的PERCENT_RANK值。
SELECT RANK()OVER w AS r,
PERCENT_RANK()OVER w AS pr,
id,category_id,category,`name`,price,stock
WINDOW w AS (PARTITION BY category_id ORDER BY price DESC);
SELECT RANK()OVER(PARTITION BY category_id ORDER BY price DESC) AS r,
PERCENT_RANK()OVER(PARTITION BY category_id ORDER BY price DESC) AS pr,
id,category_id,category,`name`,price,stock
#举例:查询goods数据表中小于或等于当前价格的比例。
SELECT CUME_DIST()OVER(PARTITION BY category_id ORDER BY price ASC)AS cd,
#举例:查询goods数据表中前一个商品价格与当前商品价格的差值。
SELECT id,category, NAME, price, pre_price, price - pre_price AS diff_price
FROM(SELECT id, category, NAME, price,LAG(price,1) OVER w AS pre_price
WINDOW w AS (PARTITION BY category_id ORDER BY price)
#举例:查询goods数据表中后一个商品价格与当前商品价格的差值
SELECT id,category, NAME, price, pre_price, price - pre_price AS diff_price
FROM(SELECT id, category, NAME, price,LEAD(price,1) OVER w AS pre_price
WINDOW w AS (PARTITION BY category_id ORDER BY price)
SELECT id, category, NAME, price, stock,FIRST_VALUE(price)OVER w AS first_price
WINDOW w AS (PARTITION BY category_id ORDER BY price);
#举例:按照价格排序,查询最后一个商品的价格信息。
SELECT id, category, NAME, price, stock,LAST_VALUE(price)OVER w AS first_price
WINDOW w AS (PARTITION BY category_id ORDER BY price);
#举例:查询goods数据表中排名第2和第3的价格信息
SELECT id, category, NAME, price,NTH_VALUE(price,2)OVER w AS second_price,
NTH_VALUE(price,3)OVER w AS third_price
WINDOW w AS (PARTITION BY category_id ORDER BY price);
SELECT NTILE(3) OVER w AS nt,id,category, NAME, price
WINDOW w AS (PARTITION BY category_id ORDER BY price);
##################################【公用表表达式】##########################################
SELECT * FROM departments
SELECT DISTINCT department_id
WITH cte_emp#相当于临时一个结果集,当成表看待即可
AS (SELECT DISTINCT department_id FROM employees)#注意此处没有结束,不要写分号
FROM departments d JOIN cte_emp e
ON d.`department_id`=e.`department_id`;
AS(SELECT employee_id,last_name,manager_id,1 AS n FROM employees WHERE employee_id = 100 #种子查询,找到第一代领导
SELECT a.employee_id,a.last_name,a.manager_id,n+1 FROM employees AS a JOIN cte
ON (a.manager_id = cte.employee_id) #递归查询,找出以递归公用表表达式的人为领导的人
SELECT employee_id,last_name FROM cte WHERE n >= 3
###################################【初级篇完结】##################################################
