• mysql学习笔记


    1. #############################【mysql初级篇学习笔记】#######################################
    2. #############################学校作业二:#####################################
    3. #2.创建数据库[学生选课数据库](不需要代码,按照上次上机中所创建数据库步骤创建);
    4. CREATE TABLE 课程表(课程号 CHAR(4) PRIMARY KEY,课程名 CHAR(40) NOT NULL,先行课程号 CHAR(4),学分 SMALLINT NOT NULL);
    5. #3.创建[课程表]代码;课程表(课程号 char(4),课程名 char40),先行课程号 char(4),学分 smallint)
    6. # 要求设置:主键(课程号)、非空(课程名, 学分)
    7. CREATE TABLE 学生表(学号 CHAR(9) PRIMARY KEY,姓名 CHAR(20),性别 CHAR(2),年龄 SMALLINT,专业 CHAR(20) NOT NULL);
    8. #4.创建[学生表]代码;学生表(学号 char(9),姓名char20),性别char2),年龄smallint,专业char20))
    9. # 要求设置:主键(学号)、非空(专业)
    10. CREATE TABLE 选课表(学号 CHAR(9),课程号 CHAR(4),分数 SMALLINT,
    11. FOREIGN KEY(学号) REFERENCES 学生表(学号),
    12. FOREIGN KEY(课程号) REFERENCES 课程表(课程号));
    13. #5.修改[课程表],将“学分”列的数据类型改为int
    14. ALTER TABLE 课程表 MODIFY 学分 INT;
    15. #ALTER TABLE 课程表 alter column 学分 char(2); ==>错误的
    16. #6.修改[学生表],在表中增加一列,属性名为“备注”,数据类型为char(20);
    17. ALTER TABLE 学生表 ADD 备注 CHAR(20);
    18. #7.修改[学生表],对“姓名”列增加唯一约束条件;
    19. ALTER TABLE 学生表 ADD UNIQUE(姓名);
    20. #8.修改[学生表],删除表中新增的“备注”列;
    21. ALTER TABLE 学生表 DROP 备注;
    22. #9.删除[选课表]。
    23. DROP TABLE 选课表;#伪删除
    24. #############################################################################################
    25. #having不可以单独使用,只能和group by一起使用;wherehaving后写过滤条件,建议方式一where,因为执行效率高
    26. #结论:当过滤条件中有聚合函数时,必须写在having;若没有,则havingwhere均可,但建议使用where(必须)。
    27. SELECT department_id,MAX(salary) FROM employees WHERE department_id IN (10,20,30,40,50) GROUP BY department_id HAVING MAX(salary)>10000;
    28. SELECT department_id,MAX(salary) FROM employees GROUP BY department_id HAVING MAX(salary)>10000 AND department_id IN (10,30,40,50);
    29. #SQL底层执行原理
    30. SELECT ...,...,... 包含聚合函数
    31. FROM ... (LEFT / RIGHT)JOIN ... ON 多表连接条件
    32. (LEFT / RIGHT)JOIN ... ON ...
    33. WHERE 多表的连接条件 AND 不包含聚合函数的过滤条件
    34. GROUP BY ...,...
    35. HAVING 包含聚合函数的过滤条件
    36. ORDER BY ...,... (ASC,DESC)
    37. LIMIT ...,...
    38. #执行过程
    39. FROM ...,...->ON->(LEFT/RIGHT JOIN)->WHERE->GROUP BY->HAVING->SELECT->DISTINCT->ORDER BY->LIMIT
    40. #1.where子句可否使用组函数进行过滤?
    41. 不可以
    42. #2.查询公司员工工资的最大值,最小值,平均值,总和
    43. SELECT MAX(salary) max_sal,MIN(salary) min_sal,AVG(salary) avg_sal,SUM(salary) sum_sal FROM employees;
    44. #3.查询各job_id的员工工资的最大值,最小值,平均值,总和
    45. SELECT job_id,MIN(salary),MAX(salary),AVG(salary),SUM(salary) FROM employees GROUP BY job_id;
    46. #4.选择具有各个job_id的员工人数
    47. SELECT job_id,COUNT(*) FROM employees GROUP BY job_id;
    48. # 5.查询员工最高工资和最低工资的差距(DIFFERENCE)
    49. SELECT MAX(salary),MIN(salary),MAX(salary)-MIN(salary) 'diff' FROM employees;
    50. # 6.查询各个管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内
    51. SELECT MIN(salary) FROM employees WHERE manager_id IS NOT NULL GROUP BY manager_id HAVING MIN(salary)>=6000;
    52. # 7.查询所有部门的名字,location_id,员工数量和平均工资,并按平均工资降序
    53. SELECT d.department_name,d.location_id,COUNT(*),AVG(e.salary)
    54. FROM employees e RIGHT JOIN departments d
    55. ON e.department_id = d.department_id
    56. GROUP BY d.`department_name`,d.`location_id`
    57. HAVING AVG(salary)IS NOT NULL
    58. ORDER BY AVG(salary);
    59. # 8.查询每个工种、每个部门的部门名、工种名和最低工资
    60. SELECT department_name,job_id,MIN(salary)
    61. FROM departments d JOIN employees e
    62. ON d.`department_id` = e.`department_id`
    63. GROUP BY department_name,job_id;
    64. #子查询
    65. #需求:i准的工资比Abel的高?
    66. #方式一:自连接
    67. SELECT e2.last_name,e2.salary
    68. FROM employees e1,employees e2
    69. WHERE e2.`salary` > e1.`salary`#多表的连接条件
    70. AND e1.last_name = 'Abel';
    71. #方式二:子查询
    72. SELECT last_name,salary
    73. FROM employees
    74. WHERE salary > (SELECT salary
    75. FROM employees
    76. WHERE last_name = 'Abel');
    77. #2.称谓的规范:外查询(或者主查询),内查询(或内查询)
    78. #注意事项:a.子查询要包含在括号内;b.将子查询放在比较条件的右侧;c.单行操作符对应单行子查询,多行操作符对应多行子查询
    79. /*子查询的分类:
    80. 1.单行子查询【结果一行】 VS 多行子查询【多行结果】;
    81. 2.内查询是否被执行多次:相关子查询 VS 不相关子查询。
    82. */
    83. #单行子查询 单行操作符:<,>,=,<=,>=,<>不等于符号
    84. /*题目:显式员工的employee_id,last_name和location.
    85. 其中,若员工department id与location_id为1800的department_id相同,
    86. 则location为' Canada',其余则为'UsA’。*/
    87. SELECT employee_id,last_name,(CASE department_id WHEN (SELECT department_id FROM departments WHERE location_id=1800)
    88. THEN 'Canada'
    89. ELSE 'USA'
    90. END) "location"
    91. FROM employees;
    92. #多行子查询 多行操作符:in,【any,all,some(同any) 与单行操作符混合使用】
    93. SELECT employee_id,last_name
    94. FROM employees
    95. WHERE salary IN (SELECT MIN(salary)FROM employees GROUP BY department_id);
    96. #anyall的区别
    97. /*题目:返回其它job_id中比job_id为'IT_PROG’部门任一工资低的员工的员工号、
    98. 姓名、job_id以及salary
    99. */
    100. SELECT employee_id,last_name,job_id,salary
    101. FROM employees
    102. WHERE salary < ANY(SELECT salary FROM employees WHERE job_id='IT_PROG')
    103. AND job_id <> 'IT_PROG';
    104. SELECT employee_id,last_name,job_id,salary
    105. FROM employees
    106. WHERE salary < ALL(SELECT salary FROM employees WHERE job_id='IT_PROG')
    107. AND job_id <> 'IT_PROG';
    108. #【难】题目:查询平均工资最低的部门id【注意:mysql中聚合函数不可以嵌套,其他的也许可以,比如oracle】
    109. SELECT MIN(avg_sal)
    110. FROM (SELECT AVG(salary) avg_sal FROM employees GROUP BY department_id)t_dept_avg_sal
    111. SELECT department_id
    112. FROM employees
    113. GROUP BY department_id
    114. HAVING AVG(salary)=(SELECT MIN(avg_sal)FROM (SELECT AVG(salary) avg_sal FROM employees GROUP BY department_id)t_dept_avg_sal);
    115. #相关子查询
    116. #题目:查询员工中工资大于本部门平均工资的员工的last_name,salary和其department_id
    117. SELECT last_name,salary,department_id
    118. FROM employees e1
    119. WHERE salary > (SELECT AVG(salary)FROM employees e2 WHERE e2.`department_id`=e1.`department_id`);
    120. #题目:查询员工的id,salary,按照department_name排序【难】
    121. SELECT employee_id,salary
    122. FROM employees e
    123. ORDER BY (
    124. SELECT department_name
    125. FROM departments d
    126. WHERE e.`department_id`=d.`department_id`
    127. ) ASC;
    128. #结论:在select中,除了group by和limit之外,其他位置都可以声明子查询
    129. /*【重要】
    130. 题目:若employees表中employee_id与job_history表中employee_id相同的数目不小于2,
    131. 输出这些相同id的员工的employee_id,last_name和其job_id
    132. */
    133. SELECT employee_id,last_name,job_id
    134. FROM employees e
    135. WHERE 2<=(SELECT COUNT(*) FROM job_history j WHERE e.`employee_id` = j.`employee_id`);
    136. #existsnot exists关键字
    137. #题目:查询公司管理者的employee_id,last_name,job_id,department_id信息
    138. #方式一:自连接
    139. SELECT DISTINCT mgr.employee_id,mgr.last_name,mgr.job_id,mgr.department_id
    140. FROM employees emp JOIN employees mgr
    141. ON emp.manager_id = mgr.employee_id;
    142. #方式二:子查询
    143. SELECT employee_id,last_name,job_id,department_id
    144. FROM employees
    145. WHERE employee_id IN(SELECT DISTINCT manager_id FROM employees);
    146. #方式三:使用exists
    147. SELECT employee_id,last_name,job_id,department_id
    148. FROM employees e1
    149. WHERE EXISTS(SELECT * FROM employees e2 WHERE e1.`employee_id`=e2.`manager_id`);
    150. #1.查询和Zlotkey相同部门的员工姓名和工资
    151. SELECT last_name,salary,department_id
    152. FROM employees e1
    153. WHERE e1.`department_id` = (SELECT e2.department_id FROM employees e2 WHERE e2.`last_name`="Zlotkey")
    154. AND e1.`last_name` <> "Zlotkey"
    155. #2.查询工资比公司平均工资高的员工的员工号,姓名和工资。
    156. SELECT employee_id,last_name,salary
    157. FROM employees
    158. WHERE salary > (SELECT AVG(salary) FROM employees);
    159. #3.选择工资大于所有JOB_ID = 'SA_MAN'的员工的工资的员工的last_name, job_id, salary
    160. SELECT last_name,job_id,salary
    161. FROM employees
    162. WHERE salary > ALL(SELECT salary FROM employees WHERE job_id="SA_MAN");
    163. #4.查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名
    164. SELECT employee_id,last_name
    165. FROM employees
    166. WHERE department_id = ANY (SELECT department_id FROM employees WHERE last_name LIKE "%u%");
    167. SELECT employee_id,last_name
    168. FROM employees
    169. WHERE department_id IN (SELECT department_id FROM employees WHERE last_name LIKE "%u%");
    170. #5.查询在部门的location_id为1700的部门工作的员工的员工号
    171. SELECT employee_id
    172. FROM employees
    173. WHERE department_id = SOME (SELECT department_id FROM departments WHERE location_id = 1700);
    174. #6.查询管理者是King的员工姓名和工资
    175. SELECT e1.last_name,e1.salary,e1.`manager_id`
    176. FROM employees e1,employees e2
    177. WHERE e1.`manager_id` = e2.`employee_id`
    178. AND e2.`last_name` = "King";
    179. SELECT last_name,salary
    180. FROM employees
    181. WHERE manager_id IN (SELECT employee_id FROM employees WHERE last_name = "King");
    182. #7.查询工资最低的员工信息: last_name, salary
    183. SELECT last_name,salary
    184. FROM employees
    185. WHERE salary =(SELECT MIN(salary) FROM employees);
    186. #8.查询平均工资最低的部门信息
    187. SELECT *
    188. FROM departments
    189. WHERE department_id = (SELECT department_id
    190. FROM employees
    191. GROUP BY department_id
    192. HAVING AVG(salary)=(SELECT MIN(dept_avgsal)
    193. FROM (SELECT AVG(salary)dept_avgsal
    194. FROM employees
    195. GROUP BY department_id
    196. )
    197. avg_sal
    198. )
    199. );
    200. #【牛掰】
    201. SELECT d.*
    202. FROM departments d,(SELECT department_id,AVG(salary) avg_sal
    203. FROM employees
    204. GROUP BY department_id
    205. ORDER BY avg_sal ASC
    206. LIMIT 0,1
    207. )t_dept_avg_sal
    208. WHERE d.`department_id`=t_dept_avg_sal.`department_id`
    209. #9.查询平均工资最低的部门信息和该部门的平均工资(相关子查询)
    210. SELECT d.*,t_dept_avg_sal.`avg_sal`
    211. FROM departments d,(SELECT department_id,AVG(salary) avg_sal
    212. FROM employees
    213. GROUP BY department_id
    214. ORDER BY avg_sal ASC
    215. LIMIT 0,1
    216. )t_dept_avg_sal
    217. WHERE d.`department_id`=t_dept_avg_sal.`department_id`;
    218. #10.查询平均工资最高的 job 信息
    219. #【方法一】排序取最大
    220. SELECT j.*
    221. FROM jobs j,(SELECT job_id,AVG(salary) avg_sal
    222. FROM employees
    223. GROUP BY job_id
    224. ORDER BY avg_sal DESC
    225. LIMIT 0,1
    226. )t_max_avg_sal
    227. WHERE j.`job_id`=t_max_avg_sal.`job_id`;
    228. #【方法二】一个一个比较找最大
    229. SELECT *
    230. FROM jobs
    231. WHERE job_id=(
    232. SELECT job_id
    233. FROM employees
    234. GROUP BY job_id
    235. HAVING AVG(salary)=(SELECT MAX(avg_sal)
    236. FROM (SELECT AVG(salary) avg_sal
    237. FROM employees
    238. GROUP BY job_id
    239. )t_job_avg_sal
    240. )
    241. );
    242. #【方法三】>=all
    243. SELECT *
    244. FROM jobs
    245. WHERE job_id=(
    246. SELECT job_id
    247. FROM employees
    248. GROUP BY job_id
    249. HAVING AVG(salary)>= ALL(SELECT AVG(salary) avg_sal
    250. FROM employees
    251. GROUP BY job_id
    252. )
    253. );
    254. #11. 查询平均工资高于公司平均工资的部门有哪些?
    255. SELECT department_id
    256. FROM (SELECT department_id,AVG(salary)avg_sal FROM employees WHERE department_id IS NOT NULL GROUP BY department_id)t_dep_avg_sal
    257. WHERE t_dep_avg_sal.`avg_sal` > (SELECT AVG(salary)FROM employees);
    258. SELECT department_id
    259. FROM employees
    260. WHERE department_id IS NOT NULL
    261. GROUP BY department_id
    262. HAVING AVG(salary) > (
    263. SELECT AVG(salary)
    264. FROM employees
    265. );
    266. #12. 查询出公司中所有 manager 的详细信息
    267. SELECT DISTINCT e2.employee_id, e2.last_name, e2.salary
    268. FROM employees e1,employees e2
    269. WHERE e1.`manager_id` = e2.`employee_id`;
    270. SELECT employee_id,last_name,salary
    271. FROM employees
    272. WHERE employee_id IN (SELECT DISTINCT manager_id FROM employees WHERE manager_id IS NOT NULL);
    273. SELECT employee_id, last_name, salary
    274. FROM employees e1
    275. WHERE EXISTS ( SELECT *
    276. FROM employees e2
    277. WHERE e2.manager_id = e1.employee_id);
    278. #13. 各个部门中,最高工资中最低的那个部门的最低工资是多少?
    279. SELECT MIN(salary)
    280. FROM employees
    281. WHERE department_id=(
    282. SELECT department_id
    283. FROM employees
    284. GROUP BY department_id
    285. HAVING MAX(salary)=(
    286. SELECT MIN(max_sal)
    287. FROM (
    288. SELECT MAX(salary) max_sal
    289. FROM employees
    290. GROUP BY department_id
    291. )t_dept_max_sal
    292. )
    293. );
    294. SELECT MIN(salary)
    295. FROM employees
    296. WHERE department_id = (
    297. SELECT department_id
    298. FROM employees
    299. GROUP BY department_id
    300. HAVING MAX(salary) <= ALL(
    301. SELECT MAX(salary) max_sal
    302. FROM employees
    303. GROUP BY department_id
    304. )
    305. );
    306. #14. 查询平均工资最高的部门的 manager 的详细信息: last_name,department_id, email, salary
    307. SELECT last_name,department_id,email,salary
    308. FROM employees
    309. WHERE employee_id = (
    310. SELECT manager_id
    311. FROM departments d,(
    312. SELECT department_id,AVG(salary) avg_sal
    313. FROM employees
    314. GROUP BY department_id
    315. ORDER BY avg_sal DESC
    316. LIMIT 0,1
    317. ) t_emp_avg_sal
    318. WHERE d.`department_id` = t_emp_avg_sal.`department_id`
    319. );
    320. #15. 查询部门的部门号,其中不包括job_id是"ST_CLERK"的部门号
    321. SELECT department_id
    322. FROM departments
    323. WHERE department_id NOT IN (
    324. SELECT DISTINCT department_id
    325. FROM employees
    326. WHERE job_id = 'ST_CLERK'
    327. );
    328. #方法二:
    329. SELECT department_id
    330. FROM departments d
    331. WHERE NOT EXISTS (
    332. SELECT *
    333. FROM employees e
    334. WHERE d.`department_id` = e.`department_id`
    335. AND job_id = 'ST_CLERK'
    336. );
    337. #16. 选择所有没有管理者的员工的last_name
    338. SELECT last_name,manager_id
    339. FROM employees
    340. WHERE manager_id IS NULL;
    341. SELECT last_name
    342. FROM employees e1
    343. WHERE NOT EXISTS (
    344. SELECT *
    345. FROM employees e2
    346. WHERE e1.manager_id = e2.employee_id
    347. );
    348. #17.查询员工号、姓名、雇用时间、工资,其中员工的管理者为 'De Haan'
    349. SELECT employee_id,last_name,hire_date,salary
    350. FROM employees
    351. WHERE manager_id = (
    352. SELECT employee_id
    353. FROM employees
    354. WHERE last_name = "De Haan"
    355. );
    356. SELECT employee_id,last_name,hire_date,salary
    357. FROM employees
    358. GROUP BY manager_id
    359. HAVING manager_id = (
    360. SELECT employee_id
    361. FROM employees
    362. WHERE last_name = "De Haan"
    363. );
    364. #18.查询各部门中工资比本部门平均工资高的员工的员工号, 姓名和工资(难)
    365. #19.查询每个部门下的部门人数大于 5 的部门名称
    366. SELECT department_name,department_id
    367. FROM departments d
    368. WHERE 5 < (
    369. SELECT COUNT(*)
    370. FROM employees e
    371. WHERE d.`department_id` = e.`department_id`
    372. )
    373. #20.查询每个国家下的部门个数大于 2 的国家编号
    374. SELECT country_id
    375. FROM locations l
    376. WHERE 2<(
    377. SELECT COUNT(*)
    378. FROM departments d
    379. WHERE l.`location_id`=d.`location_id`
    380. );
    381. #####################################1.数据库的操作#################################
    382. #1.数据库的操作
    383. #1.1创建数据库
    384. #方式一:使用的是默认的字符集
    385. CREATE DATABASE dbtest4;
    386. #方式二:显示的指明了要创建的数据库的字符集
    387. CREATE DATABASE dbtest3 CHARACTER SET 'gbk';
    388. SHOW CREATE DATABASE dbtest3;
    389. #方式三
    390. CREATE DATABASE IF NOT EXISTS dbtest5 CHARACTER SET 'utf8';
    391. #1.2 管理数据库
    392. #查看所有数据库
    393. SHOW DATABASES;
    394. #切换数据库
    395. USE atguigudb;
    396. #查看当前数据库中的表
    397. SHOW TABLES;
    398. #查看当前使用的数据库
    399. SELECT DATABASE() FROM DUAL;
    400. #查看指定数据库下的表
    401. SHOW TABLES FROM atguigudb;
    402. #1.3修改数据库【不要修改】
    403. SHOW CREATE DATABASE dbtest4;
    404. #修改字符集
    405. ALTER DATABASE dbtest4 CHARACTER SET 'gbk';
    406. #【注意】DATABASE 不能改名。一些可视化工具可以改名,它是建新库,
    407. #把所有表复制到新库,再删旧库。
    408. #1.4删除数据库
    409. #方式一
    410. DROP DATABASE dbtest4;
    411. #方式二:安全 [建议]
    412. DROP DATABASE IF EXISTS dbtest4;
    413. #####################################2.创建表#################################
    414. #2.创建数据表
    415. USE atguigudb;
    416. SHOW CREATE DATABASE atguigudb;
    417. #方式一:白手起家
    418. CREATE TABLE IF NOT EXISTS myemp1(#需要用户具备创建表的权限
    419. id INT,
    420. emp_name VARCHAR(15),
    421. hire_date DATE
    422. );
    423. #查看表结构
    424. DESC myemp1;
    425. #查看创建表语句的结构
    426. SHOW CREATE TABLE myemp1;#如果创建表时没有指定使用的字符集,则默认使用表所在的数据库字符集
    427. SELECT * FROM myemp1;
    428. #方式二:基于现有的表创建新表(属性+数据 全部copy)
    429. CREATE TABLE myemp2 AS (SELECT employee_id,last_name,salary FROM employees);
    430. DESC myemp2;
    431. SHOW CREATE TABLE myemp2;
    432. SELECT * FROM myemp2;
    433. #说明1:查询语句中字段的别名,可以作为新创建的表的字段的名称。
    434. #说明2:此时的查询语句可以结构比较丰富,使用前面章节讲过的各种SELECT
    435. #练习:创建一个表employees_blank,实现对employees表的复制,不包括表数据
    436. CREATE TABLE employees_blank
    437. AS
    438. SELECT *
    439. FROM employees
    440. ORDER BY employee_id
    441. LIMIT 0;
    442. CREATE TABLE IF NOT EXISTS employees_blank
    443. AS
    444. SELECT *
    445. FROM employees
    446. WHERE FALSE;
    447. SELECT * FROM employees_blank;
    448. USE atguigudb;
    449. #####################################3.修改表#################################
    450. #3.修改表 -> alter table
    451. DESC myemp1;
    452. #3.1 添加一个字段 默认添加到表中的最后一个字段的位置
    453. #add first after
    454. ALTER TABLE myemp1
    455. ADD salary DOUBLE(10,2);#共10位,小数点后2
    456. ALTER TABLE myemp1
    457. ADD phone_number VARCHAR(20) FIRST;#添加到第一个位置
    458. ALTER TABLE myemp1
    459. ADD email VARCHAR(45) AFTER emp_name;#指定位置
    460. DESC myemp1;
    461. #3.2修改一个字段:数据类型,长度,默认值 modify default
    462. ALTER TABLE myemp1
    463. MODIFY emp_name VARCHAR(25);
    464. ALTER TABLE myemp1
    465. MODIFY emp_name VARCHAR(30) DEFAULT 'aaa';#默认值 前面必须有 varchar
    466. #3.3重命名一个字段 change
    467. ALTER TABLE myemp1
    468. CHANGE salary monthly_salary DOUBLE(10,2);
    469. ALTER TABLE myemp1
    470. CHANGE email my_email INT;#修改名字的同时,也可以修改数据类型、长度
    471. #3.4删除一个字段
    472. ALTER TABLE myemp1
    473. DROP COLUMN my_email;
    474. DESC myemp1;
    475. #####################################4.重命名表#################################
    476. #方式一:rename...to【建议】
    477. RENAME TABLE myemp1
    478. TO myemp11;
    479. #方式二:alter...rename to
    480. ALTER TABLE myemp2
    481. RENAME TO myemp1;
    482. #####################################5.删除表#################################
    483. #将表结构删除掉,同时表中的数据也删除表,释放表空间
    484. CREATE TABLE myemp2(id CHAR(10),my_name VARCHAR(20));
    485. DROP TABLE IF EXISTS myemp2;
    486. #####################################6.清除表#################################
    487. #清空表,表示清空表中的所有数据,但是表结构保留。truncate
    488. TRUNCATE TABLE employees_copy;
    489. CREATE TABLE employees_copy AS SELECT * FROM employees;
    490. SELECT * FROM employees_copy;
    491. DESC employees_copy;
    492. #############################7.DCL中的commitrollback#################################
    493. #COMMIT:提交数据。一旦执行CONMIT,则数据就被永久的保存在了数据库中,意味着数据不可以回滚(撤销)。
    494. #ROLLBACK:回滚数据。一旦执行ROLLBACK,则可以实现数据的回滚。回滚到最近的一次COMMIT之后。
    495. #8.对比TRUNCATE TABLEDELETE FROM
    496. #相同点:都可以实现对表中所有数据的删除,同时保留表结构。
    497. #不同点:
    498. # TRUNCATE TABLE:一旦执行此操作,表数据全部清除。同时,数据是不可以回滚的。
    499. # DELETE FROM:一旦执行此操作”表数据可以全部清除。同时,数据是可以回滚的
    500. #9.DDL和DML的说明
    501. # ①DDL的操作一旦执行,就不可回滚。指令SET autocommit = FALs山x寸DDL操作失效。
    502. # ②DML的操作默认情况,一旦执行,也是不可回滚的。但是,如果在执行DNL之前,执行了
    503. # SET autocommit = FALSE,则执行的DML操作就可以实现回滚。
    504. COMMIT;
    505. SELECT * FROM myemp2;
    506. SET autocommit = FALSE;
    507. DELETE FROM myemp2;
    508. SELECT * FROM myemp2;
    509. ROLLBACK;
    510. SELECT * FROM myemp2;
    511. ###################################创建和管理表【练习题一】####################################
    512. #1. 创建数据库test01_office,指明字符集为utf8。并在此数据库下执行下述操作
    513. CREATE DATABASE IF NOT EXISTS test01_office CHARACTER SET 'utf8';
    514. USE test01_office;
    515. #2. 创建表dept01
    516. CREATE TABLE dept01(
    517. id INT(7),
    518. NAME VARCHAR(25)
    519. );
    520. #3. 将表departments中的数据插入新表dept02中
    521. CREATE TABLE dept02
    522. AS
    523. SELECT * FROM atguigudb.departments;
    524. #4. 创建表emp01
    525. CREATE TABLE emp01(
    526. id INT(7),
    527. first_name VARCHAR(25),
    528. last_name VARCHAR(25),
    529. dept_id INT(7)
    530. );
    531. #5. 将列last_name的长度增加到50
    532. DESC emp01;
    533. ALTER TABLE emp01
    534. MODIFY last_name VARCHAR(50);
    535. #6. 根据表employees创建emp02
    536. CREATE TABLE emp02
    537. AS
    538. SELECT * FROM atguigudb.employees;
    539. #7. 删除表emp01
    540. DROP TABLE IF EXISTS emp01;
    541. #8. 将表emp02重命名为emp01
    542. ALTER TABLE emp02
    543. RENAME TO emp01;
    544. RENAME TABLE emp01 TO emp02;
    545. #9. 在表dept02和emp01中添加新列test_column,并检查所作的操作
    546. ALTER TABLE dept02 ADD test_column VARCHAR(10);
    547. DESC dept02;
    548. ALTER TABLE emp01 ADD test_column VARCHAR(10);
    549. DESC emp01;
    550. #10.直接删除表emp01中的列 department_id
    551. ALTER TABLE emp01
    552. DROP COLUMN department_id;
    553. ###################################创建和管理表【练习题二】####################################
    554. # 11、创建数据库 test02_market
    555. CREATE DATABASE test02_market;
    556. USE test02_market;
    557. # 12、创建数据表 customers
    558. CREATE TABLE customers(
    559. c_num INT,
    560. c_name VARCHAR(50),
    561. c_contact VARCHAR(50),
    562. c_city VARCHAR(50),
    563. c_birth DATE
    564. );
    565. # 13、将 c_contact 字段移动到 c_birth 字段后面【注意此题】
    566. ALTER TABLE customers MODIFY c_contact VARCHAR(50) AFTER c_birth;#必须加上VARCHAR(50)
    567. # 14、将c_name字段数据类型改为 varchar(70)
    568. ALTER TABLE customers MODIFY c_name VARCHAR(70);
    569. # 15、将c_contact字段改名为c_phone【注意此题】
    570. ALTER TABLE customers CHANGE c_contact c_phone VARCHAR(50);
    571. # 16、增加c_gender字段到c_name后面,数据类型为char(1)
    572. ALTER TABLE customers ADD c_gender CHAR(1) AFTER c_name;
    573. # 17、将表名改为customers_info
    574. RENAME TABLE customers TO customers_info;
    575. # 18、删除字段c_city
    576. ALTER TABLE customers_info DROP COLUMN c_city;
    577. ###################################创建和管理表【练习题三】####################################
    578. # 1、创建数据库test03_company
    579. CREATE DATABASE test03_company;
    580. USE test03_company;
    581. # 2、创建表offices
    582. CREATE TABLE offices(
    583. officeCode INT,
    584. city VARCHAR(30),
    585. address VARCHAR(50),
    586. country VARCHAR(50),
    587. postalCode VARCHAR(25)
    588. );
    589. DESC offices;
    590. # 3、创建表employees
    591. CREATE TABLE employees(
    592. empNum INT,
    593. lastName VARCHAR(50),
    594. firstName VARCHAR(50),
    595. mobile VARCHAR(25),
    596. CODE INT,
    597. jobTitle VARCHAR(50),
    598. birth DATE,
    599. note VARCHAR(255),
    600. sex VARCHAR(5)
    601. );
    602. DESC employees;
    603. # 4、将表employees的mobile字段修改到code字段后面
    604. ALTER TABLE employees MODIFY mobile VARCHAR(25) AFTER CODE;
    605. # 5、将表employees的birth字段改名为birthday 【重要:change改名】
    606. ALTER TABLE employees CHANGE birth birthday DATE;
    607. # 6、修改sex字段,数据类型为char(1)
    608. ALTER TABLE employees MODIFY sex CHAR(1);
    609. # 7、删除字段note
    610. ALTER TABLE employees DROP COLUMN note;
    611. # 8、增加字段名favoriate_activity,数据类型为varchar(100)
    612. ALTER TABLE employees ADD favoriate_activity VARCHAR(100);
    613. DESC employees;
    614. # 9、将表employees的名称修改为 employees_info
    615. RENAME TABLE employees TO employees_info;
    616. ###################################数据处理之增删改###########################################
    617. #0.筹备工作
    618. USE atguigudb;
    619. CREATE TABLE IF NOT EXISTS emp1(
    620. id INT,
    621. `name` VARCHAR(15),
    622. hire_date DATE,
    623. salary DOUBLE(10,2)
    624. );
    625. DESC emp1;
    626. SELECT * FROM emp1;
    627. #1.插入数据 insert into ... values (),()...
    628. #方式一:一条一条的
    629. #①没有指明添加的字段,必须严格按照声明的字段的先后顺序
    630. INSERT INTO emp1 VALUES(1,'Tom','2002-12-21',3400);#value也可以
    631. INSERT INTO emp1 VALUES(2,'Jerry','2001-10-01',3500);
    632. #②指明要添加的字段【推荐】
    633. INSERT INTO emp1(id,hire_date,salary,`NAME`) VALUES(3,'1999-02-17',800,'Jack');
    634. #没有进行赋值的值为NULL
    635. INSERT INTO emp1(id,salary,`NAME`) VALUES(4,800,'ZMJ');#可以不写全,NULL
    636. #③同时插入多条记录,逗号隔开【推荐】
    637. INSERT INTO emp1(id,hire_date,salary,`NAME`)
    638. VALUES (5,'2001-09-19',900,'李玟琪'),(6,'2003-10-20',1002,'张梦姣');
    639. #方式二:将查询结果插入表中
    640. SELECT * FROM emp1;
    641. INSERT INTO emp1(id,NAME,salary,hire_date)
    642. SELECT employee_id,last_name,salary,hire_date FROM employees WHERE department_id IN (60,70);
    643. SELECT * FROM emp1;
    644. #说明: empl表中要添加数据的字段的长度不能低于employees表中查询的字段的长度。
    645. #2.更新数据(修改数据)
    646. #update ... set ... where ... 可以实现批量修改
    647. UPDATE emp1
    648. SET hire_date=CURDATE()
    649. WHERE id=4;#没有where,则全部修改
    650. SELECT * FROM emp1;
    651. #同时修改一条数据的多个字段:逗号隔开即可
    652. UPDATE emp1
    653. SET hire_date=CURDATE(),salary=10000
    654. WHERE id=4;
    655. #3.删除数据 delete from ... where ...
    656. DELETE FROM emp1
    657. WHERE id=1;
    658. #小结:DML操作默认情况下,执行完以后都会自动提交数据。
    659. #如果希望执行完以后不自动提交数据,则需要使用SET autocommit = FALSE.
    660. #4.MySQL8新特性:计算列
    661. #计算列:简单来说就是某一列的值是通过别的列计算得来的。
    662. CREATE TABLE test1(
    663. a INT,
    664. b INT,
    665. c INT GENERATED ALWAYS AS (a+b)VIRTUAL
    666. );
    667. INSERT INTO test1(a,b)#必须指定a b
    668. VALUES (1,2),(10,20),(100,200);
    669. UPDATE test1
    670. SET a=1000,b=2000
    671. WHERE a=1 AND b=2;
    672. SELECT * FROM test1;
    673. ###################################增删改【练习题一】###################################
    674. #1. 创建数据库dbtest11
    675. CREATE DATABASE IF NOT EXISTS dbtest11 CHARACTER SET 'utf8';
    676. #2. 运行以下脚本创建表my_employees
    677. USE dbtest11;
    678. CREATE TABLE my_employees(
    679. id INT(10),
    680. first_name VARCHAR(10),
    681. last_name VARCHAR(10),
    682. userid VARCHAR(10),
    683. salary DOUBLE(10,2)
    684. );
    685. CREATE TABLE users(
    686. id INT,
    687. userid VARCHAR(10),
    688. department_id INT
    689. );
    690. #3. 显示表my_employees的结构
    691. DESC my_employees;
    692. SELECT * FROM my_employees;
    693. #4. 向my_employees表中插入下列数据
    694. INSERT INTO my_employees
    695. VALUES (1,'patel','Ralph','Rpatel',895),
    696. (2,'Dancs','Betty','Bdancs',860),
    697. (3,'Biri','Ben','Bbiri',1100),
    698. (4,'Newman','Chad','Cnewman',750),
    699. (5,'Ropeburn','Audrey','Aropebur',1550);
    700. SELECT * FROM my_employees;
    701. #5. 向users表中插入数据
    702. INSERT INTO users
    703. VALUES (1,'Rpatel',10),
    704. (2,'Bdancs',10),
    705. (3,'Bbiri',20),
    706. (4,'Cnewman',30),
    707. (5,'Aropebur',40);
    708. SELECT * FROM users;
    709. #6.3号员工的last_name修改为“drelxer”
    710. UPDATE my_employees
    711. SET last_name='drelxer'
    712. WHERE id=3;
    713. #7. 将所有工资少于900的员工的工资修改为1000
    714. UPDATE my_employees
    715. SET salary=1000
    716. WHERE salary<900;
    717. #8. 将userid为Bbiri的user表和my_employees表的记录全部删除【注意此题】
    718. DELETE u,e
    719. FROM users u JOIN my_employees e
    720. ON u.`userid` = e.`userid`
    721. WHERE u.`userid` = 'Bbiri';
    722. #9. 删除my_employees、users表所有数据
    723. DELETE FROM users;
    724. DELETE FROM my_employees;
    725. #10. 检查所作的修正
    726. SELECT * FROM users;
    727. SELECT * FROM my_employees;
    728. #11. 清空表my_employees
    729. TRUNCATE TABLE my_employees;
    730. ###################################增删改【练习题二】###################################
    731. # 1. 使用现有数据库dbtest11
    732. USE dbtest11;
    733. # 2. 创建表格pet
    734. CREATE TABLE pet(
    735. `name` VARCHAR(20),
    736. `owner` VARCHAR(20),
    737. species VARCHAR(20),
    738. sex CHAR(1),
    739. birth YEAR,
    740. death YEAR
    741. );
    742. # 3. 添加记录
    743. INSERT INTO pet VALUES('Fluffy','harold','Cat','f','2013','2010');
    744. INSERT INTO pet(`name`,`owner`,species,sex,Birth)
    745. VALUES('Claws','gwen','Cat','m','2014');
    746. INSERT INTO pet(`name`,species,sex,Birth) VALUES('Buffy','Dog','f','2009');
    747. INSERT INTO pet(`name`,`owner`,species,sex,Birth)
    748. VALUES('Fang','benny','Dog','m','2000');
    749. INSERT INTO pet VALUES('bowser','diane','Dog','m','2003','2009');
    750. INSERT INTO pet(`name`,species,sex,birth) VALUES('Chirpy','Bird','f','2008');
    751. SELECT * FROM pet;
    752. # 4. 添加字段:主人的生日owner_birth DATE类型
    753. ALTER TABLE pet
    754. ADD COLUMN owner_birth DATE;
    755. # 5. 将名称为Claws的猫的主人改为kevin
    756. UPDATE pet
    757. SET `owner`='kevin'
    758. WHERE NAME='Claws'
    759. AND species='Cat';
    760. # 6. 将没有死的狗的主人改为duck
    761. UPDATE pet
    762. SET `owner`='duck'
    763. WHERE death IS NULL AND species = 'Dog';
    764. # 7. 查询没有主人的宠物的名字
    765. SELECT `name`
    766. FROM pet
    767. WHERE `owner` IS NULL;
    768. # 8. 查询已经死了的cat的姓名,主人,以及去世时间
    769. SELECT `name`,`owner`,death
    770. FROM pet
    771. WHERE species='Cat' AND death IS NOT NULL;
    772. # 9. 删除已经死亡的狗
    773. DELETE FROM pet
    774. WHERE species = 'Dog' AND death IS NOT NULL;
    775. # 10. 查询所有宠物信息
    776. SELECT * FROM pet;
    777. ###################################增删改【练习题三】###################################
    778. # 1. 使用已有的数据库dbtest11
    779. USE dbtest11;
    780. # 2. 创建表employee,并添加记录
    781. CREATE TABLE employee(
    782. id INT,
    783. `name` VARCHAR(20),
    784. sex VARCHAR(2),
    785. tel VARCHAR(20),
    786. addr VARCHAR(50),
    787. salary DOUBLE
    788. );
    789. INSERT INTO employee(id,`name`,sex,tel,addr,salary)
    790. VALUES
    791. (10001,'张一一','男','13456789000','山东青岛',1001.58),
    792. (10002,'刘小红','女','13454319000','河北保定',1201.21),
    793. (10003,'李四','男','0751-1234567','广东佛山',1004.11),
    794. (10004,'刘小强','男','0755-5555555','广东深圳',1501.23),
    795. (10005,'王艳','男','020-1232133','广东广州',1405.16);
    796. # 3. 查询出薪资在1200~1300之间的员工信息`dbtest11`
    797. SELECT * FROM employee WHERE salary>=1200 AND salary<=1300;
    798. # 4. 查询出姓“刘”的员工的工号,姓名,家庭住址
    799. SELECT id,`name`,addr
    800. FROM employee
    801. WHERE `name` LIKE '刘%';
    802. # 5. 将“李四”的家庭住址改为“广东韶关”
    803. UPDATE employee
    804. SET addr='广东韶关'
    805. WHERE `name`='李四';
    806. # 6. 查询出名字中带“小”的员工
    807. SELECT `name`
    808. FROM employee
    809. WHERE `name` LIKE '%小%';
    810. ######################################数据类型精讲#####################################
    811. #关于属性:character set name
    812. #【1】创建数据库时指明字符集类型
    813. CREATE DATABASE IF NOT EXISTS dbtest12 CHARACTER SET 'utf8';
    814. #【2】创建表时候,指定
    815. CREATE TABLE temp(
    816. id INT,
    817. NAME VARCHAR(10)
    818. )CHARACTER SET 'utf8';
    819. #【3】创建表时,指表中的字段时,可以指定
    820. CREATE TABLE temp1(
    821. id INT,
    822. NAME VARCHAR(10) CHARACTER SET 'gbk'
    823. );
    824. SHOW CREATE TABLE temp1;
    825. #【一、整数类型】tinyint 1字节、smallint 2字节、mediumint 3字节、int (integer) 4字节和bigint 8字节.
    826. USE dbtest12;
    827. CREATE TABLE test_int1(
    828. f1 TINYINT,
    829. f2 SMALLINT,
    830. f3 MEDIUMINT,
    831. f4 INT,
    832. f5 BIGINT
    833. );
    834. DESC test_int1;
    835. INSERT INTO test_int1(f1)
    836. VALUES(127),(-128),(0),(1000);#超出范围报错
    837. SELECT * FROM test_int1;
    838. #指定显示宽度 配合zerofill使用
    839. CREATE TABLE test_int2(
    840. f1 INT,
    841. f2 INT(5),#括号中的数字表示显示宽度,不改变本身数值,只是显示
    842. f3 INT(5) ZEROFILL#不足5位,用0向前填充
    843. );
    844. INSERT INTO test_int2(f1,f2)
    845. VALUES (123,123),(123456,123456);
    846. SELECT * FROM test_int2;
    847. INSERT INTO test_int2(f3)
    848. VALUES (123),(123456);
    849. #unsigned
    850. CREATE TABLE test_int3(
    851. f1 INT UNSIGNED
    852. );
    853. DESC test_int3;
    854. INSERT INTO test_int3(f1)
    855. VALUES (21),(100);
    856. SELECT * FROM test_int3;
    857. /*【整数类型使用场景】
    858. TINYINT:一般用于枚举数据,比如系统设定取值范围很小且固定的场景。
    859. SMALLINT:可以用于较小范围的统计数据,比如统计工厂的固定资产库存数量等。
    860. MEDIUMINT:用于较大整数的计算,比如车站每日的客流量等。
    861. INT、INTEGER︰取值范围足够大,一般情况下不用考虑超限问题,用得最多。比如商品编号。
    862. BIGINT:只有当你处理特别巨大的整数时才会用到。比如双十一的交易量、大型门户网站点击量、主E一生产品持仓等。
    863. */
    864. #【二、浮点类型】
    865. #float 4字节,double 8字节,real(real默认是double类型)
    866. #小数部分超出,则四舍五入; 整数部分超出,报错
    867. CREATE TABLE test_double1(
    868. f1 FLOAT,
    869. f2 FLOAT(5,2),
    870. f3 DOUBLE,
    871. f4 DOUBLE(5,2)
    872. );
    873. DESC test_double1;
    874. SELECT * FROM test_double1;
    875. INSERT INTO test_double1(f1,f2)
    876. VALUES(123.45,123.45);
    877. INSERT INTO test_double1(f2)
    878. VALUES(133.456);#整数部分超出,报错
    879. INSERT INTO test_double1(f3,f4)
    880. VALUES(123.45,123.456);#小数部分超出,则四舍五入
    881. INSERT INTO test_double1(f4)
    882. VALUES(1233.456);#整数部分超出,报错
    883. #【三、定点数类型】
    884. #decimal(M,D) ==>存储空间M+2字节, dec numeric
    885. #定点数在MySQL内部是以字符串的形式进行存储,这就决定了它一定是精准的。
    886. #默认decimal(10,0)
    887. #【四、位类型bit】
    888. #存储形式:二进制 bit(M)长度M,约为(M+7)/8字节
    889. CREATE TABLE test_bit1(
    890. f1 BIT,
    891. f2 BIT(3),
    892. f3 BIT(64)
    893. );
    894. DESC test_bit1;
    895. SELECT * FROM test_bit1;
    896. INSERT INTO test_bit1(f1,f2,f3)
    897. VALUES (1,5,255);#超出指定位数表示的数据范围,报错
    898. #指定进制显示 oct bin hex
    899. SELECT BIN(f1),BIN(f2),BIN(f3),HEX(f3),OCT(f3)
    900. FROM test_bit1;
    901. #显示十进制 +0即可
    902. SELECT f1+0,f2+0
    903. FROM test_bit1;
    904. #【五、日期与时间类型】
    905. #year1个字节,time 时间 3,date 日期 3,datetime 日期时间 8,timestamp 日期时间4.
    906. #year YYYY或YY; time HH:MM:SS; date YYYY-MM-DD
    907. #【5.1 year类型】默认就是year(4),不推荐使用year(2) 可以字符串【建议】,也可以数字
    908. CREATE TABLE test_year(
    909. f1 YEAR,
    910. f2 YEAR(4)
    911. );
    912. INSERT INTO test_year(f1,f2)
    913. VALUES(1921,'2078'),('1999',2018);
    914. SELECT * FROM test_year;
    915. #【5.2 date类型】 显示格式:"YYYY-MM-DD",插入格式:"YYYY-MM-DD"或者"YYYYMMDD" current_date()或now()返回当前系统时间
    916. CREATE TABLE test_date(
    917. f1 DATE
    918. );
    919. INSERT INTO test_date(f1)
    920. VALUES ("2020-07-11"),(20200711),("20200711");#2020-07-11插入错误
    921. SELECT * FROM test_date;
    922. INSERT INTO test_date(f1)
    923. VALUES (CURRENT_DATE());
    924. #【5.3 time类型】 3个字节 标准格式:"HH:MM:SS"或者"HHMMSS"或HHMMSS
    925. #格式:"D HH:MM:SS" "HH:MM:SS" "HH:MM" "D HH:MM" "D HH" "SS" ==>D表示天,转换成小时D*24+HH
    926. CREATE TABLE test_time(
    927. f1 TIME
    928. );
    929. SELECT * FROM test_time;
    930. INSERT INTO test_time(f1)
    931. VALUES ("12:20:19"),("122019");
    932. INSERT INTO test_time(f1)
    933. VALUES ("1 12:20:00"),("1 12:20"),("20");#最后一个是秒数,前两个一样
    934. INSERT INTO test_time(f1)
    935. VALUES (CURRENT_TIME());
    936. INSERT INTO test_time(f1)
    937. VALUES (NOW());#截取时间部分
    938. SELECT CURRENT_DATE(),CURRENT_TIME(),NOW() FROM DUAL;
    939. #【5.4 datetime类型】 ==> 【常用】
    940. #"YYYY-MM-DD HH:MM:SS" "YYYYMMDDHHMMSS"
    941. CREATE TABLE test_datatime(
    942. f1 DATETIME
    943. );
    944. SELECT * FROM test_datatime;
    945. INSERT INTO test_datatime(f1)
    946. VALUES ("2020-10-21 12:12:12"),("20201021121212");#一模一样
    947. INSERT INTO test_datatime(f1)
    948. VALUES (CURRENT_TIMESTAMP()),(NOW()),(SYSDATE());
    949. #【5.5 timestamp类型】
    950. #格式:"YYYY-MM-DD HH:MM:SS"或者"YYYY@MM@DD@HH@MM@SS"
    951. #UTC表示世界统一时间,也叫作世界标准时间。
    952. /*存储数据的时候需要对当前时间所在的时区进行转换,查询数据的时候再将时间转换回当前的时区
    953. 因此,使用TIMESTAMP存储的同一个时间值,在不同的时区查询时会显示不同的时间。*/
    954. CREATE TABLE test_timestamp(
    955. f1 TIMESTAMP
    956. );
    957. SELECT * FROM test_timestamp;
    958. INSERT INTO test_timestamp(f1)
    959. VALUES ("2020-10-21 12:12:12"),("2020@10@21@12@12@12");
    960. INSERT INTO test_timestamp(f1)
    961. VALUES (NOW()),(CURRENT_TIMESTAMP());
    962. #【六、文本字符串类型】
    963. #char(M) varchar(M) tinytext text mediumtext longtext enum set
    964. #【6.1 charchar(M)】: 不指定时,只能存储一位字符或一个汉字
    965. #如果保存时,数据的实际长度比CHAR类型声明的长度小,则会在右侧填充空格以达到指定的长度。
    966. #检索CHAR类型的数据时,CHAR类型的字段会去除尾部的空格。
    967. CREATE TABLE test_char(
    968. f1 CHAR,
    969. f2 CHAR(5)
    970. );
    971. SELECT * FROM test_char;
    972. INSERT INTO test_char(f1,f2)
    973. VALUES ('a','你好帅呀朋'),('好','asfgg');
    974. INSERT INTO test_char(f2)
    975. VALUES ('abc '),('abcde');
    976. SELECT CONCAT(f2,f2) FROM test_char;#自动去掉末尾的空格
    977. #【6.2 varchar(M)】必须指定M
    978. #CHAR(M) 固定长度 浪费存储空间 效率高 存储不大,速度要求高
    979. #VARCHAR(D) 可变长度 节省存储空间 效率低 非CHAR的情况
    980. #【6.3 text类型】存多少就是多少,不像char会自动删去空格
    981. CREATE TABLE test_text(f1 TEXT);
    982. SELECT * FROM test_text;
    983. INSERT INTO test_text(f1)VALUES('abc'),('abc ');
    984. SELECT CONCAT(f1,f1) FROM test_text;#不删除末尾空格,是什么就是什么
    985. #【6.4 enmu类型】单选 只能从枚举值中选一个,忽略大小写
    986. #索引从1开始,添加时,可以数字,也可以字符
    987. #没有限制非空时候,可以添加null,但不可以0
    988. CREATE TABLE test_enum(
    989. season ENUM('春','夏','秋','冬','unknown')
    990. );
    991. SELECT * FROM test_enum;
    992. INSERT INTO test_enum(season) VALUES('春'),('秋'),('unknown');
    993. INSERT INTO test_enum(season) VALUES('UNKNOWN'),('UnKnown');#忽略大小写
    994. INSERT INTO test_enum(season) VALUES(1),('1');#可以使用索引
    995. INSERT INTO test_enum(season) VALUES(NULL);#可以null
    996. #【6.5 set类型】多选
    997. CREATE TABLE test_set(
    998. s SET('A','B','C')
    999. );
    1000. SELECT * FROM test_set;
    1001. INSERT INTO test_set(s)VALUES('A'),('A,B');
    1002. INSERT INTO test_set(s)VALUES('A,B,A');#写重复的不报错,自动过滤掉
    1003. INSERT INTO test_set(s)VALUES('A,D');#写不在范围内的,报错
    1004. CREATE TABLE temp_mul(
    1005. gender ENUM('男','女'),
    1006. hobby SET('吃','喝','玩','乐')
    1007. );
    1008. SELECT * FROM temp_mul;
    1009. INSERT INTO temp_mul(gender,hobby)VALUES('男','吃,喝');
    1010. #【七、二进制字符串类型】
    1011. #binary(M) varbinary(M) tinyblob blob mediumblob longblob
    1012. #【7.1 binaryvarbinary类型】
    1013. #binaryvarbinary类似于charvarchar,只是它们存储的是二进制字符串。
    1014. #binary(M)表示最多存储M个字节,而char是字符;未指定M,表示1
    1015. #varbinary(M):必须指定M
    1016. #【7.2 blob类型】
    1017. #BLOB类型包括TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB4种类型,
    1018. #BLOB是一个二进制大对象,可以容纳可变数量的数据,比如图片,音频,视频
    1019. /*【注意】在实际工作中,往往不会在MySQL数据库中使用BLOB类型存储大对象数据,通常会将图片、音频
    1020. 和视频文件存储到服务器的磁盘上,并将图片、音频和视频的访问路径存储到MySQL中。*/
    1021. CREATE TABLE test_blob1(
    1022. id INT,
    1023. img MEDIUMBLOB
    1024. );
    1025. SELECT * FROM test_blob1;
    1026. INSERT INTO test_blob1(id)VALUES(1001);
    1027. #【八、json类型】
    1028. CREATE TABLE test_json(
    1029. js JSON
    1030. );
    1031. SELECT * FROM test_json;
    1032. INSERT INTO test_json(js)
    1033. VALUES('{"name":"zmj","age":18,"address":{"province":"河南","city":"郑州"}}');
    1034. #提取字段
    1035. SELECT js -> '$.name' AS NAME,js -> '$.age' AS age,js -> '$.address.province'AS province
    1036. FROM test_json;
    1037. #【九、空间类型】略 用到再学习
    1038. #【总结】
    1039. /*在定义数据类型时,如果确定是整数,就用INT;如果是小数,一定用定点数类型DECIMAL(N,D);
    1040. 如果是日期与时间,就用DATETIME。*/
    1041. /*【经验】
    1042. 任何字段如果为非负数,必须是UNSIGNED
    1043. 【强制】小数类型为DECIMAL,禁止使用FLOAT和DOUBLE。
    1044. 说明:在存储的时候,FLOAT和DOUBLE都存在精度损失的问题,很可能在比较值的时候,得到不正确的结果。
    1045. 如果存储的数据范围超过DECIMAL的范围,建议将数据拆成整数和小数并分开存储。
    1046. 【强制】如果存储的字符串长度几乎相等,使用CHAR定长字符串类型。
    1047. 【强制】VARCHAR是可变长字符串,不预先分配存储空间,长度不要超过50o0。
    1048. 如果存储长度大于此值,定义字段类型为TEXT,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
    1049. */
    1050. I
    1051. ################################################【约束】#################################################
    1052. #约束:constraint:列级约束,表级约束
    1053. CREATE DATABASE dbtest13;
    1054. USE dbtest13;
    1055. #【1】查看表中的约束
    1056. SELECT * FROM information_schema.table_constraints
    1057. WHERE TABLE_NAME='employees';
    1058. #【2】非空约束not null
    1059. #①默认,所有的类型的值都可以是NULL,包括INTFLOAT等数据类型
    1060. #②非空约束只能出现在表对象的列上,只能某个列单独限定非空,不能组合非空,也就是只能列级约束
    1061. #③空字符串不等于NULL0也不等于NULL
    1062. #2.1create table时添加约束
    1063. CREATE TABLE test1(
    1064. id INT NOT NULL,
    1065. last_name VARCHAR(15) NOT NULL,
    1066. email VARCHAR(25),
    1067. salary DECIMAL(10,2)
    1068. );
    1069. DESC test1;
    1070. #2.2alter table时添加约束
    1071. ALTER TABLE test1
    1072. MODIFY email VARCHAR(25) NOT NULL;
    1073. #2.3alter table时删除约束
    1074. ALTER TABLE test1
    1075. MODIFY email VARCHAR(25);
    1076. #【3unique唯一性约束
    1077. #可以向声明为unique的字段上添加null值,可以重复,即使unique
    1078. #3.1create table时添加
    1079. CREATE TABLE test2(
    1080. id INT UNIQUE,#列级约束,在创建唯一约束的时候,如果不给唯一约束命名,就默认和列名相同.
    1081. last_name VARCHAR(15),
    1082. email VARCHAR(25)UNIQUE,
    1083. salary DECIMAL(10,2),#注意此处有逗号
    1084. #表级约束
    1085. CONSTRAINT uk_test2_email UNIQUE(email)#起名字CONSTRAINT uk_test2_email
    1086. );
    1087. DESC test2;
    1088. SELECT * FROM information_schema.table_constraints
    1089. WHERE TABLE_NAME='test2';
    1090. #3.2alter table时添加
    1091. #方式一
    1092. ALTER TABLE test2 ADD CONSTRAINT uk_test2_sal UNIQUE(salary);#起名字:CONSTRAINT uk_test2_sal
    1093. #方式二
    1094. ALTER TABLE test2 MODIFY last_name VARCHAR(15) UNIQUE;
    1095. #3.3 复合的唯一性约束
    1096. CREATE TABLE USER(
    1097. id INT,
    1098. `name` VARCHAR(15),
    1099. `password` VARCHAR(25),
    1100. #表级约束
    1101. CONSTRAINT uk_user_name_pwd UNIQUE(`name`,`password`)#两个在一起建立的约束,两个不全一样就可以
    1102. );
    1103. DESC USER;
    1104. INSERT INTO USER
    1105. VALUES(1,'Tom','abc'),(1,'Tom1','abc');#可以通过
    1106. #【4】主键约束primary key
    1107. #①一个表中最多只能有一个主键约束。
    1108. #②主键约束特征:非空且唯一,用于唯一的标识表中的一条记录。
    1109. #③如果删除主键约束了,主键约束对应的索引就自动删除了。
    1110. #④MySQL的主键名总是PRIMARY,就算自己命名了主键约束名也没用。
    1111. #⑤如果是多列组合的复合主键约束,那么这些列都不允许为空值,并且组合的值不允许重复
    1112. #4.1create table时添加
    1113. CREATE TABLE test3(
    1114. id INT PRIMARY KEY,#列级约束
    1115. last_name VARCHAR(15),
    1116. email VARCHAR(25),
    1117. salary DECIMAL(10,2)
    1118. );
    1119. DESC test3;
    1120. CREATE TABLE test4(
    1121. id INT,
    1122. last_name VARCHAR(15),
    1123. email VARCHAR(25),
    1124. salary DECIMAL(10,2),
    1125. #表级约束
    1126. CONSTRAINT pk_test4_id PRIMARY KEY(id)#没有必要起名字
    1127. );
    1128. DESC test4;
    1129. SELECT * FROM information_schema.table_constraints
    1130. WHERE TABLE_NAME='test4';
    1131. CREATE TABLE USER1(
    1132. id INT,
    1133. `name` VARCHAR(15),
    1134. `password` VARCHAR(25),
    1135. PRIMARY KEY(`name`,`password`)#俩一起构成主键
    1136. );
    1137. #4.2alter table时添加
    1138. CREATE TABLE test6(
    1139. id INT,
    1140. last_name VARCHAR(15),
    1141. email VARCHAR(25),
    1142. salary DECIMAL(10,2)
    1143. );
    1144. DESC test6;
    1145. ALTER TABLE test6
    1146. ADD PRIMARY KEY(id);
    1147. #4.3 删除主键(在实际开发中,并不会删除的)
    1148. ALTER TABLE test6
    1149. DROP PRIMARY KEY;
    1150. #【5】自增列:auto_increment
    1151. #①一个表最多只能有一个自增长列I
    1152. #②当需要产生唯一标识符或顺序值时,可设置自增长
    1153. #③自增长列约束的列必须是键列(主键列,唯一键列)
    1154. #④自增约束的列的数据类型必须是整数类型
    1155. #⑥如果自增列指定了0null,会在当前最大值的基础上自增;如果自增列手动指定了具体值,直接赋值为具体值。
    1156. #5.1create table时候添加
    1157. CREATE TABLE test7(
    1158. id INT PRIMARY KEY AUTO_INCREMENT,
    1159. last_name VARCHAR(15)
    1160. );
    1161. SELECT * FROM test7;
    1162. INSERT INTO test7(last_name)
    1163. VALUES('zmj'),('lwq'),('zym'),('zyn');#自动递增
    1164. INSERT INTO test7(id,last_name)
    1165. VALUES(-10,'tom');#自动设置id也可以,负数也可以,且按照id从小到大排序了
    1166. #5.2alter table时候添加
    1167. CREATE TABLE test8(
    1168. id INT PRIMARY KEY,
    1169. last_name VARCHAR(15)
    1170. );
    1171. DESC test8;
    1172. ALTER TABLE test8
    1173. MODIFY id INT AUTO_INCREMENT;
    1174. #5.3alter table时候删除
    1175. ALTER TABLE test8
    1176. MODIFY id INT;
    1177. #【6】外键约束foreign key
    1178. #①主表(父表)︰被引用的表,被参考的表从表(子表):引用别人的表,参考别人的表
    1179. #关联:主键或者唯一性约束的
    1180. #②在创建外键约束时,如果不给外键约束命名,默认名不是列名,而是自动产生一个外键名
    1181. #③删表时,先删从表(或先删除外键约束),再删除主表
    1182. #④在“从表”中指定外键约束,并且一个表可以建立多个外键约束
    1183. #⑤当创建外键约束时,系统默认会在所在的列上建立对应的普通索引。但是索引名是列名,不是外键的约束名(根据外键查询效率很高)
    1184. #⑥删除外键约束后,必须手动删除对应的索引
    1185. #6.1 在CREATETABLE时添加
    1186. #先创建主表
    1187. CREATE TABLE dept1(
    1188. dept_id INT PRIMARY KEY,
    1189. dept_name VARCHAR(15)
    1190. );
    1191. DESC dept1;
    1192. #再创建从表
    1193. CREATE TABLE emp1(
    1194. emp_id INT PRIMARY KEY AUTO_INCREMENT,
    1195. emp_name VARCHAR(15),
    1196. department_id INT,
    1197. #表级约束
    1198. CONSTRAINT fk_emp1_dept_id FOREIGN KEY(department_id) REFERENCES dept1(dept_id)
    1199. );
    1200. DESC emp1;
    1201. SELECT * FROM information_schema.table_constraints WHERE TABLE_NAME='emp1';
    1202. #6.2alter table 添加外键约束
    1203. CREATE TABLE dept2(
    1204. dept_id INT PRIMARY KEY,
    1205. dept_name VARCHAR(15)
    1206. );
    1207. CREATE TABLE emp2(
    1208. emp_id INT PRIMARY KEY AUTO_INCREMENT,
    1209. emp_name VARCHAR(15),
    1210. department_id INT
    1211. );
    1212. ALTER TABLE emp2
    1213. ADD CONSTRAINT kf_emp2_dept2_id FOREIGN KEY(department_id)REFERENCES dept2(dept_id);
    1214. #6.3 约束等级
    1215. #Cascade方式:在父表上update/delete记录时,同步update/delete掉子表的匹配记录
    1216. #Set null方式∶在父表上update/delete记录时,将子表上匹配记录的列设为null,但是要注意子表的外键列不能为not null
    1217. #No action方式∶如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作
    1218. #Restrict方式:同no action,都是立即检查外键约束
    1219. #Set default方式(在可视化工具sQLyog中可能显示空白)︰父表有变更时,子表将外键列设置成一个默认的值,但Innodb不能识别
    1220. #如果没有指定等级,就相当于Restrict方式。
    1221. #【结论】对于外键约束,最好是采用: ON UPDATE CASCADE ON DELETE RESTRICT的方式。
    1222. #6.4 删除外键约束
    1223. ALTER TABLE emp2 DROP FOREIGN KEY kf_emp2_dept2_id;
    1224. #再手动删除外键约束对应的普通索引
    1225. SHOW INDEX FROM emp2;
    1226. ALTER TABLE emp2 DROP INDEX kf_emp2_dept2_id;#外键约束名
    1227. #6.5 开发场景
    1228. #①建和不建外键约束和查询没有关系。
    1229. #②建外键约束,你的操作(创建表、删除表、添加、修改、删除)会受到限制,从语法层面受到限制。
    1230. #③在MySQL里,外键约束是有成本的,需要消耗系统资源。对于大并发的sQL操作,有可能会不适
    1231. #【七、check约束】
    1232. #作用:检查某个字段的值是否符号xx要求,一般指的是值的范围
    1233. #7.1create table添加
    1234. CREATE TABLE test10(
    1235. id INT,
    1236. last_name VARCHAR(10),
    1237. salary DECIMAL(10,2) CHECK(salary>2000)
    1238. );
    1239. #【八、default约束】
    1240. #9.1create table时候添加
    1241. CREATE TABLE test11(
    1242. id INT,
    1243. last_name VARCHAR(19),
    1244. salary DECIMAL(10,2) DEFAULT 2000
    1245. );
    1246. #9.2alter table时添加约束
    1247. ALTER TABLE test11
    1248. MODIFY last_name VARCHAR(19) DEFAULT 'zmj';
    1249. DESC test11;
    1250. #9.3alter table时删除约束
    1251. ALTER TABLE test11
    1252. MODIFY last_name VARCHAR(19);
    1253. #【十、面试题】
    1254. #①面试1:为什么建表时,加not null default "或default 0? ==》不想让表中出现null值。
    1255. #②面试2:为什么不想要null的值?==》不好比较; 效率不高。
    1256. #③面试3:带AUTO_INCREMENT约束的字段值是从1开始的吗?==》在mysql时,是的。还可以指定。
    1257. #④面试4:并不是每个表都可以任意选择存储引擎? ==》外键约束不能跨引擎使用。
    1258. ###########################################【约束练习题一】################################################
    1259. #已经存在数据库test04_emp,两张表emp2和dept2
    1260. CREATE DATABASE test04_emp;
    1261. USE test04_emp;
    1262. CREATE TABLE emp2(
    1263. id INT,
    1264. emp_name VARCHAR(15)
    1265. );
    1266. CREATE TABLE dept2(
    1267. id INT,
    1268. dept_name VARCHAR(15)
    1269. );
    1270. #1.向表emp2的id列中添加PRIMARY KEY约束
    1271. ALTER TABLE emp2 MODIFY id INT PRIMARY KEY;
    1272. ALTER TABLE emp2 ADD PRIMARY KEY(id);
    1273. DESC emp2;
    1274. #2. 向表dept2的id列中添加PRIMARY KEY约束
    1275. ALTER TABLE dept2 ADD PRIMARY KEY(id);
    1276. DESC dept2;
    1277. #3. 向表emp2中添加列dept_id,并在其中定义FOREIGN KEY约束,与之相关联的列是dept2表中的id列。
    1278. ALTER TABLE emp2 ADD dept_id INT;
    1279. ALTER TABLE emp2 ADD CONSTRAINT fk_emp2_dep2_id FOREIGN KEY(dept_id) REFERENCES dept2(id);
    1280. ################################################【视图view】#################################################
    1281. #【1.常见的数据库对象】
    1282. #表,数据字典,约束,视图,索引,存储过程,存储函数,触发器
    1283. #【2.视图概述】
    1284. #[作用]可以帮我们使用表的一部分数据而不是所有的表;也可以针对不同的用户制定不同的查询视图。
    1285. #[优点]简化查询;减少数据冗余;控制数据的访问;使用灵活多变的需求;能够分解复杂的查询逻辑
    1286. #[缺点]如果视图过多,会导致数据库维护成本的问题;及时维护,维护成本大。
    1287. #①视图是一种虚拟表,本身是不具有数据的,占用很少的内存空间。
    1288. #②视图建立在已有表的基础上,视图赖以建立的这些表称为基表。
    1289. #③视图的创建和删除只影响视图本身,不影响对应的基表。
    1290. # 但是当对视图中的数据进行增加、删除和修改操作时,数据表中的数据会相应地发生变化,反之亦然。
    1291. #④视图,是向用户提供基表数据的另一种表现形式。通常情况下,小型项目的数据库可以不使用视图。
    1292. #【3.创建视图】
    1293. CREATE DATABASE dbtest14;
    1294. USE dbtest14;
    1295. CREATE TABLE emps AS SELECT * FROM atguigudb.employees;#数据复制过来了,但是约束没有
    1296. CREATE TABLE depts AS SELECT * FROM atguigudb.departments;
    1297. #3.1 创建单表视图
    1298. #情况一:视图中的字段在基表中有对应的字段
    1299. CREATE VIEW vu_emp1 AS SELECT employee_id,last_name,salary FROM emps;
    1300. SELECT * FROM vu_emp1;
    1301. CREATE VIEW vu_emp2
    1302. AS
    1303. SELECT employee_id 'emp_id',last_name 'l_name',salary 'sal'#查询语句中字段的别名会出现在视图的字段中
    1304. FROM emps
    1305. WHERE salary >8000;
    1306. SELECT * FROM vu_emp3;
    1307. CREATE VIEW vu_emp3(emp_id,`name`,sal)#一次匹配,字段名字
    1308. AS
    1309. SELECT employee_id,last_name,salary
    1310. FROM emps
    1311. WHERE salary >8000;
    1312. #情况二:视图中的字段在基表中可能设有对应的字段
    1313. CREATE VIEW vu_emp_sal(dep_id,avg_sal)#不在这里写,也可以
    1314. AS
    1315. SELECT department_id,AVG(salary) avg_sal
    1316. FROM emps
    1317. WHERE department_id IS NOT NULL
    1318. GROUP BY department_id;
    1319. SELECT * FROM vu_emp_sal;
    1320. #3.2 创建多表的视图
    1321. #其实没啥区别 多表查询而已
    1322. #利用视图对数据进行格式化
    1323. #其实也就是将查询数据固定下来,存为view而已
    1324. #3.3 基于视图创建视图
    1325. #其实和表一模一样啦
    1326. #【4.查看视图】==>和表一样
    1327. #语法一:查看数据库的表对象、视图对象
    1328. SHOW TABLES;#表和视图全部展示出来
    1329. #语法二:查看视图的结构
    1330. DESC vu_emp1;
    1331. DESCRIBE vu_emp1;#同上
    1332. DESCRIBE depts;
    1333. #语法三:查看视图的属性信息
    1334. SHOW TABLE STATUS LIKE 'vu_emp1';#命令行竖着显示,末尾加上\G
    1335. SHOW TABLE STATUS LIKE 'depts';
    1336. #语法四:查看视图的详细定义信息
    1337. SHOW CREATE VIEW vu_emp1;
    1338. #【5.更新视图数据】更新《==》增删改
    1339. #①更改视图,会同时更改原表
    1340. #②同理,更细表中数据,会导致视图中的数据修改
    1341. #③在定义视图的SELECT语句中使用了JOIN联合查询,视图将不支持更新操作。
    1342. #④虽然可以更新视图数居,但总的来说,视图作为虚拟表,主要用于方便查询,不建议更新视图的数据。
    1343. #⑤对视图数据的更改,都是通过对实际数据表里数据的操作来完成的。
    1344. SELECT * FROM vu_emp1;
    1345. SELECT employee_id,last_name,salary FROM emps;
    1346. UPDATE vu_emp1
    1347. SET salary =20000
    1348. WHERE employee_id=101;#更改视图,会同时更改原表
    1349. #【6.修改视图】
    1350. DESC vu_emp1;
    1351. #方式1
    1352. CREATE OR REPLACE VIEW vu_emp1#不存在就创建,存在就替换更新
    1353. AS
    1354. SELECT employee_id,last_name,salary,email
    1355. FROM emps
    1356. WHERE salary >7000;
    1357. #方式2
    1358. ALTER VIEW vu_emp1
    1359. AS
    1360. SELECT employee_id,last_name,salary,email
    1361. FROM emps;
    1362. #【7.删除视图】
    1363. DROP VIEW vu_emp3;
    1364. DROP VIEW IF EXISTS vu_emp3,vu_emp4;#可同时删除多个
    1365. SHOW TABLES;
    1366. ########################################【视图练习题一】##########################################
    1367. #1. 使用表employees创建视图employee_vu,其中包括姓名,员工号,部门号
    1368. CREATE VIEW emplouee_vu
    1369. AS
    1370. SELECT last_name,employee_id,department_id
    1371. FROM atguigudb.employees;
    1372. #2. 显示视图的结构
    1373. DESC emplouee_vu;
    1374. #3. 查询视图中的全部内容
    1375. SELECT * FROM emplouee_vu;
    1376. #4. 将视图中的数据限定在部门号是80的范围内
    1377. CREATE OR REPLACE VIEW employee_vu AS
    1378. SELECT last_name,employee_id,department_id FROM atguigudb.employees WHERE department_id=80;
    1379. ########################################【视图练习题二】##########################################
    1380. CREATE TABLE IF NOT EXISTS emps AS SELECT * FROM atguigudb.employees;
    1381. SELECT * FROM emps;
    1382. #1. 创建视图emp_v1,要求查询电话号码以‘011’开头的员工姓名和工资、邮箱
    1383. CREATE OR REPLACE VIEW emp_v1 AS
    1384. SELECT last_name,salary,email FROM emps WHERE phone_number LIKE '011%';
    1385. SELECT * FROM emp_v1;
    1386. #2. 要求将视图 emp_v1 修改为查询电话号码以‘011’开头的并且邮箱中包含 e 字符的员工姓名和邮箱、电话号码
    1387. CREATE OR REPLACE VIEW emp_v1 AS
    1388. SELECT last_name,email,phone_number FROM emps
    1389. WHERE phone_number LIKE '011%' AND email LIKE '%e%';
    1390. #3. 向 emp_v1 插入一条记录,是否可以?==》不可以
    1391. DESC emps;
    1392. DESC emp_v1;
    1393. INSERT INTO emp_v1(last_name,salary,email,phone_number)
    1394. VALUES('Tom',2300,'tom@126.com','1322321312');#错误
    1395. #4. 修改emp_v1中员工的工资,每人涨薪1000
    1396. UPDATE emp_v1
    1397. SET salary=salary+1000;
    1398. #5. 删除emp_v1中姓名为Olsen的员工
    1399. DELETE FROM emp_v1
    1400. WHERE last_name='Olsen'
    1401. #6. 创建视图emp_v2,要求查询部门的最高工资高于 12000 的部门id和其最高工资
    1402. CREATE OR REPLACE VIEW emp_v2 AS
    1403. SELECT department_id,MAX(salary) max_sal FROM emps GROUP BY department_id HAVING MAX(salary)>12000;
    1404. SELECT * FROM emp_v2;
    1405. #7. 向 emp_v2 中插入一条记录,是否可以?==》不可以
    1406. INSERT INTO emp_v2 VALUES(400,18000);
    1407. #8. 删除刚才的emp_v2 和 emp_v1
    1408. DROP VIEW IF EXISTS emp_v2,emp_v1;
    1409. ###########################################【存储过程】############################################
    1410. #【1.存储过程】procedure
    1411. #[定义]就是一组经过预先编译的SQL语句的封装。
    1412. #[优点]提高重用性;减少网络传输量;减少sql语句暴露在网络上的风险,提高安全性。
    1413. #[存储过程、视图对比]视图是虚拟表,一般不操控原表;存储过程是程序化的SQL可以直接操作底层数据表.
    1414. #[存储过程、函数对比]存储过程没有返回值,函数有返回值。
    1415. /*[分类]存储过程的参数类型是in,out,inout。参数类型可以是mysql中的任意类型。
    1416. 1.没有参数(无参数无返回)
    1417. 2.仅仅带in/out类型(有参数无返回/无参数有返回)
    1418. 3.既带in又带out(有参数有返回)
    1419. 4.带inout(有参数有返回)
    1420. 注意:IN、OUT、INOUT都可以在一个存储过程中带多个。
    1421. */
    1422. /*characteristics表示创建存储过程时指定的对存储过程的约束条件,
    1423. */
    1424. #【2.创建存储过程】procedure
    1425. DELIMITER $#定义结束符$,也可以其他符号,自定义delimiter
    1426. CREATE PROCEDURE select_all_date()
    1427. BEGIN
    1428. ...
    1429. END $
    1430. DELIMITER ;
    1431. CREATE DATABASE dbtest15;
    1432. USE dbtest15;
    1433. CREATE TABLE employees AS SELECT * FROM atguigudb.employees;
    1434. CREATE TABLE departments AS SELECT * FROM atguigudb.departments;
    1435. #类型一:无参数无返回值
    1436. #2.1 创建
    1437. DELIMITER $
    1438. CREATE PROCEDURE select_all_data()
    1439. BEGIN
    1440. SELECT *FROM employees;
    1441. END $
    1442. DELIMITER ;
    1443. #2.2 调用
    1444. CALL select_all_data();#存储过程的调用用call,函数是select
    1445. DELIMITER //
    1446. CREATE PROCEDURE avg_employee_salary()
    1447. BEGIN
    1448. SELECT AVG(salary) FROM employees;
    1449. END //
    1450. DELIMITER ;
    1451. CALL avg_employee_salary();
    1452. #类型二:out
    1453. DELIMITER $
    1454. CREATE PROCEDURE show_min_sal(OUT ms DOUBLE)#先写名字ms,再写类型double
    1455. BEGIN
    1456. SELECT MIN(salary) INTO ms FROM employees;
    1457. END $
    1458. DELIMITER ;
    1459. CALL show_min_sal(@ms);#变量ms
    1460. SELECT @ms;#查看变量值select即可
    1461. #类型三:in
    1462. DELIMITER $
    1463. CREATE PROCEDURE show_someone_salary(IN empname VARCHAR(20))
    1464. BEGIN
    1465. SELECT salary FROM employees WHERE last_name=empname;
    1466. END $
    1467. DELIMITER ;
    1468. #调用方式1:直接传入
    1469. CALL show_someone_salary('Abel');
    1470. #调用方式2:变量方式
    1471. SET @empname='Abel';#定义变量,'='或者':='均为赋值符号
    1472. CALL show_someone_salary(@empname);#传入
    1473. #类型四:in out
    1474. DELIMITER $
    1475. CREATE PROCEDURE show_someone_sal2(IN empname VARCHAR(20),OUT empsalary DECIMAL(10,2))
    1476. BEGIN
    1477. SELECT salary INTO empsalary
    1478. FROM employees
    1479. WHERE last_name=empname;
    1480. END $
    1481. DELIMITER ;
    1482. SET @empname='Abel';
    1483. CALL show_someone_sal2(@empname,@empsal);
    1484. SELECT @empsal;
    1485. #类型五:inout
    1486. DELIMITER $
    1487. CREATE PROCEDURE show_mgr_name(INOUT empname VARCHAR(25))#查询某个员工领导的姓名
    1488. BEGIN
    1489. SELECT last_name INTO empname
    1490. FROM employees
    1491. WHERE employee_id=(
    1492. SELECT manager_id
    1493. FROM employees
    1494. WHERE last_name=empname
    1495. );
    1496. END $
    1497. DELIMITER ;
    1498. SET @empname='Abel';
    1499. CALL show_mgr_name(@empname);
    1500. SELECT @empname;
    1501. #调试:逐步调试,很恶心
    1502. ########################################【存储函数】##############################################
    1503. #[语法格式]
    1504. DELIMITER $
    1505. CREATE FUNCTION 函数名(参数名 参数类型,...)
    1506. RETURNS 返回值类型
    1507. [characteristics...]
    1508. BEGIN
    1509. 函数体#函数体中肯定有return语句
    1510. END $
    1511. DELIMITER ;
    1512. #[参数列表]指定参数为IN、OUT或INOUT只对procedure是合法的,function中总是默认为IN参数
    1513. #[调用格式]
    1514. SELECT 函数名(实参列表);
    1515. #情况一:参数为空
    1516. DELIMITER $
    1517. CREATE FUNCTION email_by_name()
    1518. RETURNS VARCHAR(25)
    1519. DETERMINISTIC #解决报错的方式一
    1520. CONTAINS SQL
    1521. READS SQL DATA
    1522. BEGIN
    1523. RETURN(SELECT email FROM employees WHERE last_name='Abel');
    1524. END $
    1525. DELIMITER ;
    1526. #解决报错的方式二:创建函数前执行此语句,保证函数的创建会成功
    1527. SET GLOBAL log_bin_trust_function_creators=1;
    1528. #调用
    1529. SELECT email_by_name();
    1530. #情况二:传参
    1531. DELIMITER $
    1532. CREATE FUNCTION email_by_id(emp_id INT)
    1533. RETURNS VARCHAR(25)
    1534. BEGIN
    1535. RETURN(SELECT email FROM employees WHERE employee_id=emp_id);
    1536. END $
    1537. DELIMITER ;
    1538. SELECT email_by_id(101);
    1539. /*存储过程和函数对比
    1540. 存储过程:PROCEDURE CALL存储过程() 返回值理解为有0个或多个 一般用于更新
    1541. 存储函数:FUNCTION SELECT函数() 返回值只能是一个 一般用于查询结果为一个值并返回时
    1542. 存储函数可以放在查询语句中使用,存储过程不行。
    1543. 存储过程的功能更加强大,包括能够执行对表的操作(比如创建表,删除表等)和事务操作,
    1544. 这些功能是存储函数不具备的
    1545. */
    1546. #####################################【存储过程和函数的查看、修改、删除】####################################
    1547. #【查看】
    1548. #创建完之后,怎么知道我们创建的存储过程、存储函数是否成功了呢?
    1549. #方式一:查看存储过程和函数的创建信息
    1550. SHOW CREATE PROCEDURE show_mgr_name;
    1551. SHOW CREATE FUNCTION email_by_id;
    1552. #方式二:查看存储过程和函数的状态信息
    1553. SHOW PROCEDURE STATUS;
    1554. SHOW PROCEDURE STATUS LIKE 'show_mgr_name';
    1555. #方式三:从information_schema.Routines表中查看存储过程和函数的信息
    1556. SELECT * FROM information_schema.Routines
    1557. WHERE ROUTINE_NAME LIKE 'email_by_id'
    1558. AND ROUTINE_TYPE='FUNCTION';#这一行不写也可以
    1559. #【修改】
    1560. #修改存储过程或函数,不影响存储过程或函数功能,只是修改相关特性。使用ALTER语句实现。
    1561. ALTER PROCEDURE show_min_sal
    1562. SQL SECURITY INVOKER
    1563. COMMENT '查询最低工资';
    1564. #【删除】
    1565. DROP PROCEDURE IF EXISTS show_min_sal;
    1566. #【关于存储过程使用的争议】
    1567. /*[优点]
    1568. 存储过程可以一次编译多次使用。
    1569. 可以减少开发工作量。
    1570. 存储过程的安全性强。
    1571. 可以减少网络传输量。
    1572. 良好的封装性。
    1573. */
    1574. /*[缺点]
    1575. 可移植性差。
    1576. 调试困难。
    1577. 存储过程的版本管理很困难。
    1578. 不适合高并发的场景。
    1579. */
    1580. #########################################【存储过程、函数练习题】#############################################
    1581. #0.准备工作
    1582. CREATE DATABASE test15_pro_func;
    1583. USE test15_pro_func;
    1584. #1. 创建存储过程insert_user(),实现传入用户名和密码,插入到admin表中
    1585. CREATE TABLE IF NOT EXISTS ADMIN(
    1586. id INT PRIMARY KEY AUTO_INCREMENT,
    1587. user_name VARCHAR(15) NOT NULL,
    1588. pwd VARCHAR(25)NOT NULL
    1589. );
    1590. DELIMITER //
    1591. CREATE PROCEDURE insert_user(IN username VARCHAR(20),IN loginPwd VARCHAR(20))
    1592. BEGIN
    1593. INSERT INTO `admin`(user_name,pwd)
    1594. VALUES(username,loginpwd);
    1595. END //
    1596. DELIMITER ;
    1597. CALL insert_user('zmj','abc123');
    1598. SHOW TABLES;
    1599. SELECT * FROM `admin`;
    1600. DROP TABLE ADMIN;
    1601. #2. 创建存储过程get_phone(),实现传入女神编号,返回女神姓名和女神电话
    1602. CREATE TABLE beauty(
    1603. id INT PRIMARY KEY AUTO_INCREMENT,
    1604. NAME VARCHAR(15) NOT NULL,
    1605. phone VARCHAR(15) UNIQUE,
    1606. birth DATE
    1607. );
    1608. INSERT INTO beauty(NAME,phone,birth)
    1609. VALUES
    1610. ('朱茵','13201233453','1982-02-12'),
    1611. ('孙燕姿','13501233653','1980-12-09'),
    1612. ('田馥甄','13651238755','1983-08-21'),
    1613. ('邓紫棋','17843283452','1991-11-12'),
    1614. ('刘若英','18635575464','1989-05-18'),
    1615. ('杨超越','13761238755','1994-05-11');
    1616. SELECT * FROM beauty;
    1617. DELIMITER $
    1618. CREATE PROCEDURE get_phone(IN id_ INT,OUT name_ VARCHAR(15),OUT phone_ VARCHAR(15))
    1619. BEGIN
    1620. SELECT `name`,phone INTO name_,phone_
    1621. FROM beauty
    1622. WHERE id=id_;
    1623. END $
    1624. DELIMITER ;
    1625. #调用
    1626. CALL get_phone(2,@name_,@phone_);
    1627. SELECT @name_,@phone_;
    1628. #3. 创建存储过程date_diff(),实现传入两个女神生日,返回日期间隔大小
    1629. DELIMITER $
    1630. CREATE PROCEDURE date_diff(IN b1 DATE,IN b2 DATE,OUT diff_day INT)
    1631. BEGIN
    1632. SELECT DATEDIFF(b1,b2) INTO diff_day;
    1633. END $
    1634. DELIMITER ;
    1635. SET @b1='1999-05-26';
    1636. SET @b2='1999-06-26';
    1637. CALL date_diff(@b1,@b2,@diff_day);
    1638. SELECT @diff_day;
    1639. #4. 创建存储过程format_date(),实现传入一个日期,格式化成xx年xx月xx日并返回
    1640. DELIMITER $
    1641. CREATE PROCEDURE format_date(IN d DATE,OUT strdate VARCHAR(50))
    1642. BEGIN
    1643. SELECT DATE_FORMAT(d,'%y年%m月%d日')INTO strdate;
    1644. END $
    1645. DELIMITER ;
    1646. CALL format_date('20221024',@res);
    1647. SELECT @res;
    1648. #5. 创建存储过程beauty_limit(),根据传入的起始索引和条目数,查询女神表的记录
    1649. DELIMITER $
    1650. CREATE PROCEDURE beauty_limit(IN start_index INT,IN size INT)
    1651. BEGIN
    1652. SELECT * FROM beauty LIMIT start_index,size;
    1653. END $
    1654. DELIMITER ;
    1655. CALL beauty_limit(2,2);
    1656. #6. 传入a和b两个值,最终a和b都翻倍并返回
    1657. DELIMITER //
    1658. CREATE PROCEDURE add_double(INOUT a INT,INOUT b INT)
    1659. BEGIN
    1660. SET a=a+a;
    1661. SET b=b+b;
    1662. END //
    1663. DELIMITER ;
    1664. SET @a=3,@b=4;
    1665. CALL add_double(@a,@b);
    1666. SELECT @a,@b;
    1667. #存储函数练习
    1668. USE test15_pro_func;
    1669. CREATE TABLE employees AS SELECT * FROM atguigudb.`employees`;
    1670. CREATE TABLE departments AS SELECT * FROM atguigudb.`departments`;
    1671. #1. 创建函数get_count(),返回公司的员工个数
    1672. DELIMITER //
    1673. CREATE FUNCTION get_count()
    1674. RETURNS INT
    1675. BEGIN
    1676. RETURN(SELECT COUNT(*)FROM employees);
    1677. END //
    1678. DELIMITER ;
    1679. SELECT get_count();
    1680. #2. 创建函数ename_salary(),根据员工姓名,返回它的工资
    1681. DELIMITER //
    1682. CREATE FUNCTION ename_salary(n VARCHAR(25))
    1683. RETURNS DOUBLE
    1684. BEGIN
    1685. RETURN(SELECT salary FROM employees WHERE last_name=n);
    1686. END //
    1687. DELIMITER ;
    1688. SELECT ename_salary('De Haan');#不能返回多行的
    1689. DESC employees;
    1690. DESC departments;
    1691. #3. 创建函数dept_sal() ,根据部门名,返回该部门的平均工资
    1692. DELIMITER //
    1693. CREATE FUNCTION dept_sal(dept_name VARCHAR(30))
    1694. RETURNS DOUBLE
    1695. BEGIN
    1696. RETURN(SELECT AVG(salary)
    1697. FROM employees
    1698. GROUP BY department_id
    1699. HAVING department_id=(
    1700. SELECT department_id
    1701. FROM departments
    1702. WHERE department_name=dept_name
    1703. )
    1704. );
    1705. END //
    1706. DELIMITER ;
    1707. SELECT dept_sal('IT');
    1708. #4. 创建函数add_float(),实现传入两个float,返回二者之和
    1709. DELIMITER //
    1710. CREATE FUNCTION add_float(f1 FLOAT,f2 FLOAT)
    1711. RETURNS DOUBLE
    1712. BEGIN
    1713. RETURN(SELECT f1+f2);
    1714. END //
    1715. DELIMITER ;
    1716. SELECT add_float(2,3);
    1717. #########################################【变量、流程控制与游标】############################################
    1718. #【一、变量】
    1719. #系统变量(全局系统变量、会话系统变量),用户自定义变量
    1720. #1.1 系统变量
    1721. /*
    1722. ①变量由系统定义,不是用户定义,属于服务器层面。
    1723. ②系统变量分为:全局系统变量(需要添加global),会话系统变量(local变量)(需要添加session关键字)
    1724. ③全局系统变量针对于所有会话(连接)有效,但不能跨重启
    1725. ④会话系统变量仅针对于当前会话(连接)有效。
    1726. 会话期间,当前会话对某个会话系统变量值的修改不会影响其他会话同一个会话系统变量的值。
    1727. ⑤会话1对某个全局系统变量值的修改会导致会话2中同一个全局系统变量值的修改。
    1728. ⑥ 有些系统变量只能是全局的,例如max_connections 用于限制服务器的最大连接数;
    1729. 有些系统变量作用域既可以是全局又可以是会话,例如character_set_client用于设置客户端的字符集;
    1730. 有些系统变量的作用域只能是当前会话,例如pseudo_thread_id 用于标记当前会话的MySQL连接ID。
    1731. */
    1732. #1.1.1 查看系统变量
    1733. #【查看所有或部分系统变量】
    1734. #查看所有全局变量
    1735. SHOW GLOBAL VARIABLES;
    1736. #查看所有会话变量
    1737. SHOW SESSION VARIABLES;
    1738. SHOW VARIABLES;#默认的是会话系统变量
    1739. #查看满足条件的部分系统变量
    1740. SHOW GLOBAL VARIABLES LIKE 'admin_%';
    1741. #查看满足条件的部分会话变量
    1742. SHOW SESSION VARIABLES LIKE 'admin_%';
    1743. #【查看指定系统变量】
    1744. /*
    1745. MysQL中的系统变量以“@@”开头,其中“@@global”仅用于标记全局系统变
    1746. 量, @@session"仅用于标记会话系统变量。“@@"首先标记会话系统变量,如果
    1747. 会话系统变量不存在,则标记全局系统变量。
    1748. */
    1749. #查看指定的系统变量的值
    1750. SELECT @@global.admin_ssl_ca;
    1751. #查看指定的会话系统变量的值
    1752. #或者
    1753. SELECT @@session.character_set_client;
    1754. SELECT @@max_connections;#现在会话系统变量中找,再到全局中找
    1755. #【修改系统变量的值】
    1756. /*
    1757. 方式1:修改MySQL配置文件,继而修改MySQL系统变量的值(该方法需要重启MySQL服务) my.ini
    1758. 方式2:在MySQL服务运行期间,使用“set”命令重新设置系统变量的值
    1759. */
    1760. #[为某个系统变量赋值]
    1761. #全局系统变量:针对于当前的数据库实例是有效的,一旦重启mysql服务,就失效了。
    1762. #方式1
    1763. SET @@global.max_connections=161;
    1764. SELECT @@global.max_connections;#查看值
    1765. #方式2
    1766. SET GLOBAL max_connections=171;
    1767. #[为某个会话变量赋值]
    1768. #针对于当前会话是有效的,一旦结束会话,重新建立起新的会话,就失效了。
    1769. #方式1
    1770. SET @@session.character_set_client='gbk';
    1771. SELECT @@character_set_client;
    1772. SELECT @@global.character_set_client;#修改会话的,并不影响全局的
    1773. #方式2
    1774. SET SESSION character_set_client='gbk';
    1775. #【1.2 用户变量】
    1776. #用户变量:会话用户变量和局部变量
    1777. /* MySQL中的会话用户变量以一个“@”开头,局部变量都懒得用了。
    1778. 会话用户变量:作用域和会话变量一样,只对当前连接会话有效。
    1779. 局部变量:只在BEGIN和END语句块中有效。局部变量只能在存储过程和函数中使用。
    1780. */
    1781. #【会话用户变量】
    1782. USE dbtest15;
    1783. #变量的定义
    1784. #方式1:'='或者':='
    1785. SET @m1=1;
    1786. SET @m2:=2;
    1787. SET @sum:=@m1+@m2;
    1788. SELECT @sum;#查看变量值
    1789. #方式2
    1790. SELECT @count :=COUNT(*) FROM employees;#注意是':='
    1791. SELECT @count,@avg_sal;
    1792. SELECT AVG(salary) INTO @avg_sal FROM employees;
    1793. #【局部变量】
    1794. /*
    1795. ①声明和使用只能在begin end之间,即存储过程和函数之中。
    1796. ②使用declare声明。
    1797. ③declare的方式声明必须放在begin中首行的位置。
    1798. ④赋值还是set,select ... into这一类。
    1799. 声明格式:declare 变量名 类型 [default 值];#如果没有default,初始值为null
    1800. #注意:其他变量都没有类型,而这个局部变量声明时候有类型
    1801. */
    1802. DELIMITER //
    1803. CREATE PROCEDURE test_var()
    1804. BEGIN
    1805. #声明局部变量
    1806. DECLARE a INT DEFAULT 0;
    1807. DECLARE b INT;
    1808. #如果类型、默认值都一样的话也可以一起声明
    1809. DECLARE c,d INT DEFAULT 100;
    1810. DECLARE emp_name VARCHAR(25);
    1811. #赋值
    1812. SET a = 1;
    1813. SET b := 2;
    1814. SELECT last_name INTO emp_name FROM employees WHERE employee_id=101;
    1815. #使用
    1816. SELECT a,b,emp_name;
    1817. END //
    1818. DELIMITER ;
    1819. CALL test_var();
    1820. /*【会话用户变量VS局部变量】
    1821. 作用域 定义位置 语法
    1822. ————————————————————————————————————————————————————————————————————————————————————————
    1823. 会话用户变量: 当前会话 会话的任何地方 加@符号,不用指定类型
    1824. 局部变量: 定义它的BEGIN END中 BEGIN END的第一句话 一般不用加@,需要指定类型
    1825. */
    1826. #【二、定义条件与处理程序】
    1827. /*
    1828. 定义条件:事先定义程序执行道程中可能遇到的问题,
    1829. 处理程序:定义了在遇到问题时应当采取的处理方式,并且保证存储过程或函数在遇到警告或错误时能继续执行。
    1830. 这样可以增强存储程序处理问题的能力,避免程序异常停止运行。
    1831. 说明:定义条件和处理程序在存储过程、存储函数中都是支持的。
    1832. */
    1833. #【2.1 定义条件】
    1834. /*
    1835. 定义条件就是给MysQL中的错误码命名,这有助于存储的程序代码更清晰。
    1836. 它将一个错误名字和指定的错误条件关联起来。
    1837. 这个名字可以随后被用在定义处理程序的DECLARE HANDLER语句中。
    1838. 语法格式:
    1839. declare 错误名称 condition for 错误码(或错误条件);
    1840. 错误码说明:
    1841. MySQL_error_code和sqlstate_value都可以表示MySQL的错误。
    1842. MySQL_error_code:是数值类型错误代码。
    1843. sqlstate_value:是长度为5的字符串类型错误代码。
    1844. */
    1845. #举例1:定义"Field_Not_Be_NULL"错误名与MysQL中违反非空约束的错误类型
    1846. #是"ERROR 1048 (23000)”对应。
    1847. #方式一:使用MySQL_error_code
    1848. DECLARE Field_Not_Be_NULL CONDITION FOR 1048;
    1849. #方式二:使用qlstate_value
    1850. DECLARE Field_Not_Be_NULL2 CONDITION FOR SQLSTATE '23000';
    1851. #举例2:定义"ERROR 1148 (42000)"错误,名称为command_not_allowed。
    1852. DECLARE command_not_allowed CONDITION FOR 1148;
    1853. DECLARE command_not_allowed CONDITION FOR SQLSTATE '42000';
    1854. #【2.2 定义处理程序】
    1855. /*
    1856. 语法格式:declare 处理方式 handler for 错误类型 处理语句;
    1857. 处理方式:
    1858. CONTINUE:表示遇到错误不处理,继续执行。
    1859. EXIT:表示遇到错误马上退出。
    1860. UNDO:表示遇到错误后撤回之前的操作。MySsQL中暂时不支持这样的操作。
    1861. 错误类型(即条件)可以有如下取值:
    1862. SQLSTATE '字符串错误码':表示长度为5的sqlstate_value类型的错误代码;
    1863. MySQL_error_code :匹配数值类型错误代码;
    1864. 错误名称:表示DECLARE ... CONDITION定义的错误条件名称。
    1865. SQLWARNING:匹配所有以o1开头的SQLSTATE错误代码;
    1866. NOT FOUND:匹配所有以o2开头的SQLSTATE错误代码;
    1867. SQLEXCEPTION:匹配所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE错误代码;
    1868. 处理语句:
    1869. 如果出现上述条件之一,则采用对应的处理方式,并执行指定的处理语句。
    1870. 语句可以是像“ SET变量=值”这样的简单语句,也可以是使用BEGIN ... END编写的复合语句。
    1871. */
    1872. #举例
    1873. #1.捕获sqlstate_value
    1874. DECLARE CONTINUE HANDLER FOR SQLSTATE '42s02' SET @info='NO_SUCH_TABLE';
    1875. #2.捕获mysql_error_value
    1876. DECLARE CONTINUE HANDLER FOR 1146 SET @info='NO_SUCH_TABLE';
    1877. #3.先定义条件,再调用
    1878. DECLARE no_such_table CONTINUE FOR 1146;
    1879. DECLARE CONTINUE HANDLER FOR no_such_table SET @info='NO_SUCH_TABLE';
    1880. #4.使用SQLWARING
    1881. DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR';
    1882. #5.使用NOT FOUND
    1883. DECLARE EXIT HANDLER FOR NOT FOUND SET @info='NO_SUCH_TABLE';
    1884. #6.使用SQLEXCEPTION
    1885. DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info='ERROR';
    1886. #案例解决
    1887. /*
    1888. 在存储过程中,定义处理程序,捕获sqlstate_value值,当遇到MySQL_error_code值为1048时,
    1889. 执行CONTINUE操作,并且将@proc_value的值设置为-1。
    1890. */
    1891. DELIMITER //
    1892. CREATE PROCEDURE UpdateDataNoCondition()
    1893. BEGIN
    1894. #定义处理程序
    1895. DECLARE CONTINUE HANDLER FOR 1048 SET @proc_value=-1;
    1896. SET @x=1;
    1897. UPDATE employees SET email NULL WHERE last_name='Abel';
    1898. SET @x=2;
    1899. UPDATE employees SET email='aabbel' WHERE last_name='Abel';
    1900. SET @x=3;
    1901. END //
    1902. DELIMITER ;
    1903. #删除此存储过程
    1904. DROP PROCEDURE UpdateDataNoCondition;
    1905. #【三、流程控制】
    1906. /*是在存储过程或函数里面使用。
    1907. 条件判断语句:if语句和case语句
    1908. 循环语句:loop、while和repeat语句
    1909. 跳转语句:iterate和level语句
    1910. */
    1911. #【3.1 if语句】
    1912. /*
    1913. 语法结构:
    1914. if 表达式1 then 操作1
    1915. [elseif 表达式2 then 操作2]...
    1916. [else 操作N]
    1917. end if
    1918. */
    1919. DELIMITER //
    1920. CREATE PROCEDURE test_if()
    1921. BEGIN
    1922. #声明局部变量
    1923. DECLARE stu_name VARCHAR(15) DEFAULT 'aaa';
    1924. IF stu_name IS NULL
    1925. THEN SELECT 'stu_name is null';
    1926. ELSE
    1927. SELECT 'stu_name is not null';#注意:无then
    1928. END IF;
    1929. END //
    1930. DELIMITER ;
    1931. CALL test_if();
    1932. DROP PROCEDURE test_if;
    1933. #【3.2 case语句】
    1934. /*
    1935. 语法结构1:类似于switch
    1936. case 表达式
    1937. when 值1 then 结果1或语句1 #如果是语句,需要加分号
    1938. when 值2 then 结果2或语句2
    1939. else 结果n或语句n
    1940. end [case]; #如果放在begin end中需要加case,如果放在select后面不需要加
    1941. 语法结构2:类似于多次if
    1942. case
    1943. when 条件1 then 结果1或语句1 #如果是语句,需要加分号
    1944. when 条件2 then 结果2或语句2
    1945. else 结果n或语句n
    1946. end [case]; #如果放在begin end中需要加case,如果放在select后面不需要加
    1947. */
    1948. DELIMITER //
    1949. CREATE PROCEDURE test_case()
    1950. BEGIN
    1951. #声明局部变量
    1952. DECLARE var1 INT DEFAULT 12;
    1953. CASE
    1954. WHEN var1 >= 100 THEN SELECT '三位数';
    1955. WHEN var1 >= 10 THEN SELECT '两位数';
    1956. ELSE SELECT '一位数';
    1957. END CASE;
    1958. END //
    1959. DELIMITER ;
    1960. CALL test_case();
    1961. #【3.3 loop循环结构】
    1962. /*需要结合leave使用,跳出循环
    1963. 语法格式:
    1964. [loop_label:]loop
    1965. 循环执行的语句;
    1966. end loop [loop_label]
    1967. #loop_label表示LOOP语句的标注名称,该参数可以省略
    1968. */
    1969. DELIMITER //
    1970. CREATE PROCEDURE test_loop()
    1971. BEGIN
    1972. DECLARE num INT DEFAULT 1;
    1973. loop_label:LOOP
    1974. #重新赋值
    1975. SET num=num+1;
    1976. IF num>100 THEN LEAVE loop_label;
    1977. END IF;
    1978. END LOOP loop_label;
    1979. #查看num值
    1980. SELECT num 'num';
    1981. END //
    1982. DELIMITER ;
    1983. CALL test_loop();
    1984. DROP PROCEDURE test_loop;
    1985. #【3.4 while循环结构】
    1986. /*
    1987. 语法格式:
    1988. [while_label:]while 循环条件 do
    1989. 循环体
    1990. end while [while_label];
    1991. */
    1992. #【3.5 repeat循环语句】
    1993. /*
    1994. 与WHILE循环不同的是,REPEAT 循环首先会执行一次循环
    1995. 语法格式:
    1996. [repeat_label:]repeat
    1997. 循环体的语句
    1998. until 结束循环的条件表达式
    1999. end repeat [repeat_label];
    2000. REPEAT语句内的语句或语句群被重复,直至expr_condition为真。
    2001. */
    2002. #【3.6 leave语句】
    2003. /*相当于break
    2004. 可以用在循环语句内,或者以BEGIN和END包裹起来的程序体内,表示跳出循环或者跳出程序体的操作。
    2005. 基本格式:
    2006. leave 标记名; #离开标记名
    2007. */
    2008. #【3.7 iterate语句】
    2009. /*相当于continue
    2010. 只能用在循环语句(LOOP、REPEAT和WHILE语句)内,表示重新开始循环,将执行顺序转到语句段开头处。
    2011. 基本语法:
    2012. iterate 标记名;
    2013. */
    2014. #【四、游标或者光标】
    2015. /*相当于一个指针。
    2016. 游标,提供了一种灵活的操作方式,让我们能够对结果集中的每一条记录进行定位,
    2017. 并对指向的记录中的数据进行操作的数据结构。游标让SQL这种面向集合的语言有了面向过程开发的能力。
    2018. 在SQL中,游标是一种临时的数据库对象,可以指向存储在数据库表中的数据行指针。
    2019. 这里游标充当了指针的作用,我们可以通过操作游标来对数据行进行操作。
    2020. 游标必须在声明处理程序之前被声明,并且变量和条件还必须在声明游标或处理程序之前被声明。
    2021. 四步骤:声明游标,打开游标,使用游标,关闭游标
    2022. */
    2023. #【4.1 声明游标】
    2024. /*
    2025. 语法格式:
    2026. declare cursor_name cursor for select_statement;
    2027. 这个语法适用于MySQL,sQL Server,DB2和MariaDB。如果是用oracle或者PostgresQL,需要修改为:
    2028. declare cursor_name cursor is select_statemment;
    2029. 要使用SELECT语句来获取数据结果集,而此时还没有开始遍历数据,
    2030. 这里select_statement代表的是SELECT语句,返回一个用于创建游标的结果集。
    2031. */
    2032. #【4.2 打卡游标】
    2033. /*
    2034. 语法格式:
    2035. open cursor_name;
    2036. 当我们定义好游标之后,如果想要使用游标,必须先打开游标。
    2037. 打开游标的时候SELECT 语句的查询结果集就会送到游标工作区,
    2038. 为后面游标的逐条读取结果集中的记录做准备。
    2039. */
    2040. #【4.3 使用游标(从游标中获取数据)】
    2041. /*
    2042. 基本语法:
    2043. fetch cursor_name into var_name[,var_name]...
    2044. 这句的作用是使用cursor_name这个游标来读取当前行,并且将数据保存到var_name这个到下一行。
    2045. 如果游标读取的数据行有多个列名,则在INTO关键字后面赋值给多个变量名即可。
    2046. 注意: var_name必须在声明游标之前就定义好。
    2047. */
    2048. #【4.4 关闭游标】
    2049. /*
    2050. 语法格式:
    2051. close cursor_name;
    2052. */
    2053. #举例:创建存储过程"get_count_by_limit_total_salary()",声明IN参数limit_total_salary,
    2054. #DOUBLE类型;声明ouT参数total_count,INT类型。函数的功能可以实现累加薪资最高的几个员工的薪资值,
    2055. #直到薪资总和达到limit_total_salary参数的值,返回累加的人数给total_count。
    2056. DELIMITER //
    2057. CREATE PROCEDURE get_count_by_limit_total_salary(IN limit_total_salary DOUBLE,OUT total_count INT)
    2058. BEGIN
    2059. #声明局部变量
    2060. DECLARE sum_sal DOUBLE DEFAULT 0.0;#记录累加的工资总额
    2061. DECLARE emp_sal DOUBLE;#记录每一个员工的工资
    2062. DECLARE emp_count INT DEFAULT 0;#记录累加的人数
    2063. #声明游标
    2064. DECLARE emp_cursor CURSOR FOR SELECT salary FROM employees ORDER BY salary DESC;
    2065. #打开游标
    2066. OPEN emp_cursor;
    2067. REPEAT
    2068. #使用游标
    2069. FETCH emp_cursor INTO emp_sal;
    2070. SET sum_sal=sum_sal+emp_sal;
    2071. SET emp_count=emp_count+1;
    2072. UNTIL sum_sal>=limit_total_salary
    2073. END REPEAT;
    2074. SET total_count=emp_count;
    2075. #关闭游标
    2076. CLOSE emp_cursor;
    2077. END //
    2078. DELIMITER ;
    2079. CALL get_count_by_limit_total_salary(200000,@total_count);
    2080. SELECT @total_count;
    2081. #【补充:全局变量的持久化】
    2082. /*
    2083. set global 变量名=值; #在数据库重启时候,失效。
    2084. 语法格式:
    2085. set persist 变量名=值;
    2086. MySQL会将该命令的配置保存到数据目录下的mysqld-auto.cnf文件中,
    2087. 下次启动时会读取该文件,用其中的配置来覆盖默认的配置文件。
    2088. */
    2089. #####################################【变量练习题】############################################
    2090. USE dbtest15;
    2091. #1. 创建函数get_count(),返回公司的员工个数
    2092. DELIMITER //
    2093. CREATE FUNCTION get_count() RETURNS INT
    2094. BEGIN
    2095. DECLARE c INT DEFAULT 0;#定义局部变量
    2096. SELECT COUNT(*) INTO c#赋值
    2097. FROM employees;
    2098. RETURN c;
    2099. END //
    2100. DELIMITER ;
    2101. #调用
    2102. SELECT get_count();
    2103. SELECT * FROM employees;
    2104. #2. 创建函数ename_salary(),根据员工姓名,返回它的工资
    2105. DELIMITER //
    2106. CREATE FUNCTION ename_salary(emp_name VARCHAR(15))RETURNS DOUBLE
    2107. BEGIN
    2108. SET @sal=0;#定义用户变量
    2109. SELECT salary INTO @sal FROM employees WHERE last_name=emp_name;
    2110. RETURN @sal;
    2111. END //
    2112. DELIMITER ;
    2113. SELECT ename_salary('Abel');
    2114. #3. 创建函数dept_sal() ,根据部门名,返回该部门的平均工资
    2115. DELIMITER //
    2116. CREATE FUNCTION dept_sal(dept_name VARCHAR(15))RETURNS DOUBLE
    2117. BEGIN
    2118. DECLARE avg_sal DOUBLE DEFAULT 0.0;
    2119. SELECT AVG(salary) INTO avg_sal
    2120. FROM employees e
    2121. JOIN departments d ON e.`department_id`=d.`department_id`
    2122. WHERE d.department_name=dept_name;
    2123. RETURN avg_sal;
    2124. END //
    2125. DELIMITER ;
    2126. SELECT dept_sal('Marketing');
    2127. #4. 创建函数add_float(),实现传入两个float,返回二者之和
    2128. DELIMITER //
    2129. CREATE FUNCTION add_float(f1 FLOAT,f2 FLOAT)RETURNS FLOAT
    2130. BEGIN
    2131. SET @two_sum=f1+f2;
    2132. RETURN @tuo_sum;
    2133. END //
    2134. DELIMITER;
    2135. SELECT add_float(1.1,2.2);
    2136. #####################################【流程控制练习题】############################################
    2137. #1. 创建函数test_if_case(),实现传入成绩,如果成绩>90,返回A,如果成绩>80,返回B,如果成绩>60,返回C,否则返回D
    2138. #要求:分别使用if结构和case结构实现
    2139. DELIMITER //
    2140. CREATE FUNCTION test_if_case(sc INT)RETURNS CHAR
    2141. BEGIN
    2142. DECLARE ch CHAR;
    2143. CASE
    2144. WHEN sc>90 THEN SET ch='A';
    2145. WHEN sc>80 THEN SET ch='B';
    2146. WHEN sc>60 THEN SET ch='C';
    2147. ELSE SET ch='D';
    2148. END CASE;
    2149. RETURN ch;
    2150. END //
    2151. DELIMITER ;
    2152. SELECT test_if_case(76);
    2153. #方式2
    2154. DELIMITER //
    2155. CREATE FUNCTION test_if_case2(score DOUBLE)
    2156. RETURNS CHAR
    2157. BEGIN
    2158. DECLARE ch CHAR;
    2159. CASE
    2160. WHEN score>90 THEN SET ch='A';
    2161. WHEN score>80 THEN SET ch='B';
    2162. WHEN score>60 THEN SET ch='C';
    2163. ELSE SET ch='D';
    2164. END CASE;
    2165. RETURN ch;
    2166. END //
    2167. DELIMITER ;
    2168. #调用
    2169. SELECT test_if_case2(67);
    2170. #2. 创建存储过程test_if_pro(),传入工资值,如果工资值<3000,则删除工资为此值的员工,
    2171. #如果3000 <= 工资值 <= 5000,则修改此工资值的员工薪资涨1000,否则涨工资500
    2172. DELIMITER //
    2173. CREATE PROCEDURE test_if_pro(IN sal DOUBLE)
    2174. BEGIN
    2175. IF sal<3000 THEN
    2176. DELETE FROM employees WHERE salary=sal;
    2177. ELSEIF sal<=5000 THEN
    2178. UPDATE employees SET salary=salary+1000;
    2179. ELSE
    2180. UPDATE employees SET salary=salary+500;
    2181. END IF;
    2182. END //
    2183. DELIMITER ;
    2184. CALL test_if_pro(3500);
    2185. #3. 创建存储过程insert_data(),传入参数为 ININT 类型变量 insert_count,
    2186. #实现向admin表中批量插入insert_count条记录。
    2187. DELIMITER //
    2188. CREATE PROCEDURE insert_data(IN insert_count INT)
    2189. BEGIN
    2190. DECLARE i INT DEFAULT 1;
    2191. WHILE i<=insert_count DO
    2192. INSERT INTO ADMIN(user_name,user_pwd)VALUE(CONCAT('Rose-',i),ROUND(RAND()*100000));
    2193. SET i=i+1;
    2194. END WHILE;
    2195. END //
    2196. DELIMITER ;
    2197. CALL insert_data(10);
    2198. #####################################【游标的使用练习题】############################################
    2199. /*
    2200. 创建存储过程update_salary(),参数1为 IN 的INT型变量dept_id,表示部门id;参数2为 IN的INT型
    2201. 变量change_sal_count,表示要调整薪资的员工个数。查询指定id部门的员工信息,按照salary升序排列
    2202. ,根据hire_date的情况,调整前change_sal_count个员工的薪资,详情如下。
    2203. */
    2204. DELIMITER //
    2205. CREATE PROCEDURE update_salary(IN dept_id INT,IN change_sal_count INT)
    2206. BEGIN
    2207. DECLARE int_count INT DEFAULT 0;
    2208. DECLARE salary_rate DOUBLE DEFAULT 0.0;
    2209. DECLARE emp_id INT;
    2210. DECLARE emp_hire_date DATE;
    2211. DECLARE emp_cursor CURSOR FOR SELECT employee_id,hire_date FROM employees
    2212. WHERE department_id = dept_id ORDER BY salary;
    2213. OPEN emp_cursor;
    2214. WHILE int_count<change_sal_count DO
    2215. FETCH emp_cursor INTO emp_id,emp_hire_date;
    2216. IF (YEAR(emp_hire_date)<1995) THEN
    2217. SET salary_rate=1.2;
    2218. ELSEIF(YEAR(emp_hire_date)<1998)THEN
    2219. SET salary_rate = 1.15;
    2220. ELSEIF(YEAR(emp_hire_date) <= 2001)THEN
    2221. SET salary_rate = 1.10;
    2222. ELSE SET salary_rate = 1.05;
    2223. END IF;
    2224. #更新工资
    2225. UPDATE employees SET salary = salary * salary_rate
    2226. WHERE employee_id = emp_id;
    2227. SET int_count=int_count+1;
    2228. END WHILE;
    2229. CLOSE emp_cursor;
    2230. END //
    2231. DELIMITER ;
    2232. CALL update_salary(50,2);
    2233. #######################################【触发器】#####################################################
    2234. /*
    2235. 【触发器概述】
    2236. ①MySQL的触发器和存储过程一样,都是嵌入到MySQL服务器的一段程序。
    2237. ②触发器是由事件来触发某个操作,这些事件包括INSERT、UPDATE、DELETE事件。
    2238. ③所谓事件就是指用户的动作或者触发某项行为。如果定义了触发程序,当数据库
    2239. 执行这些语句时候,就相当于事件发生了,就会自动激发触发器执行相应的操作。
    2240. ④当对数据表中的数据执行插入、更新和删除操作,需要自动执行一些数据库逻辑时,
    2241. 可以使用触发器来实现。
    2242. 【创建触发器】
    2243. 语法结构:
    2244. create trigger 触发器名称
    2245. {before|after} {insert|update|delete} on 表名
    2246. for each row
    2247. 触发器执行的语句块;
    2248. ①表名:表示触发器监控的对象。
    2249. ②BEFORE | AFTER:表示触发的时间。BEFORE表示在事件之前触发;AFTER表示在事件之后触发。
    2250. ③INSERT | UPDATE DELETE:表示触发的事件。
    2251. INSERT 表示插入记录时触发;
    2252. UPDATE 表示更新记录时触发;
    2253. DELETE 表示删除记录时触发。
    2254. ④触发器执行的语句块:可以是单条sQL语句,也可以是由BEGIN...END结构组成的复合语句块。
    2255. 【查看触发器】查看数据库中已经存在的触发器的定义、状态和语法信息等。
    2256. 1.查看当前数据库的所有触发器定义
    2257. show triggers;
    2258. 2.查看当前数据库中某一个触发器的定义
    2259. show create trigger 触发器名
    2260. 3.从系统库information_schema的triggers表中查询“salary_check_trigger”触发器的信息
    2261. select * from information_schema.TRIGGERS;
    2262. 【删除触发器】
    2263. 语法格式:
    2264. drop trigger if exists 触发器名称;
    2265. 【优点】
    2266. ①确保数据的完整性。
    2267. ②触发器帮助我们记录操作日志。
    2268. ③触发器还可以用在操作数据前,对数据进行合法性检查
    2269. 【缺点】
    2270. ①可读性差。
    2271. ②相关数据的变更,可能会导致触发器出错。
    2272. */
    2273. #举例1
    2274. USE dbtest15;
    2275. #创建数据表
    2276. CREATE TABLE test_trigger(
    2277. id INT PRIMARY KEY AUTO_INCREMENT,
    2278. t_note VARCHAR(30)
    2279. );
    2280. CREATE TABLE test_trigger_log(
    2281. id INT PRIMARY KEY AUTO_INCREMENT,
    2282. t_log VARCHAR(30)
    2283. );
    2284. #查看表数据
    2285. SELECT * FROM test_trigger;
    2286. SELECT * FROM test_trigger_log;
    2287. #创建触发器
    2288. /*创建名称为before_insert_test_tri的触发器,向test_trigger数据表插入数据之前,
    2289. 向test_trigger_log数据表中插入before_insert的日志信息。*/
    2290. DELIMITER //
    2291. CREATE TRIGGER before_insert_test_tri
    2292. BEFORE INSERT ON test_trigger
    2293. FOR EACH ROW
    2294. BEGIN
    2295. INSERT INTO test_trigger_log(t_log)
    2296. VALUES('before insert...');
    2297. END //
    2298. DELIMITER ;
    2299. #测试
    2300. INSERT INTO test_trigger(t_note) VALUES ('Tom...');
    2301. SELECT * FROM test_trigger;
    2302. SELECT * FROM test_trigger_log;
    2303. #举例2
    2304. /*定义触发器"salary_check_trigger",基于员工表"employees”的INSERT事件,
    2305. 在INSERT之前检查将要添加的新员工薪资是否大于他领导的薪资,如果大于领
    2306. 导薪资,则报sqlstate value为'HY000'的错误,从而使得添加失败。*/
    2307. DELIMITER //
    2308. CREATE TRIGGER salary_check_trigger
    2309. BEFORE INSERT ON employees
    2310. FOR EACH ROW
    2311. BEGIN
    2312. #查询要添加员工的老板的工资
    2313. DECLARE mgr_sal DOUBLE;
    2314. SELECT salary INTO mgr_sal FROM employees
    2315. WHERE employee_id=new.manager_id;#要添加的记录用new表示,要删除的用old表示
    2316. IF new.salary>mgr_sal THEN
    2317. SIGNAL SQLSTATE 'HY000' SET MASSAGE_TEXT='薪资高于领导薪资错误';
    2318. END IF;
    2319. END //
    2320. DELIMITER ;
    2321. #####################################【触发器练习题】#############################################
    2322. #0. 准备工作
    2323. CREATE TABLE emps
    2324. AS
    2325. SELECT employee_id,last_name,salary
    2326. FROM atguigudb.`employees`;
    2327. #1. 复制一张emps表的空表emps_back,只有表结构,不包含任何数据
    2328. CREATE TABLE emps_back
    2329. AS
    2330. SELECT * FROM emps
    2331. LIMIT 0,0;
    2332. #2. 查询emps_back表中的数据
    2333. SELECT * FROM emps_back;
    2334. #3. 创建触发器emps_insert_trigger,每当向emps表中添加一条记录时,
    2335. #同步将这条记录添加到emps_back表中
    2336. DELIMITER //
    2337. CREATE TRIGGER emps_insert_trigger
    2338. AFTER INSERT ON emps
    2339. FOR EACH ROW
    2340. BEGIN
    2341. INSERT INTO emps_back(employee_id,last_name,salary)
    2342. VALUES(NEW.employee_id,NEW.last_name,NEW.salary);
    2343. END //
    2344. DELIMITER ;
    2345. #4. 验证触发器是否起作用
    2346. INSERT INTO emps(employee_id,last_name,salary)
    2347. VALUES(901,'zmj',9000);
    2348. SELECT * FROM emps_back;
    2349. ##################################【窗口函数】############################################
    2350. /*
    2351. 需要用到分组统计的结果对每一条记录进行计算的场景下,使用窗口函数更好。
    2352. 【作用】
    2353. 类似于在查询中对数据进行分组,不同的是,分组操作会把分组的结果聚合成一条记录,
    2354. 而窗口函数是将结果置于每一条数据记录中。
    2355. 【分类】静态窗口函数和动态窗口函数
    2356. 静态窗口函数的窗口大小是固定的,不会因为记录的不同而不同;
    2357. 动态窗口函数的窗口大小会随着记录的不同而变化。
    2358. 【语法结构】
    2359. 函数 over([partition by 字段名 order by 字段名 asc|desc]);
    2360. 函数 over 窗口名 ... window 窗口名 as ([partition by 字段名 order by 字段名 asc|desc]);
    2361. */
    2362. #举例1
    2363. CREATE TABLE sales(
    2364. id INT PRIMARY KEY AUTO_INCREMENT,
    2365. city VARCHAR(15),
    2366. county VARCHAR(15),
    2367. sales_value DECIMAL
    2368. );
    2369. INSERT INTO sales(city,county,sales_value)
    2370. VALUES
    2371. ('北京','海淀',10.00),
    2372. ('北京','朝阳',20.00),
    2373. ('上海','黄埔',30.00),
    2374. ('上海','长宁',10.00);
    2375. SELECT * FROM sales;
    2376. /*
    2377. 需求:现在计算这个网站在每个城市的销售总额、在全国的销售总额、每个区的销售额占
    2378. 所在城市销售额中的比率,以及占总销售额中的比率。
    2379. */
    2380. SELECT city '城市',county '区',sales_value '区销售额',
    2381. SUM(sales_value) OVER(PARTITION BY city) '市销售额',
    2382. sales_value/SUM(sales_value) OVER (PARTITION BY city) '市比率',
    2383. SUM(sales_value) OVER() '总销售额',
    2384. sales_value/SUM(sales_value) OVER() '总比率'
    2385. FROM sales
    2386. GROUP BY city,county;
    2387. #举例2
    2388. CREATE TABLE goods(
    2389. id INT PRIMARY KEY AUTO_INCREMENT,
    2390. category_id INT,
    2391. category VARCHAR(15),
    2392. NAME VARCHAR(30),
    2393. price DECIMAL(10,2),
    2394. stock INT,
    2395. upper_time DATETIME
    2396. );
    2397. INSERT INTO goods(category_id,category,NAME,price,stock,upper_time)
    2398. VALUES
    2399. (1, '女装/女士精品', 'T恤', 39.90, 1000, '2020-11-10 00:00:00'),
    2400. (1, '女装/女士精品', '连衣裙', 79.90, 2500, '2020-11-10 00:00:00'),
    2401. (1, '女装/女士精品', '卫衣', 89.90, 1500, '2020-11-10 00:00:00'),
    2402. (1, '女装/女士精品', '牛仔裤', 89.90, 3500, '2020-11-10 00:00:00'),
    2403. (1, '女装/女士精品', '百褶裙', 29.90, 500, '2020-11-10 00:00:00'),
    2404. (1, '女装/女士精品', '呢绒外套', 399.90, 1200, '2020-11-10 00:00:00'),
    2405. (2, '户外运动', '自行车', 399.90, 1000, '2020-11-10 00:00:00'),
    2406. (2, '户外运动', '山地自行车', 1399.90, 2500, '2020-11-10 00:00:00'),
    2407. (2, '户外运动', '登山杖', 59.90, 1500, '2020-11-10 00:00:00'),
    2408. (2, '户外运动', '骑行装备', 399.90, 3500, '2020-11-10 00:00:00'),
    2409. (2, '户外运动', '运动外套', 799.90, 500, '2020-11-10 00:00:00'),
    2410. (2, '户外运动', '滑板', 499.90, 1200, '2020-11-10 00:00:00');
    2411. SELECT * FROM goods;
    2412. #1.序号函数
    2413. #1.1 row_number()函数:能够对数据中的序号进行顺序显示
    2414. /*举例:查询goods数据表中每个商品分类下价格降序排列的各个商品信息。*/
    2415. SELECT ROW_NUMBER()OVER(PARTITION BY category_id ORDER BY price DESC)'row_num',
    2416. id,category_id,category,`name`,price,stock
    2417. FROM goods;
    2418. /*举例:查询 goods 数据表中每个商品分类下价格最高的3种商品信息。*/
    2419. SELECT *
    2420. FROM (SELECT ROW_NUMBER() OVER(PARTITION BY category_id ORDER BY price DESC)'row_num',
    2421. id,category_id,category,`name`,price,stock
    2422. FROM goods
    2423. )t
    2424. WHERE row_num<=3;
    2425. #1.2 rank()函数
    2426. /*rank():能够对序号进行并列排序,并且会跳过重复的序号,比如序号为1、1、3。*/
    2427. #举例:使用RANK()函数获取 goods 数据表中各类别的价格从高到低排序的各商品信息
    2428. SELECT RANK()OVER(PARTITION BY category_id ORDER BY price DESC)'row_num',
    2429. id,category_id,category,`name`,price,stock
    2430. FROM goods;
    2431. #举例:使用RANK()函数获取 goods 数据表中类别为“女装/女士精品”的价格最高的4款商品信息。
    2432. SELECT *
    2433. FROM (SELECT RANK() OVER(PARTITION BY category_id ORDER BY price DESC)'row_num',
    2434. id,category_id,category,`name`,price,stock
    2435. FROM goods
    2436. )t
    2437. WHERE category='女装/女士精品' AND row_num<=4;
    2438. #1.3 dense_rank()函数
    2439. /*对序号进行并列排序,并且不会跳过重复的序号,比如序号为1、1、2。*/
    2440. #举例:使用DENSE_RANK()函数获取 goods 数据表中各类别的价格从高到低排序的各商品信息。
    2441. SELECT DENSE_RANK()OVER(PARTITION BY category_id ORDER BY price DESC)'row_num',
    2442. id,category_id,category,`name`,price,stock
    2443. FROM goods;
    2444. #2.分布函数
    2445. #2.1 percent_rank()函数
    2446. /*求比例的。
    2447. PERCENT_RANK()函数是等级值百分比函数。按照如下方式进行计算。(rank - 1) / (rows - 1)
    2448. 其中,rank的值为使用RANK()函数产生的序号,rows的值为当前窗口的总记录数。
    2449. */
    2450. #举例:计算 goods 数据表中名称为“女装/女士精品”的类别下的商品的PERCENT_RANK值。
    2451. #方式1
    2452. SELECT RANK()OVER w AS r,
    2453. PERCENT_RANK()OVER w AS pr,
    2454. id,category_id,category,`name`,price,stock
    2455. FROM goods
    2456. WHERE category_id=1
    2457. WINDOW w AS (PARTITION BY category_id ORDER BY price DESC);
    2458. #方式2
    2459. SELECT RANK()OVER(PARTITION BY category_id ORDER BY price DESC) AS r,
    2460. PERCENT_RANK()OVER(PARTITION BY category_id ORDER BY price DESC) AS pr,
    2461. id,category_id,category,`name`,price,stock
    2462. FROM goods
    2463. WHERE category_id=1;
    2464. #2.2 cume_dist()函数
    2465. /*主要用于查询小于或等于某个值的比例。*/
    2466. #举例:查询goods数据表中小于或等于当前价格的比例。
    2467. SELECT CUME_DIST()OVER(PARTITION BY category_id ORDER BY price ASC)AS cd,
    2468. id,category,`name`,price
    2469. FROM goods;
    2470. #3. 前后函数
    2471. #3.1 lag('属性名',n)函数
    2472. /*返回当前行的前n行的'属性名'的值。*/
    2473. #举例:查询goods数据表中前一个商品价格与当前商品价格的差值。
    2474. SELECT id,category, NAME, price, pre_price, price - pre_price AS diff_price
    2475. FROM(SELECT id, category, NAME, price,LAG(price,1) OVER w AS pre_price
    2476. FROM goods
    2477. WINDOW w AS (PARTITION BY category_id ORDER BY price)
    2478. )t;
    2479. #3.2 lead(expr,n)函数
    2480. /*返回当前行的后n行的expr的值。*/
    2481. #举例:查询goods数据表中后一个商品价格与当前商品价格的差值
    2482. SELECT id,category, NAME, price, pre_price, price - pre_price AS diff_price
    2483. FROM(SELECT id, category, NAME, price,LEAD(price,1) OVER w AS pre_price
    2484. FROM goods
    2485. WINDOW w AS (PARTITION BY category_id ORDER BY price)
    2486. )t;
    2487. #4.首尾函数
    2488. #4.1 first_value(expr)函数
    2489. /*返回第一个expr的值*/
    2490. #举例:按照价格排序,查询第1个商品的价格信息。
    2491. SELECT id, category, NAME, price, stock,FIRST_VALUE(price)OVER w AS first_price
    2492. FROM goods
    2493. WINDOW w AS (PARTITION BY category_id ORDER BY price);
    2494. #4.2 last_value(expr)函数
    2495. /*返回最后一个expr的值*/
    2496. #举例:按照价格排序,查询最后一个商品的价格信息。
    2497. SELECT id, category, NAME, price, stock,LAST_VALUE(price)OVER w AS first_price
    2498. FROM goods
    2499. WINDOW w AS (PARTITION BY category_id ORDER BY price);
    2500. #5.其他函数
    2501. #5.1 nth_value(expr,n)函数
    2502. /*返回第n个expr的值。*/
    2503. #举例:查询goods数据表中排名第2和第3的价格信息
    2504. SELECT id, category, NAME, price,NTH_VALUE(price,2)OVER w AS second_price,
    2505. NTH_VALUE(price,3)OVER w AS third_price
    2506. FROM goods
    2507. WINDOW w AS (PARTITION BY category_id ORDER BY price);
    2508. #5.2 ntile(n)函数
    2509. /*将分区中的有序数据分为n个桶,记录桶编号。*/
    2510. #举例:将goods表中的商品按照价格分为3组。
    2511. SELECT NTILE(3) OVER w AS nt,id,category, NAME, price
    2512. FROM goods
    2513. WINDOW w AS (PARTITION BY category_id ORDER BY price);
    2514. #【总结】
    2515. /*窗口函数的特点是可以分组,而且可以在分组内排序。另外,窗口函数不会因为分组而减少
    2516. 原表中的行数,这对我们在原表数据的基础上进行统计和排序非常有用。*/
    2517. ##################################【公用表表达式】##########################################
    2518. /*
    2519. 公用表表达式(或通用表表达式)简称为CTE(Common Table Expressions)。
    2520. CTE是一个命名的临时结果集,作用范围是当前语句。CTE可以理解成一个可以复用的子查询,
    2521. 当然跟子查询还是有点区别的,CTE可以引用其他CTE,但子查询不能引用其他子查询。所以,可
    2522. 以考虑代替子查询。
    2523. 【分类】
    2524. 普通公用表表达式 和 递归公用表表达式。
    2525. */
    2526. #【1.普通公用表表达式】
    2527. /*
    2528. 【语法结构】
    2529. with CTE名称
    2530. as (子查询)
    2531. select|delete|update 语句;
    2532. 普通公用表表达式类似于子查询,不过,跟子查询不同的是,它可以被多次引用,
    2533. 而且可以被其他的普通公用表表达式所引用。
    2534. */
    2535. #举例:查询员工所在的部门的详细信息。
    2536. #方法1:子查询实现
    2537. SELECT * FROM departments
    2538. WHERE department_id IN (
    2539. SELECT DISTINCT department_id
    2540. FROM employees
    2541. );
    2542. #方法2:CTE实现
    2543. WITH cte_emp#相当于临时一个结果集,当成表看待即可
    2544. AS (SELECT DISTINCT department_id FROM employees)#注意此处没有结束,不要写分号
    2545. SELECT *
    2546. FROM departments d JOIN cte_emp e
    2547. ON d.`department_id`=e.`department_id`;
    2548. #【2.递归公用表表达式】
    2549. /*可以调用自己。
    2550. 【语法格式】
    2551. with recursive
    2552. cte名称 as (子查询)
    2553. select|delete|update 语句;
    2554. */
    2555. #举例
    2556. /* 针对于我们常用的employees表,包含employee_id,last_name和manager_id三个字段。
    2557. 如果a是b的管理者,那么,我们可以把b叫做a的下属,如果同时b又是c的管理者,那么c就是
    2558. b的下属,是a的下下属。*/
    2559. WITH RECURSIVE cte
    2560. AS(SELECT employee_id,last_name,manager_id,1 AS n FROM employees WHERE employee_id = 100 #种子查询,找到第一代领导
    2561. UNION ALL
    2562. SELECT a.employee_id,a.last_name,a.manager_id,n+1 FROM employees AS a JOIN cte
    2563. ON (a.manager_id = cte.employee_id) #递归查询,找出以递归公用表表达式的人为领导的人
    2564. )
    2565. SELECT employee_id,last_name FROM cte WHERE n >= 3
    2566. ###################################【初级篇完结】##################################################

  • 相关阅读:
    【3568开发板教程上新】Android11移植开发教程升级来袭!
    【c++随笔08】可变参数——va_list、va_start、va_end、va_arg
    css为盒子设置滚动条&隐藏滚动条
    MongoDB——将时间戳转换为日期
    GitHub上线重量级分布式架构原理设计笔记,开源的东西看着就是爽
    百度实习后端开发一二三面
    一个例子形象地理解同步与异步
    传输层协议 - TCP(Transmission Control Protocol)
    2022R2移动式压力容器充装考题及模拟考试
    Leetcode 1769. 移动所有球到每个盒子所需的最小操作数
  • 原文地址:https://blog.csdn.net/m0_58086930/article/details/127570054