MySQL 整理

一 初识MySQL

数据库和SQL概述

数据库简介

数据库指的是长期存在计算机内、有组织、可共享的、大量数据的集合。数据是按照特定的数据模型来组织、存储在数据库中的。

数据库分类

关系型数据库

关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织。
常见的关系型数据库有:SQLServer,Oracle,MySQL,PostgreSQL

优点:

  1. 易于维护:都是使用表结构,格式一致;
  2. 使用方便:SQL语言通用,可用于复杂查询;
  3. 复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。

缺点:

  1. 读写性能比较差,尤其是海量数据的高效率读写;
  2. 固定的表结构,灵活度稍欠;
  3. 高并发读写需求,传统关系型数据库来说,硬盘I/O是一个很大的瓶颈。
非关系型数据库

非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合,可以是文档或者键值对等。
常见的非关系型数据库有:MongoDB,Redis,CouchDB

优点:

  1. 格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型;
  2. 速度快:NoSQL可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
  3. 高扩展性:方便扩展,大数据量性能高,数据类型多样;
  4. 成本低:NoSQL数据库部署简单,基本都是开源软件。

缺点:

  1. 不提供sql支持,学习和使用成本较高;
  2. 无事务处理;
  3. 数据结构相对复杂,复杂查询方面稍欠缺。

数据库相关概念

  • DB(database):数据库,保存一组有组织的数据的容器
  • DBMS(Database Management System):数据库管理系统,又称为数据库软件(产品),用于管理DB中的数据
    • 常见DBMS:MySQL、Oracle、DB2、SQL Server等
  • SQL:结构化查询语言,用于和数据库通信的语言

SQL的语言分类

  • DML(Data Manipulate Language):数据操纵语言,用于添加、删除、修改数据库记录,并检查数据完整性
    关键字:insert, delete, update 等
  • DDL(Data Define Languge):数据定义语言,用于库和表的创建、修改、删除
    关键字:create, drop,alter等
  • DCL(Data Control Language):数据事务语言
  • DQL(Data Query Language):数据查询语言,用来查询数据库中表的记录(数据)
    关键字:select, where 等

MySQL

MySQL 是一个开放源码的小型关联式数据库管理系统,开发者为瑞典 MySQL AB 公司。目前 MySQL 被广泛地应用在 Internet 上的中小型网站中。由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,许多中小型网站为了降低网站总体拥有成本而选择了 MySQL 作为网站数据库。

MySQL特性

  • 使用 C 和 C++ 编写,并使用了多种编译器进行测试,保证源代码的可移植性。
  • 支持 AIX、BSDi、FreeBSD、HP-UX、Linux、Mac OS、Novell Netware、NetBSD、OpenBSD、OS/2 Wrap、Solaris、SunOS、Windows 等多种操作系统。
  • 为多种编程语言提供了 API。这些编程语言包括 C、C++、C#、Delphi、Eiffel、Java、Perl、PHP、Python、Ruby 和 Tcl 等。
  • 支持多线程,充分利用 CPU 资源,支持多用户。
  • 优化的 SQL 查询算法,有效地提高查询速度。
  • 既能够作为一个单独的应用程序应用在客户端服务器网络环境中,也能够作为一个库而嵌入到其他的软件中。
  • 提供多语言支持,常见的编码如中文的 GB2312、BIG5,日文的 Shift_JIS 等都可以用作数据表名和数据列名。
  • 提供 TCP/IP、ODBC 和 JDBC 等多种数据库连接途径。
  • 提供用于管理、检查、优化数据库操作的管理工具。 可以处理拥有上千万条记录的大型数据库。

二 MySQL数据库的基本操作

MySQL数据库操作

启动和停止MySQL服务

  • 方式一:通过计算机管理方式
    右击计算机—管理—服务—启动或停止MySQL服务
  • 方式二:通过命令行方式
    启动:net start mysql服务名
    停止:net stop mysql服务名

MySQL服务端的登录和退出

  • 登录
mysql -h 127.0.0.1 -P 3306 -u root -p

mysql:使用mysql操作
-h:指明登陆部署了mysql服务的主机ip (不写默认本地主机ip)
-P:指明要访问的端口号(可修改) (不写默认设置的port(3306))
-u:指明登陆的用户(默认为root)
-p:指明要输入的密码,密码输入的时候,是不显示的

  • 退出

Ctrl + C

exit

MySQL数据库的使用

MySQL语法规范
  • 不区分大小写,但建议关键字大写、表名、列名小写
  • 每句话用;或\g结尾
  • 各子句一般分行写
  • 关键字不能缩写也不能分行
  • 用缩进提高语句的可读性
  • 注释

单行注释:
方式一:

 # 注释文字

方式二:

-- 注释文字

多行注释:

/* 
注释文字
注释文字
注释文字
···
*/

数据查询语言(Data Query Language, DQL)

01 基础查询

语法
SELECT 要查询的东西
【FROM 表名】;
特点
  • 通过SELECT查询完的结果 ,是一个虚拟的表格,不是真实存在
  • 要查询的东西 可以是常量值、可以是表达式、可以是字段、可以是函数
示例
#1.查询表中的单个字段
SELECT
	last_name 
FROM
	employees;
	
#2.查询表中的多个字段
SELECT
	first_name,
	employee_id,
	department_id,
	hiredate 
FROM
	employees;
	
#3.查询表中所有字段
#方式一:
SELECT
	first_name,
	employee_id,
	department_id,
	hiredate,
	last_name,
	email,
	phone_number,
	job_id,
	salary,
	commission_pct,
	manager_id 
FROM
	employees;
	
#方式二:
SELECT * FROM employees;
	
#4.查询常量值
SELECT
	100;
SELECT
	'john';
	
#5.查询表达式
SELECT
	100 % 98;
	
#6.查询函数
SELECT
	VERSION();
	
#7.起别名
/*
1.便于理解
2.如果要查询的字段有重名的情况,使用别名可以区分开来
*/
#方式一:
SELECT
	100 % 98 AS 结果;
SELECT
	last_name AS,
	first_name ASFROM
	employees;
	
#方式二:
SELECT
	last_name 姓,
	first_name 名 
FROM
	employees;
	
#案例:查询salary,显示结果为 out put
SELECT
	salary AS 'out put' 
FROM
	employees;
	
#8.去重

#案例:查询员工表中涉及到的所有部门编号
SELECT DISTINCT
	department_id 
FROM
	employees;
	
#9.+号的作用
/*
作用:做加法运算
SELECT	数值 + 数值; 直接运算 
SELECT 字符 + 数值; 先试图将字符转换成数值,如果转换成功,则继续运算;否则转换成 0,再做运算 
SELECT NULL + 值; 结果都为 NULL
*/

#案例:查询员工名和姓连接成一个字段,并显示为姓名
SELECT
	CONCAT( last_name, ' ', first_name ) AS 姓名 
FROM
	employees;

02 条件查询

语法
SELECT 查询列表
FROM 表名
WHERE 筛选条件
分类
简单条件运算符
 < = <>(!=) >=  <= <=>(安全等于)
逻辑运算符
&& AND 	
|| OR 	
!NOT
模糊查询
LIKE:一般搭配通配符使用,可以判断字符型或数值型
通配符:%任意多个字符(包含0个字符),_任意单个字符
BETWEEN AND
IN
IS NULL/IS NOT NULL:用于判断null值
示例
#一、按条件表达式筛选
#案例1:查询工资>12000的员工信息
SELECT
	* 
FROM
	employees 
WHERE
	salary > 12000;

#案例2:查询部门编号不为90号的员工名和部门编号
SELECT
	last_name,
	department_id 
FROM
	employees employees 
WHERE
	department_id <> 90;
	
#二、按逻辑表达式筛选
#案例1:查询工资在10000到20000之间的员工名、工资及奖金
SELECT
	last_name,
	salary,
	commission_pct 
FROM
	employees 
WHERE
	salary >= 10000 
	AND salary <= 20000;
	
#案例2:查询部门编号不是在90到110之间,或者工资高于15000的员工信息
SELECT
	* 
FROM
	employees 
WHERE
-- 		NOT (department_id >= 90 AND department_id <= 110) OR salary > 15000;
		department_id < 90 OR department_id > 110 OR salary > 15000;
		
#三、模糊查询

#1.LIKE
#案例1:查询员工名中包含字符a的员工信息
SELECT
	* 
FROM
	employees 
WHERE
	last_name LIKE '%a%';

#案例2:查询员工名中第三个字符为n,第五个字符为l的员工名和工资
SELECT
	last_name,
	salary 
FROM
	employees 
WHERE
	last_name LIKE '__n_l%';
	
#案例2:查询员工名中第二个字符为_的员工名(转义字符)
SELECT
	last_name,
	salary 
FROM
	employees 
WHERE
-- 	last_name LIKE '_\_%';
	last_name LIKE '_$_%' ESCAPE '$';

#2.BETWEEN AND
#案例:查询员工编号在100到120之间的员工信息
SELECT
	* 
FROM
	employees 
WHERE
-- 	employee_id >= 90 AND employee_id <= 120;
	employee_id BETWEEN 100 
	AND 120;
	
#3.IN
/*
含义:判断某字段的值是否属于IN列表中的某一项

特点:
①:使用IN提高语句简洁度
②:IN列表的值类型必须一致或兼容 '123' 123
③:IN列表的值不能使用通配符
*/
#案例:查询员工的工种编号是IT_PROG、AD_VP、AD_PRES中的一个员工名和工种编号
SELECT
	last_name,
	job_id 
FROM
	employees 
WHERE
-- 	job_id = 'IT_PROG' OR job_id = 'AD_VP' OR job_id = 'AD_PRES';
	job_id IN ( 'IT_PROG', 'AD_VP', 'AD_PRES' );

# 4.IS NULL
/*
= 或 <> 不能用于判断null值
*/
#案例1:查询没有奖金的员工名和奖金率
SELECT
	last_name,
	commission_pct 
FROM
	employees 
WHERE
	commission_pct IS NULL;
	
#案例2:查询有奖金的员工名和奖金率
SELECT
	last_name,
	commission_pct 
FROM
	employees 
WHERE
	commission_pct IS NOT NULL;
	
#安全等于 <=>
#案例1:查询没有奖金的员工名和奖金率
SELECT
	last_name,
	commission_pct 
FROM
	employees 
WHERE
	commission_pct <=> NULL;
	
#案例2:查询工资为12000的员工信息
SELECT
	last_name,
	salary,
	commission_pct 
FROM
	employees 
WHERE
	salary <=> 12000;
IS NULL P.K. <=>
普通类型的数值NULL值可读性
IS NULL×
<=>×

03 排序查询

语法
SELECT 查询列表
FROM 表
WHERE 筛选条件
ORDER BY 排序列表 【ASC/DESC】
特点
  • ASC代表升序,DESC代表降序,默认升序。
  • ORDER BY子句中可以支持单个字段、多个字段、表达式、函数、别名
  • ORDER BY子句一般是放在查询语句的最后面,LIMIT子句除外
示例
#案例1:查询员工信息,要求工资从高到底排序
SELECT
	* 
FROM
	employees 
ORDER BY
	salary DESC;
	
#案例2:查询部门编号>=90的员工信息,按入职时间的先后进行操作【添加筛选条件】
SELECT
	* 
FROM
	employees 
WHERE
	department_id >= 90 
ORDER BY
	hiredate;
	
#案例3:按年薪的高低显示员工的信息和年薪【按表达式排序】
SELECT
	*,
	salary * 12 * (1 + IFNULL(commission_pct, 0)) AS 年薪
FROM
	employees
ORDER BY
	salary * 12 * (1 + IFNULL(commission_pct, 0)) DESC;

#案例4:按年薪的高低显示员工的信息和年薪【按别名排序】
SELECT
	*,
	salary * 12 * (1 + IFNULL(commission_pct, 0)) AS 年薪
FROM
	employees
ORDER BY
	年薪 DESC;

#案例5:按姓名的长度显示;员工的姓名和工资【按函数排序】
SELECT
	LENGTH(last_name) 字节长度,
	last_name,
	salary
FROM
	employees
ORDER BY
	字节长度 DESC;

#案例6:查询员工信息,要求先按工资排序,再按员工编号排序【按多个字段排序】
SELECT
	* 
FROM
	employees 
ORDER BY
	salary ASC,
	employee_id DESC;

04 常见函数

概述

功能:类似于Java中的方法
好处:提高重用性和隐藏实现细节
调用:SELECT 函数名(实参列表);

分类
  • 字符函数
  • 数学函数
  • 日期函数
  • 其他函数
  • 流程控制函数
示例
#一、单行函数
#1.字符函数

#1.1 LENGTH(str) 获取参数值的字节个数
SELECT LENGTH('john');
SELECT LENGTH('张三丰');

-- SHOW VARIABLES LIKE '%char%'

#1.2 CONCAT(str1,str2,...) 拼接字符串
SELECT
	CONCAT( last_name, ' ', first_name ) AS 姓名 
FROM
	employees;
	
#1.3 UPPER(str)、LOWER(str)
SELECT UPPER('john');
SELECT LOWER('john');
#示例:将姓变大写,名变小写,然后拼接
SELECT CONCAT(UPPER(last_name),' ',LOWER(first_name)) AS 姓名 FROM employees;
	
#1.4 SUBSTR()、SUBSTRING()
#注意:索引从1开始
#截取从指定索引后面所有字符
SELECT SUBSTR('123456789', 7) 指定字符;

#截取从指定索引处指定字符长度的字符
SELECT SUBSTR('123456789', 3, 5) 指定字符;

#案例:姓名中首字母大写,其他字母小写然后用'_'拼接,显示出来
SELECT CONCAT(CONCAT(UPPER(SUBSTR(last_name,1,1)),'_',LOWER(SUBSTR(last_name,2))),' ',first_name) AS 姓名 FROM employees;
	
#1.5 INSTR(str,substr) 返回字串第一次出现的索引,如果找不到返回0
#案例:
SELECT
	INSTR( '111222333222111', '222' ) AS out_put;
	INSTR( '111222333222111', '444' ) AS out_put;

#1.6 TRIM函数可以过滤指定的字符串:
/*
完整格式:TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str)
简化格式:TRIM([remstr FROM] str)
*/

#未指定删除左右空格
SELECT
	TRIM( '  bar   ' );
#删除指定的首字符 x
SELECT
	TRIM( LEADING 'x' FROM 'xxxbarxxx' );

#删除指定的首尾字符 x
SELECT
-- 	TRIM( 'x' FROM 'xxxbarxxx' );
	TRIM( BOTH 'x' FROM 'xxxbarxxx' );

#删除指定的尾字符 x
SELECT
	TRIM( TRAILING 'xyz' FROM 'barxxyz' );

#LTRIM(str) 去除左空格
SELECT
	LTRIM( '   barbar' );

#RTRIM(str) 去除右空格
SELECT
	RTRIM( 'barbar   ' );
	
#1.7 填充函数LPAD(str,len,padstr) & RPAD(str,len,padstr)
/*
参数含义:
str: 要处理的对象
len: 处理完后的str长度为len
padstr: 如果str的长度小于len指定的值,那么长度差由padstr在左边填充;如果str的长度大于len指定的值,则截取str到len指定的长度。

注意:如果指定的len小于str的长度,此时将进行截取,并且无论是LPAD还是RPAD,都是保留靠前的字符.
*/

#LPAD(str,len,padstr) 用指定的字符实现左填充指定长度
SELECT
	LPAD( 'zhao', 10, '-' );
	
SELECT
	LPAD( 'zhao', 2, '-' );

#RPAD(str,len,padstr) 用指定的字符实现右填充指定长度
SELECT
	RPAD( 'zhao', 10, '-' );
	
SELECT
	RPAD( 'zhao', 2, '-' );

#1.8 REPLACE(str,from_str,to_str) 替换
/*
参数说明:
field: 数据库表的列名
search: 需要替换的字符串
replace: 替换成的字符串

将列名:field 中出现的search字符串,全部替换成replace字符串
*/

SELECT REPLACE
	( 'aaa.mysql.com', 'a', 'w' );
	
#2.数学函数
#2.1 ROUND() 四舍五入
SELECT
	ROUND(- 1.55 );
SELECT
	ROUND(- 1.643, 2 );
	
#2.2 CEIL(X) 向上取整,返回>=该参数的最小整数
SELECT
	CEIL(- 1.99 );
	
#2.3 FLOOR(X) 向下取整,返回<=该参数的最大整数
SELECT
	FLOOR(-9.01);
	
#2.4 TRUNCATE(X,D) 截断
SELECT
	TRUNCATE(1.733333,3);
	
#2.5 MOD(N,M) 取余
/*
MOD(N,M) : N-N/M*M
*/
SELECT MOD
	( 10,- 3 );
	
SELECT
	- 10 % - 3;
	
#2.6 RAND() 获取随机数,返回0-1之间的小数
SELECT	RAND();	
SELECT RAND();
SELECT RAND(33);
SELECT RAND(100);

#3.日期函数
#3.1 NOW() 返回当前系统日期 + 时间
SELECT NOW();

#3.2 CURTIME() 返回当前时间,不包含日期
SELECT CURTIME();

#3.3 获取指定部分,年、月、日、小时、分钟、秒
#年
SELECT YEAR(NOW());

#月
SELECT MONTH(NOW());
SELECT MONTHNAME(NOW());

#日
SELECT DAY(NOW());

#小时
SELECT HOUR(NOW());

#分钟
SELECT MINUTE(NOW());

#秒
SELECT SECOND(NOW());

#3.4 STR_TO_DATE(str,format) 将字符通过指定的格式转化成日期
/*
转化格式:
格式符				功能
	%Y 				4位的年份
	%y 				2位的年份
	%m 				月份(01,02,···11,12)
	%c 				月份(1,2,···11,12)
	%d 				日(01,02,···)
	%H 				24小时制
	%h 				12小时制
	%i 				分钟(00,01,···59)
	%s 				秒(00,01,···59)
*/

SELECT STR_TO_DATE('1999-12-10','%Y-%c-%d') AS birthday;
#案例:查询入职日期为1992-4-3的员工信息
SELECT*FROM employees WHERE hiredate='1992-4-3';	
SELECT*FROM employees WHERE hiredate=STR_TO_DATE('4-3 1992','%c-%d %Y');

#3.5 DATE_FORMAT(date,format) 将日期转换成字符
SELECT DATE_FORMAT(NOW(),'%y年%m月%d日') AS 日期;

#案例:查询有奖金的的员工名和入职日期(xx月/xx日  xx年)
SELECT last_name,DATE_FORMAT(hiredate,'%c月/%d日  %Y年') FROM employees WHERE commission_pct IS NOT NULL;

#3.6 DATEDIFF(expr1,expr2) 返回连个日期之间的间隔天数

#4.其他函数
#当前数据库服务器的版本
SELECT VERSION();
#当前打开的数据库
SELECT DATABASE();
#当前用户
SELECT USER();
#字符加密
SELECT PASSWORD(str);
SELECT MD5(str);

#5.流程控制函数
#5.1 IF(expr1,expr2,expr3) 三元运算符效果
SELECT IF(10>9,'TRUE','FALSE');
SELECT
	last_name,
	commission_pct,
IF
	( commission_pct IS NOT NULL, '有奖金', '没奖金' ) 
FROM
	employees;
	
#5.2 CASE函数
#CASE函数的使用①简单变量表达式:switch···case的效果
/*
CASE <表达式(变量)>
    WHEN <表达式1(变量的值)> THEN <表达式1(符合变量值的结果)>
    WHEN <表达式2(变量的值)> THEN <表达式2(符合变量值的结果)>
    WHEN <表达式3(变量的值)> THEN <表达式3(符合变量值的结果)>
		...
    ELSE <表达式(符合变量值的结果)>
END
*/

-- 案例:查询员工的工资,要求
-- 部门号=30,显示的工资为1.1倍
-- 部门号=40,显示的工资为1.2倍
-- 部门号=50,显示的工资为1.3倍

SELECT
	salary 原始工资,
	department_id 部门编号,
CASE department_id 
	WHEN 30 THEN salary*1.1 
	WHEN 40 THEN salary*1.2 
	WHEN 50 THEN salary*1.3 
	ELSE salary 
END AS 调整工资
FROM employees;

#CASE函数的使用②搜索变量表达式:类似多重if
/*
CASE 
    WHEN <求值表达式1(一般是判断)> THEN <表达式1(符合变量值的结果)>
    WHEN <求值表达式2(一般是判断)> THEN <表达式2(符合变量值的结果)>
    WHEN <求值表达式3(一般是判断)> THEN <表达式3(符合变量值的结果)>
		...
		ELSE <表达式(符合变量值的结果)>
END
*/

-- 案例:查询员工的工资的情况
-- 如果工资>20000, 显示A级别
-- 如果工资>15000, 显示B级别
-- 如果工资>10000, 显示C级别
-- 否则,显示D级别

SELECT
	salary,
	CASE
		WHEN salary > 20000 THEN 'A'
		WHEN salary > 15000 THEN 'B'
		WHEN salary > 10000 THEN 'C'
		ELSE 'D'
	END AS salary_level
FROM employees;

#二、分组函数
/*
MAX 最大值
MIN 最小值
SUM 和
AVG 平均值
COUNT 计算个数
*/
#1.简单使用
SELECT SUM(salary) FROM employees;
SELECT AVG(salary) FROM employees;
SELECT MAX(salary) FROM employees;
SELECT MIN(salary) FROM employees;
SELECT COUNT(salary) FROM employees;

SELECT SUM(salary), AVG(salary) 平均, MAX(salary) 最大, MIN(salary) 最小, COUNT(salary)  数量 FROM employees;

SELECT SUM(salary), ROUND(AVG(salary), 2) 平均, MAX(salary) 最大, MIN(salary) 最小, COUNT(salary)  数量 FROM employees;

#2.参数支持类型
#SUM和AVG一般用于处理数值型
#MAX、MIN、COUNT可以处理任何数据类型

#3.是否忽略NULL值
#SUM、AVG、MAX、MIN、COUNT都忽略NULL值

#4.和DISTINCT搭配使用,实现去重
SELECT COUNT(DISTINCT job_id), COUNT(job_id) FROM employees;

#5.COUNT()函数的详细介绍
/*
COUNT(expr): 统计该字段非空值的个数
COUNT(*): 统计结果集的行数
*/
SELECT COUNT(commission_pct) FROM employees;
SELECT COUNT(*) FROM employees;
SELECT COUNT(1) FROM employees;

/*
查询效率:
MYISAM存储引擎下,COUNT(*) 的效率高;
INNODB存储引擎下,COUNT(*) 和 COUNT(1) 的效率差不多,比COUNT(字段) 高一些。
*/
 
#6.和分组函数一同查询的字段,要求是GROUP BY后出现的字段

05 分组函数

语法
SELECT 分组函数,分组后的字段
FROM 表
【WHERE 筛选条件】
GROUP BY 分组的字段
【HAVING 分组后的筛选】
【ORDER BY 排序列表】
特点
  1. 和分组函数一同查询的字段必须是GROUP BY后出现的字段
  2. 筛选分为两类:分组前筛选和分组后筛选
针对的表位置连接的关键字
分组前筛选原始表GROUP BY前WHERE
分组后筛选GROUP BY后的结果集GROUP BY后HAVING

问题1:分组函数做筛选能不能放在WHERE后面
答:不能,要放在HAVING子句

问题2:WHERE——GROUP BY——HAVING
一般来讲,能用分组前筛选的,尽量使用分组前筛选,提高效率

  1. 分组可以按单个字段也可以按多个字段
  2. 可以搭配着排序使用
示例
#引入:查询每个部门的平均工资?

#1.简单分组
#案例1:查询每个工种的最高工资
SELECT
	MAX( salary ),
	job_id 
FROM
	employees 
GROUP BY
	job_id;

#案例2: 查询每个位置上的部门个数
SELECT
	COUNT(*) number,
	location_id 
FROM
	departments 
GROUP BY
	location_id;
	
#2. 实现分组前筛选
#案例1:查询邮箱中包含a字符的,每个部门的平均工资
SELECT
	AVG( salary ),
	department_id 
FROM
	employees 
WHERE
	email LIKE '%a%' 
GROUP BY
	department_id;
	
#案例2:查询有奖金的每个领导手下员工的平均工资
SELECT
	AVG( salary ),
	manager_id 
FROM
	employees 
WHERE
	commission_pct IS NOT NULL 
GROUP BY 
	manager_id;
	
#3.分组后筛选
#案例1:查询哪个部门的员工个数>5
#①查询每个部门的员工个数
SELECT
	COUNT(*),
	department_id 
FROM
	employees 
GROUP BY
	department_id;
#②筛选刚才①结果
SELECT
	COUNT(*),
	department_id 
FROM
	employees 
GROUP BY
	department_id 
HAVING
	COUNT(*)> 5;
	
#案例2:每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
SELECT
	job_id,
	MAX( salary ) 
FROM
	employees 
WHERE
	commission_pct IS NOT NULL
GROUP BY
	job_id 
HAVING
	MAX( salary )> 12000;

#案例3:领导编号>102的每个领导手下的最低工资大于5000的领导编号和最低工资
SELECT
	manager_id 领导编号,
	MIN( salary ) 最低工资 
FROM
	employees 
WHERE
	manager_id > 102 
GROUP BY
	manager_id
HAVING
	MIN(salary) > 5000;
	
#4.添加排序
#案例1:按员工姓氏的产生分组,查询每一组的员工个数,筛选员工个数>5的有哪些
SELECT
	COUNT(*) 数量,
	LENGTH( last_name ) 长度 
FROM
	employees 
GROUP BY
	长度 
HAVING
	数量 > 5;
	
#案例2:每个工种有奖金的员工的最高工资>6000的工种编号和最高工资,按最高工资升序
SELECT
	job_id 工种编号,
	MAX( salary ) 最高工资 
FROM
	employees 
WHERE
	commission_pct IS NOT NULL
GROUP BY
	工种编号 
HAVING
	最高工资 > 6000 
ORDER BY
	最高工资 ASC;


#5. 按多个字段分组
#案例:查询每个部门每个工种的员工的平均工资
SELECT
	department_id 部门,
	job_id 工种,
	ROUND( AVG( salary ), 2 ) 平均工资 
FROM
	employees 
GROUP BY
	部门,
	工种;

06 连接查询

含义

又称多表查询,当查询字段来自于多个表时,就会用到连接查询

笛卡尔乘积现象

表1 有m行,表2 有n行 结果 = m*n 行

  • 发生原因:没有有效的连接条件
  • 解决方法:添加有效的连接条件
分类
  • 按年代分

SQL92标准:仅仅支持内连接
SQL99标准【推荐】:支持内连接+外连接(左外+右外)+交叉连接

  • 按功能分
    ①内连接:
    等值连接
    非等值连接
    自连接
    ②外连接:
    左外连接
    右外连接
    全外连接
    ③交叉连接
示例
#错误示范:
SELECT 
	name,
	boyName 
FROM
	boys,
	beauty;
	
#修改建议:
SELECT 
	Name,
	boyName 
FROM
	boys,
	beauty
WHERE
	beauty.boyfriend_id = boys.id;
	
 #一、SQL92标准
 #--1.等值连接
 /*
 ①多表等值连接的结果为多表的交集部分
 ②n表连接,至少需要n-1个连接条件
 ③多表的顺序不作要求
 ④一般需要为表取别名 取别名后不能使用原来的表名
 ⑤可以搭配其他语句使用,如排序、分组、筛选
 */
 #案例1:查询女神名和对应的男神名
 SELECT 
	Name,
	boyName 
FROM
	boys,
	beauty
WHERE
	beauty.boyfriend_id = boys.id;
	
#案例2:查询员工名和对应的部门名
SELECT last_name, department_name
FROM employees, departments
WHERE employees.department_id = departments.department_id;

#2.为表起别名
/*
1.提高语句的简洁度
2.区分多个重名的字段

注意:
	①如果为表起了别名,则查询的字段就不能使用原来的表名去限定
	②查询表名的顺序可以调换

*/
#查询员工名、工种号、工种名
SELECT 
e.last_name, j.job_id, j.job_title
FROM
employees e,
jobs j
WHERE
e.job_id = j.job_id;

#3.添加筛选
#案例1:查询有奖金的员工名、部门名
SELECT
	last_name,
	department_name 
FROM
	employees e,
	departments d 
WHERE
	e.commission_pct IS NOT NULL 
	AND e.department_id = d.department_id;
	
#案例2:查询城市名中第二个字符为o所对应的部门名和城市名
SELECT
	department_name,
	city 
FROM
	departments d,
	locations l 
WHERE
	l.city LIKE '_o%' 
	AND d.department_id = l.location_id;
	
#4.添加分组
#案例1:查询每个城市的部门个数
SELECT
	l.city 名字,
	COUNT(*) 数量 
FROM
	locations l,
	departments d 
WHERE
	d.location_id = l.location_id 
GROUP BY
	city;

#案例2:查询有奖金的每个部门的部门名和部门的领导编号和部门的最低工资
SELECT
	department_name,
	d.manager_id,
	MIN( salary ) 
FROM
	employees e,
	departments d 
WHERE
	e.commission_pct IS NOT NULL 
	AND e.department_id = d.department_id 
GROUP BY
	department_name,
	d.manager_id;
	
#5.添加排序
#案例:查询每个工种的工种名和员工的个数,并且按员工个数降序排列
SELECT job_title 类别, COUNT(*) 数量
FROM employees e, jobs j 
WHERE e.job_id = j.job_id
GROUP BY j.job_title
ORDER BY 数量 DESC;

#6.三表及以上连接
#案例:查询员工名、部门名、和所在的城市
SELECT last_name, department_name, city
FROM employees e, departments d, locations l 
WHERE e.department_id = d.department_id AND d.location_id = l.location_id
ORDER BY last_name;

#--2.非等值连接

-- CREATE TABLE job_grades ( grade_level VARCHAR ( 3 ), lowest_sal INT, highest_sal INT );
-- INSERT INTO job_grades
-- VALUES
-- 	( 'A', 1000, 2999 );
-- INSERT INTO job_grades
-- VALUES
-- 	( 'B', 3000, 5999 );
-- INSERT INTO job_grades
-- VALUES
-- 	( 'C', 6000, 9999 );
-- INSERT INTO job_grades
-- VALUES
-- 	( 'D', 10000, 14999 );
-- INSERT INTO job_grades
-- VALUES
-- 	( 'E', 15000, 24999 );
-- INSERT INTO job_grades
-- VALUES
-- 	( 'F', 25000, 40000 );

#案例:查询员工的工资和工资级别
SELECT
	salary,
	grade_level,
	department_name,
	e.department_id
FROM
	employees e,
	job_grades g,
	departments d 
WHERE
	salary BETWEEN g.lowest_sal 
	AND g.highest_sal 
	AND d.department_id = e.department_id 
ORDER BY
	d.department_id;
	
#--3.自连接
#案例:查询员工名和上级领导名
SELECT
	e.last_name ename,
	e.manager_id,
	m.last_name mname,
	m.employee_id 
FROM
	employees e,
	employees m 
WHERE
	e.manager_id = m.employee_id 
ORDER BY
	m.employee_id;
	
#二、SQL99语法
/*
语法:
				SELECT		查询列表
				FROM	表1	别名	【连接类型】
				JOIN		表2 别名
				ON	连接条件
				【WHERE 筛选条件】
				【GROUP BY 分组】
				【HAVING 筛选条件】
				【ORDER BY 排序列表】
	
分类:
				内连接(★):INNER
				外连接
					左外(★):LEFT	【OUTER】
					右外(★):RIGHT	【OUTER】
					全外:FULL	【OUTER】
				交叉连接:CROSS

*/

#一)内连接
/*
语法:
				SELECT		查询列表
				FROM	表1	别名
				INNER JOIN		表2 别名
				ON	连接条件

分类:
				等值
				非等值
				自连接
				
特点:
①添加排序、分组、筛选
②INNER可以省略
③筛选条件放在WHERE后面,连接条件放在ON后面,提高分离性,便于阅读
④INNER JOIN连接和SQL92语法中的等值连接效果是一样的,都是查询多表的交集
*/

#1.等值连接
#案例1:查询员工名、部门名
SELECT
	last_name,
	department_name 
FROM
	employees e
	INNER JOIN departments d ON e.department_id = d.department_id;
	
#案例2:查询名字中包含e的员工名和工种名(添加筛选)
SELECT
	last_name,
	job_title 
FROM
	employees e
	INNER JOIN jobs j ON e.job_id = j.job_id 
WHERE
	last_name LIKE '%e%' 
GROUP BY
	job_title;

#案例3:查询部门个数>3的城市名和部门个数(添加分组+筛选)
SELECT
	city,
	COUNT(*) 部门个数 
FROM
	departments d
	INNER JOIN locations l ON d.location_id = l.location_id 
GROUP BY
	city 
HAVING
	COUNT(*) > 3;
	
#案例4:查询哪个部门的员工个数>3的部门名和员工个数,并按个数降序(添加排序)
SELECT
	department_name,
	COUNT(*)	员工个数
	FROM
	employees e 
	INNER JOIN departments d ON e.department_id = d.department_id
	GROUP BY
	department_name
	HAVING
	COUNT(*) > 3	
	ORDER BY
	COUNT(*) DESC;
	
#案例5:查询员工名、部门名、工种名,并按部门名降序(添加三表连接)
SELECT
	last_name,
	department_name,
	job_title 
FROM
	employees e
	INNER JOIN departments d ON e.department_id = d.department_id
	INNER JOIN jobs j ON e.job_id = j.job_id 
ORDER BY
	department_name DESC;
	
#2.非等值连接

#查询员工的工资级别
SELECT
	salary,
	grade_level 
FROM
	employees e
	JOIN job_grades g ON e.salary BETWEEN lowest_sal 
	AND highest_sal;
	
#查询工资级别的个数>20的个数,并且按工资级别降序
SELECT
	COUNT(*),
	grade_level 
FROM
	employees e
	JOIN job_grades g ON e.salary BETWEEN lowest_sal 
	AND highest_sal 
GROUP BY
	grade_level 
HAVING
	COUNT(*) > 20 
ORDER BY
	grade_level DESC;
	
#3.自连接

#查询员工的名字、上级的名字
SELECT
	e.last_name,
	e.manager_id,
	m.last_name,
	m.employee_id 
FROM
	employees e
	JOIN employees m ON e.manager_id = m.employee_id 
ORDER BY
	m.employee_id;
	
	#查询员工的名字中带m的名字以及上级的名字
SELECT
	e.last_name,
	e.manager_id,
	m.last_name,
	m.employee_id 
FROM
	employees e
	JOIN employees m ON e.manager_id = m.employee_id 
WHERE
	e.last_name LIKE '%m%' 
ORDER BY
	m.employee_id;
	
#二)外连接
/*
应用场景:用于查询一个表中有, 另一个表没有的记录

特点:
				1.外连接查询的结果为主表中的所有记录
					 如果从表中有和它匹配的,则显示匹配的值
					 如果从表中没有和它匹配的,则显示NULL
					 外连接查询结果=内连接结果+主表中有而从表中没有的记录
				 2.左外连接,LEFT左边的是主表
						右外连接,RIGHT JOIN右边的是主表
				 3.左外和右外交换两个表的顺序,可以实现同样的效果
				 4.全外连接=内连接的结果+表1中有但表2中没有的+表2中有但表1中没有的
*/ 

#引入:查询没有男朋友的女神名
#左外连接:
SELECT
	b.`name` 
FROM
	beauty b
	LEFT JOIN boys bo ON b.boyfriend_id = bo.id 
WHERE
	bo.id IS NULL;
	
#右外连接:
SELECT
	b.`name` 
FROM
	boys bo
	RIGHT JOIN beauty b ON b.boyfriend_id = bo.id 
WHERE
	bo.id IS NULL;
	
#案例1:查询哪个部门没有员工
-- 左外连接:
SELECT
	d.department_name
FROM
	departments d
	LEFT JOIN employees e ON e.department_id = d.department_id 
WHERE
	e.department_id IS NULL;
	
-- 右外连接:
SELECT
	d.department_name
	FROM
	employees e 
	RIGHT JOIN departments d ON e.department_id = d.department_id
WHERE
	e.employee_id IS NULL;

#全外连接:
SELECT
	b.*,
	bo.* 
FROM
	beauty b
	FULL JOIN boys bo ON b.boyfriend_id = bo.id;

#三)交叉连接
SELECT
	b.*,
	bo.* 
FROM
	beauty b
	CROSS JOIN boys bo;
	
#SQL92 & SQL99 P.K.
-- 功能:SQL99支持的较多
-- 可读性:SQL99实现连接条件和筛选条件的分离,可读性较高

07 子查询

含义
  • 嵌套在其他语句内部的SELECT语句称为子查询或内查询
  • 外面的语句可以是INSERT、UPDATE、DELETE、SELECT等,一般SELECT作为外面语句较多
  • 外面如果为SELECT语句,则此语句称为外查询或主查询
分类
  • 按子查询出现的位置:
    SELECT后面:
    仅仅支持标量子查询
    FROM后面:
    支持表子查询
    WHERE或HAVING后面:★
    标量子查询(单行子查询) √
    列子查询(多行子查询) √
    行子查询
    EXISTS后面(相关子查询):
    表子查询
  • 按结果集的行列数不同:
    标量子查询(单行子查询):结果集为一行一列
    列子查询(多行子查询):结果集为多行一列
    行子查询:结果集为多行多列
    表子查询:结果集为多行多列
示例

#一、WHERE或HAVING后面
#1. 标量子查询(单行子查询)
#2. 列子查询(多行子查询)
#3. 行子查询(多列子查询)
/*
特点:
①子查询放在小括号内
②子查询一般放在条件的右侧
③标量子查询,一般搭配着单行操作符使用 > < >= <= = <=>
④列子查询,一般搭配着多行操作符使用IN ANY/SOME ALL
⑤子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果
*/

#1. 标量子查询(单行子查询)
#案例1:谁的工资比Abel高
#①查询Abel的工资
SELECT
	salary 
FROM
	employees 
WHERE
	last_name = 'Abel';
	
#②查询员工的信息,满足 salary>①结果
SELECT
	* 
FROM
	employees 
WHERE
	salary > ( SELECT salary FROM employees WHERE last_name = 'Abel' );
	
#案例2:返回job_id与141号员工相同,salary比143号员工多的员工 姓名 job_id 和 工资
#①查询141号员工的job_id
SELECT
	job_id 
FROM
	employees 
WHERE
	employee_id = 141;
	
#②查询143号员工的salary
SELECT
	salary 
FROM
	employees 
WHERE
	employee_id = 143;
	
#③查询员工的姓名,job_id和工资,要求job_id=①并且salary>②
SELECT
	last_name,
	job_id,
	salary 
FROM
	employees 
WHERE
	job_id = ( SELECT job_id FROM employees WHERE employee_id = 141 ) 
	AND salary > ( SELECT salary FROM employees WHERE employee_id = 143 );
	
#案例3:返回公司工资最少的员工的last_name, job_id 和 salary
#①查询公司的最低工资
SELECT 
MIN(salary)
FROM
employees;

#②查询salary = ① 员工的last_name, job_id 和 salary
SELECT
	last_name,
	job_id,
	salary 
FROM
	employees 
WHERE
	salary = ( SELECT MIN( salary ) FROM employees );
	
#案例4:查询最低工资大于40号部门最低工资的部门id和其最低工资
#①查询40号部门的最低工资
SELECT
	MIN( salary ) 
FROM
	employees 
WHERE
	department_id = 40;

#②查询每个部门的最低工资
SELECT
	MIN( salary ),
	department_id 
FROM
	employees 
GROUP BY
	department_id;
	
#③在②基础上筛选,满足MIN( salary )>①
SELECT
	MIN( salary ), department_id 
FROM
	employees 
GROUP BY
	department_id 
HAVING
	MIN(salary) > ( SELECT MIN( salary ) FROM employees WHERE department_id = 40 );
	
#非法使用标量子查询
SELECT
	MIN( salary ), department_id 
FROM
	employees 
GROUP BY
	department_id 
HAVING
	MIN(salary) > ( SELECT salary FROM employees WHERE department_id = 50 );

#2. 列子查询(多行子查询)
#案例1:返回location_id是1400或1700的部门中的所有员工姓名
#①查询location_id是1400或1700的部门编号
SELECT DISTINCT
	department_id 
FROM
	departments 
WHERE
	location_id IN ( 1400, 1700 );
	
#②查询员工姓名,要求部门编号是①列表中的某一个
SELECT
	last_name 
FROM
	employees 
WHERE
	department_id IN ( SELECT DISTINCT department_id FROM departments WHERE location_id IN ( 1400, 1700 ) );
	
#案例2:返回其他工种中比job_id为’IT_PROG'任一工资低的员工的员工号,姓名,job_id 以及salary

#①查询job_id为‘IT_PROG' 部门任一工资
SELECT DISTINCT
	salary 
FROM
	employees 
WHERE
	job_id = 'IT_PROG';
	
#②查询员工的员工号,姓名,job_id 以及salary,salary<①的任意一个其他工种
SELECT
	employee_id,
	last_name,
	job_id,
	salary 
FROM
	employees 
WHERE
	salary < ANY ( SELECT DISTINCT salary FROM employees WHERE job_id = 'IT_PROG' ) 
	AND job_id <> 'IT_PROG';
	
	-- 或
	SELECT
	employee_id,
	last_name,
	job_id,
	salary 
FROM
	employees 
WHERE
	salary < ( SELECT MAX(salary) FROM employees WHERE job_id = 'IT_PROG' ) 
	AND job_id <> 'IT_PROG';
	
#案例3:返回其他部门中比job_id为’IT_PROG'部门所有工资都低的员工的 员工号,姓名,job_id 以及salary
SELECT
	employee_id,
	last_name,
	job_id,
	salary 
FROM
	employees 
WHERE
	salary < ALL ( SELECT DISTINCT salary FROM employees WHERE job_id = 'IT_PROG' ) 
	AND job_id <> 'IT_PROG';
	
		-- 或
	SELECT
	employee_id,
	last_name,
	job_id,
	salary 
FROM
	employees 
WHERE
	salary < ( SELECT MIN(salary) FROM employees WHERE job_id = 'IT_PROG' ) 
	AND job_id <> 'IT_PROG';
	
/*
IN 或 = ANY
NOT IN 或  <> ALL
*/

#3. 行子查询(结果集一行多列或多行多列)

#案例:查询员工编号最小并且工资最高的员工信息
#①查询最小的员工编号
SELECT
	MIN( employee_id ) 
FROM
	employees;
	
#②查询最高工资
SELECT
	MAX( salary ) 
FROM
	employees;
	
#③查询员工信息
SELECT
	* 
FROM
	employees 
WHERE
	employee_id = ( SELECT MIN( employee_id ) FROM employees ) 
	AND salary = ( SELECT MAX( salary ) FROM employees );
	
#行子查询
SELECT
	* 
FROM
	employees 
WHERE
	( employee_id, salary ) = ( SELECT MIN( employee_id ), MAX( salary ) FROM employees );
-- 局限在于所有条件的判断符号要一致

#二、SELECT后面
/*
	仅仅支持标量子查询
*/
#案例:查询每个部门的员工个数
#①查询有哪些部门
SELECT 
	department_id 
FROM
	departments;
	
#②完成查询
SELECT
	d.department_id,
	COUNT(*) 人数 
FROM
	departments d LEFT JOIN employees e ON e.department_id = d.department_id
-- WHERE
-- 	e.employee_id IS NOT NULL 
GROUP BY
	d.department_id 
ORDER BY
	d.department_id;

#
SELECT
	department_id,
	( SELECT COUNT(*) FROM employees e WHERE e.department_id = d.department_id ) 人数 
FROM
	departments d 
ORDER BY
	department_id;

#案例2:查询员工号=102的部门名
SELECT
	( SELECT department_name FROM departments d JOIN employees e ON d.department_id = e.department_id WHERE e.employee_id = 102 ) 部门名;
	
#三、FROM后面
/*
	将子查询结果充当一张表,要求必须起别名
*/
#案例:查询每个部门的平均工资等级
#①查询每个部门的平均工资
SELECT
AVG(salary),
department_id
FROM
employees
GROUP BY
department_id;

#②连接①的结果集和job_grades表,筛选条件平均工资between lowest_sal and highest_sal
SELECT
	avg_dep.*,
	g.grade_level 
FROM
	( SELECT AVG( salary ) avg, department_id FROM employees GROUP BY department_id ) avg_dep
	JOIN job_grades g ON avg_dep.avg BETWEEN g.lowest_sal 
	AND g.highest_sal;
	
#四、EXISTS后面(相关子查询)
/*
语法:
EXISTS(完整的查询语句)
结果:
1或者0


*/
SELECT EXISTS
	( SELECT employees employee_id FROM employees WHERE salary = 30000 );
	
#案例1:查询有员工的部门名
#EXISTS
SELECT
	department_name
FROM
	departments d 
WHERE
	EXISTS ( SELECT * FROM employees e WHERE d.department_id = e.department_id );
	
#IN
SELECT
	department_name 
FROM
	departments d 
WHERE
	d.department_id IN ( SELECT department_id FROM employees );
	
#案例2:查询没有女朋友的男神信息

#IN
SELECT
	bo.* 
FROM
	boys bo 
WHERE
	bo.id NOT IN ( SELECT b.boyfriend_id FROM beauty );
	
#EXISTS
SELECT
	bo.* 
FROM
	boys bo 
WHERE
	NOT EXISTS ( SELECT b.boyfriend_id FROM beauty b WHERE bo.id = b.boyfriend_id );

08 分页查询

应用场景:当要显示的数据,一页显示不全,需要分页提交SQL请求

语法
SELECT 查询列表
FROM 表1 【JOIN TYPE】JOIN 表2
ON 连接条件
WHERE 筛选条件
GROUP BY 分组字段
HAVING 分组后的筛选
ORDER BY 排序的字段
LIMIT 【OFFSET,】SIZE;

注意:

  • OFFSET代表的是起始的条目索引,默认从0卡死
  • SIZE代表的是显示的条目数
特点:
①lLIMIT语句放在查询语句的最后
②公式
要显示的页数 PAGE,每页的条目数SIZE

SELECT
FROM
LIMIT (PAGE - 1) * SIZE, SIZE;
示例
#案例1:查询前五条员工信息

SELECT
	* 
FROM
	employees 
	LIMIT 0,
	5;
	
SELECT
	* 
FROM
	employees 
	LIMIT 5;
	
#案例2:查询第11-25条数据
SELECT
	* 
FROM
	employees 
	LIMIT 10,
	15;
	
#案例3:有奖金的员工信息,并且工资较高的前10名显示出来
SELECT
	* 
FROM
	employees 
WHERE
	commission_pct IS NOT NULL 
ORDER BY
	salary DESC 
	LIMIT 10;
	
	
#DQL语言执行顺序
SELECT 查询列表						⑦
FROM1 别名						①
【JOIN TYPEJOIN2ON 连接条件						    ③
WHERE 筛选条件				        ④
GROUP BY 分组字段					⑤
HAVING 分组后的筛选				    ⑥
ORDER BY 排序的字段					⑧
LIMITOFFSET,】SIZE;

09 联合查询

含义

UNION:合并、联合,将多次查询结果合并成一个结果

语法
查询语句1
UNION 【ALL】
查询语句2
UNION 【ALL】
...
意义
  • 将一条比较复杂的查询语句拆分成多条语句
  • 适用于查询多个表的时候,查询的列基本是一致
特点
  • 要求多条查询语句的查询列数必须一致
  • 要求多条查询语句的查询的各列类型、顺序最好一致
  • UNION 去重,UNION ALL 包含重复项
示例
#引入案例:查询部门编号>90或邮箱包含a的员工信息
SELECT
	* 
FROM
	employees 
WHERE
	email LIKE '%a%' 
	OR department_id > 90;
	
SELECT
	* 
FROM
	employees 
WHERE
	email LIKE '%a%' UNION
SELECT
	* 
FROM
	employees 
WHERE
	department_id > 90;
	
#案例:查询中国用户中男性的信息以及外国用户中年男性的用户信息
SELECT
	id,
	cname 
FROM
	t_ca 
WHERE
	csex = '男' UNION ALL
SELECT
	t_id,
	tname 
FROM
	t_ua 
WHERE
	tGender = 'male';

查询总结

select 查询列表                      	⑦
from 表1 别名                            ①
连接类型 join 表2                  	    ②
on 连接条件         					    ③
where 筛选         	 				    ④
group by 分组列表   				 		⑤
having 筛选         						⑥
order by排序列表    						⑧
limit 起始条目索引,条目数;  				⑨

数据操纵语言(Data Manipulate Language, DML)

#DML语言
/*
数据操作语言:
插入:INSERT
修改:UPDATE
删除:DELETE

*/

#一、插入语句
#方式一:经典的插入
/*
语法:
INSERT INTO 表名(列名,...) VALUES(值1,...);

*/
SELECT
	* 
FROM
	beauty;
#1.插入的值的类型要与列的类型一致或兼容
INSERT INTO beauty ( id, NAME, sex, borndate, phone, photo, boyfriend_id )
VALUES
	( 13, '唐艺昕', '女', '1990-4-23', '1898888888', NULL, 2 );

#2.不可以为null的列必须插入值。可以为null的列如何插入值?
#方式一:
INSERT INTO beauty ( id, NAME, sex, borndate, phone, photo, boyfriend_id )
VALUES
	( 13, '唐艺昕', '女', '1990-4-23', '1898888888', NULL, 2 );

#方式二:
INSERT INTO beauty ( id, NAME, sex, borndate, phone, boyfriend_id )
VALUES
	( 14, '金星', '女', '1990-4-23', '1388888888', 9 );

INSERT INTO beauty ( id, NAME, sex, phone )
VALUES
	( 15, '娜扎', '女', '1388888888' );


#3.列的顺序是否可以调换
INSERT INTO beauty ( NAME, sex, id, phone )
VALUES
	( '蒋欣', '女', 16, '110' );

#4.列数和值的个数必须一致
INSERT INTO beauty ( NAME, sex, id, phone )
VALUES
	( '关晓彤', '女', 17, '110' );

#5.可以省略列名,默认所有列,而且列的顺序和表中列的顺序一致
INSERT INTO beauty
VALUES
	( 18, '张飞', '男', NULL, '119', NULL, NULL );

#方式二:
/*

语法:
INSERT INTO 表名
SET 列名=值,列名=值,...
*/
INSERT INTO beauty 
SET id = 19,
NAME = '刘涛',
phone = '999';


#两种方式大pk ★
#1、方式一支持插入多行,方式二不支持
INSERT INTO beauty
VALUES
	( 23, '唐艺昕1', '女', '1990-4-23', '1898888888', NULL, 2 ),(
		24,
		'唐艺昕2',
		'女',
		'1990-4-23',
		'1898888888',
		NULL,
		2 
		),(
		25,
		'唐艺昕3',
		'女',
		'1990-4-23',
		'1898888888',
		NULL,
		2 
	);

#2、方式一支持子查询,方式二不支持
INSERT INTO beauty ( id, NAME, phone ) SELECT
26,
'宋茜',
'11809866';

INSERT INTO beauty ( id, NAME, phone ) SELECT
id,
boyname,
'1234567' 
FROM
	boys 
WHERE
	id < 3;
	
#二、修改语句

/*

1.修改单表的记录★

语法:
UPDATE 表名
SET 列=新值,列=新值,...
WHERE 筛选条件;

2.修改多表的记录【补充】

语法:
SQL92语法:
UPDATE 表1 别名,表2 别名
SET 列=值,...
WHERE 连接条件
AND 筛选条件;

SQL99语法:
UPDATE 表1 别名
INNER|LEFT|RIGHT JOIN 表2 别名
ON 连接条件
SET 列=值,...
WHERE 筛选条件;


*/


#1.修改单表的记录
#案例1:修改beauty表中姓唐的女神的电话为13899888899
UPDATE beauty 
SET phone = '13899888899' 
WHERE
	NAME LIKE '唐%';

#案例2:修改boys表中id号为2的名称为张飞,魅力值 10
UPDATE boys 
SET boyname = '张飞',
usercp = 10 
WHERE
	id = 2;



#2.修改多表的记录

#案例 1:修改张无忌的女朋友的手机号为114
UPDATE boys bo
INNER JOIN beauty b ON bo.`id` = b.`boyfriend_id` 
SET b.`phone` = '119',
bo.`userCP` = 1000 
WHERE
	bo.`boyName` = '张无忌';


#案例2:修改没有男朋友的女神的男朋友编号都为2号
UPDATE boys bo
RIGHT JOIN beauty b ON bo.`id` = b.`boyfriend_id` 
SET b.`boyfriend_id` = 2 
WHERE
	bo.`id` IS NULL;

SELECT
	* 
FROM
	boys;


#三、删除语句
/*

方式一:DELETE
语法:

1、单表的删除【★】
DELETE FROM 表名 WHERE 筛选条件

2、(级联删除)多表的删除【补充】

SQL92语法:
DELETE 表1的别名,表2的别名
FROM 表1 别名,表2 别名
WHERE 连接条件
AND 筛选条件;

SQL99语法:

DELETE 表1的别名,表2的别名
FROM 表1 别名
INNER|LEFT|RIGHT JOIN 表2 别名 ON 连接条件
WHERE 筛选条件;



方式二:TRUNCATE
语法:TRUNCATE TABLE 表名;

*/

#方式一:DELETE
#1.单表的删除
#案例:删除手机号以9结尾的女神信息
DELETE 
FROM
	beauty 
WHERE
	phone LIKE '%9';
SELECT
	* 
FROM
	beauty;


#2.多表的删除

#案例:删除张无忌的女朋友的信息
DELETE b 
FROM
	beauty b
	INNER JOIN boys bo ON b.`boyfriend_id` = bo.`id` 
WHERE
	bo.`boyName` = '张无忌';


#案例:删除黄晓明的信息以及他女朋友的信息
DELETE b,
bo 
FROM
	beauty b
	INNER JOIN boys bo ON b.`boyfriend_id` = bo.`id` 
WHERE
	bo.`boyName` = '黄晓明';



#方式二:TRUNCATE语句

#案例:将魅力值>100的男神信息删除
TRUNCATE TABLE boys ;



#DELETE PK TRUNCATE【面试题★】

/*

1.DELETE 可以加WHERE 条件,TRUNCATE不能加

2.TRUNCATE删除,效率高一丢丢
3.假如要删除的表中有自增长列,
如果用DELETE删除后,再插入数据,自增长列的值从断点开始,
而TRUNCATE删除后,再插入数据,自增长列的值从1开始。
4.TRUNCATE删除没有返回值,DELETE删除有返回值

5.TRUNCATE删除不能回滚,DELETE删除可以回滚.

*/

SELECT
	* 
FROM
	boys;
	
DELETE 
FROM
	boys;
	
TRUNCATE TABLE boys;

INSERT INTO boys ( boyname, usercp )
VALUES
	( '张飞', 100 ),(
		'刘备',
		100 
		),(
		'关云长',
		100 
	);

数据定义语言(Data Define Languge, DDL)

库的管理

#DDL
/*

数据定义语言

库和表的管理

一、库的管理
创建、修改、删除
二、表的管理
创建、修改、删除

创建: CREATE
修改: ALTER
删除: DROP

*/

#库的管理
#1、库的创建
/*
语法:
CREATE DATABASE  [IF NOT EXISTS]库名;
*/


#案例:创建库Books
CREATE DATABASE
IF
	NOT EXISTS books;


#2、库的修改
-- RENAME DATABASE books TO 新库名;
-- 已被移除
/*
停止服务后直接修改文件名
*/

#更改库的字符集
ALTER DATABASE books CHARACTER 
SET gbk;


#3、库的删除
DROP DATABASE
IF
	EXISTS books;

表的管理

#表的管理
#1.表的创建 ★

/*
语法:
CREATE TABLE 表名(
	列名 列的类型【(长度) 约束】,
	列名 列的类型【(长度) 约束】,
	列名 列的类型【(长度) 约束】,
	...
	列名 列的类型【(长度) 约束】


)


*/
#案例:创建表Book
CREATE TABLE book ( id INT, #编号
	bName VARCHAR ( 20 ), #图书名
	price DOUBLE, #价格
	authorId INT, #作者编号
	publishDate DATETIME #出版日期
);


DESC book;

#案例:创建表author
CREATE TABLE
IF
	NOT EXISTS author (
		id INT,
		au_name VARCHAR ( 20 ),
		nation VARCHAR ( 10 )
)

DESC author;


#2.表的修改

/*
语法
ALTER TABLE 表名 ADD|DROP|MODIFY|CHANGE COLUMN 列名 【列类型 约束】;

*/

#①修改列名
ALTER TABLE book CHANGE COLUMN publishdate pubDate DATETIME;

#②修改列的类型或约束
ALTER TABLE book MODIFY COLUMN pubdate TIMESTAMP;

#③添加新列
ALTER TABLE author ADD COLUMN annual DOUBLE; 

#④删除列
ALTER TABLE author DROP COLUMN  annual;

#⑤修改表名
ALTER TABLE author RENAME TO book_author;

DESC book;
DESC author;




#3.表的删除

DROP TABLE IF EXISTS book_author;

SHOW TABLES;


#通用的写法:

DROP DATABASE IF EXISTS 旧库名;
CREATE DATABASE 新库名;


DROP TABLE IF EXISTS 旧表名;
CREATE TABLE  表名;



#4.表的复制

INSERT INTO author VALUES
(1,'村上春树','日本'),
(2,'莫言','中国'),
(3,'冯唐','中国'),
(4,'金庸','中国');

SELECT * FROM Author;
SELECT * FROM copy2;
#1.仅仅复制表的结构
CREATE TABLE copy LIKE author;

#2.复制表的结构+数据
CREATE TABLE copy2 SELECT
* 
FROM
	author;
#只复制部分数据
CREATE TABLE copy3 SELECT
id,
au_name 
FROM
	author 
WHERE
	nation = '中国';


#仅仅复制某些字段的结构
CREATE TABLE copy4 SELECT
id,
au_name 
FROM
	author 
WHERE
	0;

数据类型

#常见的数据类型
/*
数值型:
	整型
	小数:
		定点数
		浮点数
字符型:
	较短的文本:CHAR、VARCHAR
	较长的文本:TEXT、BLOB(较长的二进制数据)

日期型:
	


*/

#一、整型
/*
分类:
tinyint、smallint、mediumint、int/integer、bigint
	1	 	2		  3			  4			8

特点:
① 如果不设置无符号还是有符号,默认是有符号,如果想设置无符号,需要添加unsigned关键字
② 如果插入的数值超出了整型的范围,会报out of range异常,并且插入临界值
③ 如果不设置长度,会有默认的长度
长度代表了显示的最大宽度,如果不够会用0在左边填充,但必须搭配zerofill使用!

*/

#1.如何设置无符号和有符号
DROP TABLE
IF
	EXISTS tab_int;
CREATE TABLE tab_int ( t1 INT ( 7 ) ZEROFILL, t2 INT ( 7 ) ZEROFILL );

DESC tab_int;

INSERT INTO tab_int
VALUES
	(- 123456 );
	
INSERT INTO tab_int
VALUES
	(- 123456,- 123456 );
	
INSERT INTO tab_int
VALUES
	( 2147483648, 4294967296 );

INSERT INTO tab_int
VALUES
	( 123, 123 );

SELECT * FROM tab_int;


#二、小数
/*
分类:
1.浮点型
FLOAT(M,D)
DOUBLE(M,D)
2.定点型
DEC(M,D)
DECIMAL(M,D)

特点:

①
M:整数部位+小数部位
D:小数部位
如果超过范围,则插入临界值

②
M和D都可以省略
如果是decimal,则M默认为10,D默认为0
如果是float和double,则会根据插入的数值的精度来决定精度

③定点型的精确度较高,如果要求插入数值的精度较高如货币运算等则考虑使用


*/
#测试M和D

DROP TABLE tab_float;
CREATE TABLE tab_float(
	f1 FLOAT,
	f2 DOUBLE,
	f3 DECIMAL
);
SELECT * FROM tab_float;
DESC tab_float;

INSERT INTO tab_float VALUES(123.4523,123.4523,123.4523);
INSERT INTO tab_float VALUES(123.456,123.456,123.456);
INSERT INTO tab_float VALUES(123.4,123.4,123.4);
INSERT INTO tab_float VALUES(1523.4,1523.4,1523.4);



#原则:
/*
所选择的类型越简单越好,能保存数值的类型越小越好

*/

#三、字符型
/*
较短的文本:

CHAR
VARCHAR

其他:

BINARY和VARBINARY用于保存较短的二进制
ENUM用于保存枚举
SET用于保存集合


较长的文本:
TEXT
BLOB(较大的二进制)

特点:



			写法					M的意思						特点			   空间的耗费		效率
CHAR	  CHAR(M)	   最大的字符数,可以省略,默认为1		固定长度的字符		比较耗费	     高
VARCHAR  VARCHAR(M)		最大的字符数,不可以省略			可变长度的字符		比较节省	     低

*/


#ENUM 枚举类型
CREATE TABLE tab_char ( c1 ENUM ( 'a', 'b', 'c' ) );


INSERT INTO tab_char
VALUES
	( 'a' );
INSERT INTO tab_char
VALUES
	( 'b' );
INSERT INTO tab_char
VALUES
	( 'c' );
INSERT INTO tab_char
VALUES
	( 'm' );
INSERT INTO tab_char
VALUES
	( 'A' );
SELECT * FROM tab_set;


#SET 集合类型
CREATE TABLE tab_set ( s1 SET ( 'a', 'b', 'c', 'd' ) );
INSERT INTO tab_set
VALUES
	( 'a' );
INSERT INTO tab_set
VALUES
	( 'A,B' );
INSERT INTO tab_set
VALUES
	( 'a,c,d' );


#四、日期型

/*

分类:
DATE 只保存日期
TIME 只保存时间
YEAR 只保存年

DATETIME 保存日期+时间
TIMESTAMP 保存日期+时间


特点:

					   字节				范围				时区等的影响
DATETIME	          	8			1000--9999	            不受
TIMESTAMP				4	        1970--2038	             受

*/


CREATE TABLE tab_date ( t1 DATETIME, t2 TIMESTAMP );


INSERT INTO tab_date
VALUES
	(
		NOW(),
	NOW());

SELECT
	* 
FROM
	tab_date;

SHOW VARIABLES LIKE 'time_zone';


SET time_zone = '+9:00';

常见约束

含义

一种限制,用于限制表中的数据,为了保证表中的数据的准确和可靠性

分类:

六大约束

NOT NULL:非空,用于保证该字段的值不能为空
比如姓名、学号等
DEFAULT:默认,用于保证该字段有默认值
比如性别
PRIMARY KEY:主键,用于保证该字段的值具有唯一性,并且非空
比如学号、员工编号等
UNIQUE:唯一,用于保证该字段的值具有唯一性,可以为空
比如座位号
CHECK:检查约束【mysql中不支持】
比如年龄、性别
FOREIGN KEY:外键,用于限制两个表的关系,用于保证该字段的值必须来自于主表的关联列的值
在从表添加外键约束,用于引用主表中某列的值
比如学生表的专业编号,员工表的部门编号,员工表的工种编号

添加约束的时机

  • 创建表时
  • 修改表时

约束的添加分类

  • 列级约束:
    六大约束语法上都支持,但外键约束没有效果

  • 表级约束:
    除了非空、默认,其他的都支持

主键和唯一的大对比

保证唯一性是否允许为空一个表中可以有多少个是否允许组合
主键×至多有1个√,但不推荐
唯一可以有多个√,但不推荐

外键

  1. 要求在从表设置外键关系
  2. 从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求
  3. 主表的关联列必须是一个key(一般是主键或唯一)
  4. 插入数据时,先插入主表,再插入从表;删除数据时,先删除从表,再删除主表
示例
CREATE TABLE 表名(
	字段名 字段类型 列级约束,
	字段名 字段类型,
	表级约束

)
CREATE DATABASE students;
#一、创建表时添加约束

#1.添加列级约束
/*
语法:

直接在字段名和类型后面追加 约束类型即可。

只支持:默认、非空、主键、唯一



*/

USE students;
DROP TABLE stuinfo;
CREATE TABLE stuinfo (
	id INT PRIMARY KEY,#主键
	stuName VARCHAR ( 20 ) NOT NULL UNIQUE,#非空
	gender CHAR ( 1 ) CHECK ( gender = '男' OR gender = '女' ),#检查
	seat INT UNIQUE,#唯一
	age INT DEFAULT 18,#默认约束
	majorId INT REFERENCES major ( id ) #外键
	
);


CREATE TABLE major ( id INT PRIMARY KEY, majorName VARCHAR ( 20 ) );

#查看stuinfo中的所有索引,包括主键、外键、唯一
SHOW INDEX 
FROM
	stuinfo;


#2.添加表级约束
/*

语法:在各个字段的最下面
 【constraint 约束名】 约束类型(字段名) 
*/

DROP TABLE
IF
	EXISTS stuinfo;
CREATE TABLE stuinfo (
	id INT,
	stuname VARCHAR ( 20 ),
	gender CHAR ( 1 ),
	seat INT,
	age INT,
	majorid INT,
	CONSTRAINT pk PRIMARY KEY ( id ),#主键
	CONSTRAINT uq UNIQUE ( seat ),#唯一键
	CONSTRAINT ck CHECK ( gender = '男' OR gender = '女' ),#检查
	CONSTRAINT fk_stuinfo_major FOREIGN KEY ( majorid ) REFERENCES major ( id ) #外键
	
);


SHOW INDEX 
FROM
	stuinfo;



#通用的写法:★

CREATE TABLE
IF
	NOT EXISTS stuinfo (
		id INT PRIMARY KEY,
		stuname VARCHAR ( 20 ),
		sex CHAR ( 1 ),
		age INT DEFAULT 18,
		seat INT UNIQUE,
		majorid INT,
		CONSTRAINT fk_stuinfo_major FOREIGN KEY ( majorid ) REFERENCES major ( id ) 
	);



#二、修改表时添加约束

/*
1、添加列级约束
alter table 表名 modify column 字段名 字段类型 新约束;

2、添加表级约束
alter table 表名 add 【constraint 约束名】 约束类型(字段名) 【外键的引用】;


*/
DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo(
	id INT,
	stuname VARCHAR(20),
	gender CHAR(1),
	seat INT,
	age INT,
	majorid INT
)
DESC stuinfo;
#1.添加非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR ( 20 ) NOT NULL;
#2.添加默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18;
#3.添加主键
#①列级约束
ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY;
#②表级约束
ALTER TABLE stuinfo ADD PRIMARY KEY(id);

#4.添加唯一

#①列级约束
ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE;
#②表级约束
ALTER TABLE stuinfo ADD UNIQUE(seat);


#5.添加外键
ALTER TABLE stuinfo ADD CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id); 

#三、修改表时删除约束

#1.删除非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NULL;

#2.删除默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT ;

#3.删除主键
ALTER TABLE stuinfo DROP PRIMARY KEY;

#4.删除唯一
ALTER TABLE stuinfo DROP INDEX seat;

#5.删除外键
ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major;

SHOW INDEX FROM stuinfo;

/*
				位置			支持的约束类型		是否可以起约束名
列级约束:  	  列的后面		  除了外键				  否
表级约束:	所有列的下面		除了非空和默认		 是(主键无效)

列级约束可以在一个字段上追加多个,中间用空格隔开,没有顺序要求
*/


#四、自增长列(标识列)
#标识列
/*
又称为自增长列
含义:可以不用手动的插入值,系统提供默认的序列值

语法:
一、创建表时设置自增长列
create table 表(
	字段名 字段类型 约束 auto_increment
)
二、修改表时设置自增长列
alter table 表 modify column 字段名 字段类型 约束 auto_increment
三、删除自增长列
alter table 表 modify column 字段名 字段类型 约束 


特点:
1、标识列必须和主键搭配吗?不一定,但要求是一个key
2、一个表可以有几个标识列?至多一个!
3、标识列的类型只能是数值型
4、标识列可以通过 SET auto_increment_increment=3;设置步长
可以通过 手动插入值,设置起始值
如果要更改起始值:手动插入值
如果要更改步长:更改系统变量

*/

#1. 创建表时设置标识列


DROP TABLE
IF
	EXISTS tab_identity;
	
CREATE TABLE tab_identity ( id INT, NAME FLOAT UNIQUE AUTO_INCREMENT, seat INT );
TRUNCATE TABLE tab_identity;


INSERT INTO tab_identity ( id, NAME )
VALUES
	( NULL, 'john' );
INSERT INTO tab_identity ( NAME )
VALUES
	( 'lucy' );
SELECT
	* 
FROM
	tab_identity;


SHOW VARIABLES LIKE '%auto_increment%';


SET auto_increment_increment = 3;
CREATE TABLE 表名(
	字段名 字段类型 列级约束,
	字段名 字段类型,
	表级约束

)
CREATE DATABASE students;
#一、创建表时添加约束

#1.添加列级约束
/*
语法:

直接在字段名和类型后面追加 约束类型即可。

只支持:默认、非空、主键、唯一



*/

USE students;
DROP TABLE stuinfo;
CREATE TABLE stuinfo (
	id INT PRIMARY KEY,#主键
	stuName VARCHAR ( 20 ) NOT NULL UNIQUE,#非空
	gender CHAR ( 1 ) CHECK ( gender = '男' OR gender = '女' ),#检查
	seat INT UNIQUE,#唯一
	age INT DEFAULT 18,#默认约束
	majorId INT REFERENCES major ( id ) #外键
	
);


CREATE TABLE major ( id INT PRIMARY KEY, majorName VARCHAR ( 20 ) );

#查看stuinfo中的所有索引,包括主键、外键、唯一
SHOW INDEX 
FROM
	stuinfo;


#2.添加表级约束
/*

语法:在各个字段的最下面
 【constraint 约束名】 约束类型(字段名) 
*/

DROP TABLE
IF
	EXISTS stuinfo;
CREATE TABLE stuinfo (
	id INT,
	stuname VARCHAR ( 20 ),
	gender CHAR ( 1 ),
	seat INT,
	age INT,
	majorid INT,
	CONSTRAINT pk PRIMARY KEY ( id ),#主键
	CONSTRAINT uq UNIQUE ( seat ),#唯一键
	CONSTRAINT ck CHECK ( gender = '男' OR gender = '女' ),#检查
	CONSTRAINT fk_stuinfo_major FOREIGN KEY ( majorid ) REFERENCES major ( id ) #外键
	
);


SHOW INDEX 
FROM
	stuinfo;



#通用的写法:★

CREATE TABLE
IF
	NOT EXISTS stuinfo (
		id INT PRIMARY KEY,
		stuname VARCHAR ( 20 ),
		sex CHAR ( 1 ),
		age INT DEFAULT 18,
		seat INT UNIQUE,
		majorid INT,
		CONSTRAINT fk_stuinfo_major FOREIGN KEY ( majorid ) REFERENCES major ( id ) 
	);



#二、修改表时添加约束

/*
1、添加列级约束
alter table 表名 modify column 字段名 字段类型 新约束;

2、添加表级约束
alter table 表名 add 【constraint 约束名】 约束类型(字段名) 【外键的引用】;


*/
DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo(
	id INT,
	stuname VARCHAR(20),
	gender CHAR(1),
	seat INT,
	age INT,
	majorid INT
)
DESC stuinfo;
#1.添加非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR ( 20 ) NOT NULL;
#2.添加默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18;
#3.添加主键
#①列级约束
ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY;
#②表级约束
ALTER TABLE stuinfo ADD PRIMARY KEY(id);

#4.添加唯一

#①列级约束
ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE;
#②表级约束
ALTER TABLE stuinfo ADD UNIQUE(seat);


#5.添加外键
ALTER TABLE stuinfo ADD CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id); 

#三、修改表时删除约束

#1.删除非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NULL;

#2.删除默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT ;

#3.删除主键
ALTER TABLE stuinfo DROP PRIMARY KEY;

#4.删除唯一
ALTER TABLE stuinfo DROP INDEX seat;

#5.删除外键
ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major;

SHOW INDEX FROM stuinfo;

/*
				位置			支持的约束类型		是否可以起约束名
列级约束:	  列的后面		  除了外键				  否
表级约束:	所有列的下面		除了非空和默认		 是(主键无效)

列级约束可以在一个字段上追加多个,中间用空格隔开,没有顺序要求
*/


#四、自增长列(标识列)
#标识列
/*
又称为自增长列
含义:可以不用手动的插入值,系统提供默认的序列值

语法:
一、创建表时设置自增长列
create table 表(
	字段名 字段类型 约束 auto_increment
)
二、修改表时设置自增长列
alter table 表 modify column 字段名 字段类型 约束 auto_increment
三、删除自增长列
alter table 表 modify column 字段名 字段类型 约束 


特点:
1、标识列必须和主键搭配吗?不一定,但要求是一个key
2、一个表可以有几个标识列?至多一个!
3、标识列的类型只能是数值型
4、标识列可以通过 SET auto_increment_increment=3;设置步长
可以通过 手动插入值,设置起始值
如果要更改起始值:手动插入值
如果要更改步长:更改系统变量

*/

#1. 创建表时设置标识列


DROP TABLE
IF
	EXISTS tab_identity;
	
CREATE TABLE tab_identity ( id INT, NAME FLOAT UNIQUE AUTO_INCREMENT, seat INT );
TRUNCATE TABLE tab_identity;


INSERT INTO tab_identity ( id, NAME )
VALUES
	( NULL, 'john' );
INSERT INTO tab_identity ( NAME )
VALUES
	( 'lucy' );
SELECT
	* 
FROM
	tab_identity;


SHOW VARIABLES LIKE '%auto_increment%';


SET auto_increment_increment = 3;

事务控制语言(Transaction Control Language, DCL)

事务的含义

一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行。

事务的特性(ACID)

  • A 原子性:一个事务是不可再分割的整体,要么都执行要么都不执行
  • C 一致性:一个事务可以使数据从一个一致状态切换到另外一个一致的状态
  • I 隔离性:一个事务不受其他事务的干扰,多个事务互相隔离的
  • D 持久性:一个事务一旦提交了,则永久的持久化到本地

事务的使用步骤 ★

了解:
隐式(自动)事务:没有明显的开启和结束,本身就是一条事务可以自动提交,比如insert、update、delete
显式事务:具有明显的开启和结束

使用显式事务:
①开启事务

SET autocommit=0;
START TRANSACTION; -- 可选的

②编写一组逻辑sql语句
注意:SQL语句支持的是INSERT、UPDATE、DELETE

设置回滚点:
SAVEPOINT 回滚点名;

③结束事务
COMMIT;提交事务
ROLLBACK;回滚事务
回滚到指定的地方:ROLLBACK TO 回滚点名;

并发事务

  • 事务的并发问题是如何发生的?
    多个事务 同时 操作 同一个数据库的相同数据时
  • 并发问题都有哪些?
    **脏读:**一个事务读取了其他事务还没有提交的数据,读到的是其他事务“更新”的数据
    **不可重复读:**一个事务多次读取,结果不一样
    **幻读:**一个事务读取了其他事务还没有提交的数据,只是读到的是 其他事务“插入”的数据
  • 如何解决并发问题
    通过设置隔离级别来解决并发问题
  • 隔离级别
脏读不可重复读幻读
read uncommitted读未提交××
read committed读已提交×
repeatable read可重复读
serializable串行化

示例

SHOW VARIABLES LIKE 'autocommit';
SHOW ENGINES;

#1.演示事务的使用步骤

#开启事务
#禁用自动提交功能
SET autocommit=0;
#开启事务
START TRANSACTION; -- 可选的
#编写一组事务的语句
UPDATE account SET balance = 1000 WHERE username='张无忌';
UPDATE account SET balance = 1000 WHERE username='赵敏';

#结束事务
ROLLBACK;
#commit;

SELECT * FROM account;


#2.演示事务对于delete和truncate的处理的区别
#DELETE  能够成功回滚
SET autocommit=0;
START TRANSACTION;
DELETE FROM account;
ROLLBACK;

#TRUNCATE 数据已被删除无法回滚
SET autocommit=0;
START TRANSACTION;
TRUNCATE TABLE account;
ROLLBACK;

#3.演示savepoint 的使用

SET autocommit = 0;
START TRANSACTION;
DELETE 
FROM
	account 
WHERE
	id = 25;
SAVEPOINT a;#设置保存点
DELETE 
FROM
	account 
WHERE
	id = 28;
ROLLBACK TO a;#回滚到保存点
SELECT
	* 
FROM
	account;

其他处理

视图

含义

mysql5.1版本出现的新特性,本身是一个虚拟表,它的数据来自于表,通过执行时动态生成。

好处

  • 简化SQL语句
  • 提高了SQL的重用性
  • 保护基表的数据,提高了安全性

视图与表的比较

创建语法的关键字是否占用物理空间使用
视图CREATE VIEW占用较小仅保留SQL逻辑一般用于查询
CREATE TABLE保存实际的数据增删改查
示例
#案例:查询姓张的学生名和专业名
SELECT stuname,majorname
FROM stuinfo s
INNER JOIN major m ON s.`majorid`= m.`id`
WHERE s.`stuname` LIKE '张%';

CREATE VIEW v1
AS
SELECT stuname,majorname
FROM stuinfo s
INNER JOIN major m ON s.`majorid`= m.`id`;

SELECT * FROM v1 WHERE stuname LIKE '张%';


#一、创建视图
/*
语法:
create view 视图名
as
查询语句; 

*/
USE myemployees;

#1.查询姓名中包含a字符的员工名、部门名和工种信息
#①创建
CREATE VIEW myv1 AS SELECT
last_name,
department_name,
job_title 
FROM
	employees e
	JOIN departments d ON e.department_id = d.department_id
	JOIN jobs j ON j.job_id = e.job_id;

#②使用
SELECT * FROM myv1 WHERE last_name LIKE '%a%';






#2.查询各部门的平均工资级别

#①创建视图查看每个部门的平均工资
CREATE VIEW myv2 AS SELECT
AVG( salary ) ag,
department_id 
FROM
	employees 
GROUP BY
	department_id;

#②使用
SELECT
	myv2.`ag`,
	g.grade_level 
FROM
	myv2
	JOIN job_grades g ON myv2.`ag` BETWEEN g.`lowest_sal` 
	AND g.`highest_sal`;



#3.查询平均工资最低的部门信息
SELECT
	* 
FROM
	myv2 
ORDER BY
	ag 
	LIMIT 1;

#4.查询平均工资最低的部门名和工资
CREATE VIEW myv3 AS SELECT
* 
FROM
	myv2 
ORDER BY
	ag 
	LIMIT 1;


SELECT
	d.*,
	m.ag 
FROM
	myv3 m
	JOIN departments d ON m.`department_id` = d.`department_id`;




#二、视图的修改

#方式一:
/*
create or replace view  视图名
as
查询语句;

*/
SELECT
	* 
FROM
	myv3 CREATE 
	OR REPLACE VIEW myv3 AS SELECT
	AVG( salary ),
	job_id 
FROM
	employees 
GROUP BY
	job_id;

#方式二:
/*
语法:
ALTER VIEW 视图名
AS 
查询语句;

*/
ALTER VIEW myv3
AS
SELECT * FROM employees;

#三、删除视图

/*

语法:drop view 视图名,视图名,...;
*/

DROP VIEW emp_v1,emp_v2,myv3;


#四、查看视图

DESC myv3;

SHOW CREATE VIEW myv3;


#五、视图的更新

CREATE 
	OR REPLACE VIEW myv1 AS SELECT
	last_name,
	email,
	salary * 12 *(
	1+IFNULL ( commission_pct, 0 )) "annual salary" 
FROM
	employees;

CREATE 
	OR REPLACE VIEW myv1 AS SELECT
	last_name,
	email 
FROM
	employees;


SELECT * FROM myv1;
SELECT * FROM employees;
#1.插入

INSERT INTO myv1 VALUES('张飞','zf@qq.com');

#2.修改
UPDATE myv1 SET last_name = '张无忌' WHERE last_name='张飞';

#3.删除
DELETE FROM myv1 WHERE last_name = '张无忌';

#具备以下特点的视图不允许更新


#①包含以下关键字的sql语句:分组函数、distinct、group  by、having、union或者union all

CREATE OR REPLACE VIEW myv1
AS
SELECT MAX(salary) m,department_id
FROM employees
GROUP BY department_id;

SELECT * FROM myv1;

#更新
UPDATE myv1 SET m=9000 WHERE department_id=10;

#②常量视图
CREATE 
	OR REPLACE VIEW myv2 AS SELECT
	'john' NAME;

SELECT * FROM myv2;

#更新
UPDATE myv2 SET NAME='lucy';





#③Select中包含子查询
CREATE OR REPLACE VIEW myv3 AS SELECT department_id,( SELECT MAX( salary ) FROM employees ) 最高工资 
FROM
	departments;
	
#更新
SELECT * FROM myv3;
UPDATE myv3 SET 最高工资=100000;


#④join
CREATE 
	OR REPLACE VIEW myv4 AS SELECT
	last_name,
	department_name 
FROM
	employees e
	JOIN departments d ON e.department_id = d.department_id;

#更新

SELECT * FROM myv4;
UPDATE myv4 
SET last_name = '张飞' 
WHERE
	last_name = 'Whalen';
INSERT INTO myv4
VALUES
	( '陈真', 'xxxx' );



#⑤from一个不能更新的视图
CREATE 
	OR REPLACE VIEW myv5 AS SELECT
	* 
FROM
	myv3;

#更新

SELECT * FROM myv5;

UPDATE myv5 SET 最高工资=10000 WHERE department_id=60;



#⑥where子句的子查询引用了from子句中的表
CREATE 
	OR REPLACE VIEW myv6 AS SELECT
	last_name,
	email,
	salary 
FROM
	employees 
WHERE
	employee_id IN ( SELECT manager_id FROM employees WHERE manager_id IS NOT NULL );

#更新
SELECT
	* 
FROM
	myv6;
UPDATE myv6 
SET salary = 10000 
WHERE
	last_name = 'k_ing';

变量

系统变量

说明:变量由系统提供的,不用自定义
注意:全局变量需要添加global关键字,会话变量需要添加session关键字,如果不写,默认会话级别

使用步骤

1、查看所有系统变量
show global |【session】variables;
2、查看满足条件的部分系统变量
show global |【session】 variables like ‘%char%’;
3、查看指定的系统变量的值
select @@global |【session】系统变量名;
4、为某个系统变量赋值
方式一:
set global |【session】系统变量名=值;
方式二:
set @@global |【session】系统变量名=值;

示例
#1》全局变量
/*
服务器层面上的,必须拥有super权限才能为系统变量赋值,作用域为整个服务器,也就是针对于所有连接(会话)有效
作用域:针对于所有会话(连接)有效,但不能跨重启
*/
#①查看所有全局变量
SHOW GLOBAL VARIABLES;
#②查看满足条件的部分系统变量
SHOW GLOBAL VARIABLES LIKE '%char%';
#③查看指定的系统变量的值
SELECT @@global.autocommit;
#④为某个系统变量赋值
SET @@global.autocommit=0;
SET GLOBAL autocommit=0;

#2》会话变量
/*
服务器为每一个连接的客户端都提供了系统变量,作用域为当前的连接(会话)
作用域:针对于当前会话(连接)有效
*/
#①查看所有会话变量
SHOW SESSION VARIABLES;
#②查看满足条件的部分会话变量
SHOW SESSION VARIABLES LIKE '%char%';
#③查看指定的会话变量的值
SELECT @@autocommit;
SELECT @@session.tx_isolation;
#④为某个会话变量赋值
SET @@session.tx_isolation='read-uncommitted';
自定义变量

说明:变量由用户自定义,而不是系统提供的

分类
用户变量

作用域:针对于当前连接(会话)生效
位置:begin end里面,也可以放在外面

使用

①声明并赋值:
set @变量名=值;或
set @变量名:=值;或
select @变量名:=值;

②更新值
方式一:
set @变量名=值;或
set @变量名:=值;或
select @变量名:=值;
方式二:
select xx into @变量名 from 表;

③使用
select @变量名;

示例
#1》用户变量
/*
作用域:针对于当前会话(连接)有效,作用域同于会话变量
*/

#赋值操作符:=或:=
#①声明并初始化
SET @变量名=;
SET @变量名:=;
SELECT @变量名:=;

#②赋值(更新变量的值)
#方式一:
	SET @变量名=;
	SET @变量名:=;
	SELECT @变量名:=;
#方式二:
	SELECT 字段 INTO @变量名
	FROM;
#③使用(查看变量的值)
SELECT @变量名;
局部变量

作用域:仅仅在定义它的begin end中有效
位置:只能放在begin end中,而且只能放在第一句

使用

①声明
declare 变量名 类型 【default 值】;
②赋值或更新
方式一:
set 变量名=值;或
set 变量名:=值;或
select @变量名:=值;
方式二:
select xx into 变量名 from 表;
③使用
select 变量名;

示例
/*
作用域:仅仅在定义它的begin end块中有效
应用在 begin end中的第一句话
*/

#①声明
DECLARE 变量名 类型;
DECLARE 变量名 类型 【DEFAULT 值】;


#②赋值(更新变量的值)

#方式一:
	SET 局部变量名=值;
	SET 局部变量名:=值;
	SELECT @局部变量名:=值;
#方式二:
	SELECT 字段 INTO 局部变量名
	FROM 表;
#③使用(查看变量的值)
SELECT 局部变量名;


#案例:声明两个变量,求和并打印

#用户变量
SET @m=1;
SET @n=1;
SET @sum=@m+@n;
SELECT @sum;

#局部变量
DECLARE m INT DEFAULT 1;
DECLARE n INT DEFAULT 1;
DECLARE SUM INT;
SET SUM=m+n;
SELECT SUM;l

用户变量和局部变量的对比
作用域定义位置语法
用户变量当前会话会话的任何地方加@符号,不用指定类型
局部变量定义它的BEGIN END中BEGIN END的第一句话一般不用加@,需要指定类型

存储过程和函数

存储过程

含义:一组预先编译好的SQL语句的集合,理解成批处理语句
好处

  • 提高代码的重用性
  • 简化操作
  • 减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率
示例
#一、创建语法

CREATE PROCEDURE 存储过程名(参数列表)
BEGIN

	存储过程体(一组合法的SQL语句)
END

#注意:
/*
1、参数列表包含三部分
参数模式  参数名  参数类型
举例:
in stuname varchar(20)

参数模式:
in:该参数可以作为输入,也就是该参数需要调用方传入值
out:该参数可以作为输出,也就是该参数可以作为返回值
inout:该参数既可以作为输入又可以作为输出,也就是该参数既需要传入值,又可以返回值

2、如果存储过程体仅仅只有一句话,begin end可以省略
存储过程体中的每条sql语句的结尾要求必须加分号。
存储过程的结尾可以使用 delimiter 重新设置
语法:
delimiter 结束标记
案例:
delimiter $
*/


#二、调用语法

CALL 存储过程名(实参列表);

#--------------------------------案例演示-----------------------------------
#1.空参列表
#案例:插入到admin表中五条记录

SELECT * FROM admin;


DELIMITER $
CREATE PROCEDURE myp1 () BEGIN
	INSERT INTO admin ( username, `password` )
	VALUES
		( 'john1', '0000' ),(
			'lily',
			'0000' 
			),(
			'rose',
			'0000' 
			),(
			'jack',
			'0000' 
			),(
			'tom',
			'0000' 
		);
	
END $


#调用
CALL myp1()$

#2.创建带in模式参数的存储过程

#案例1:创建存储过程实现 根据女神名,查询对应的男神信息
CREATE PROCEDURE myp2 (
	IN beautyName VARCHAR ( 20 )) BEGIN
	SELECT
		bo.* 
	FROM
		boys bo
		RIGHT JOIN beauty b ON bo.id = b.boyfriend_id 
	WHERE
		b.NAME = beautyName;
	
END $

#调用
CALL myp2('柳岩')$

#案例2 :创建存储过程实现,用户是否登录成功

CREATE PROCEDURE myp4(IN username VARCHAR(20),IN PASSWORD VARCHAR(20))
BEGIN
	DECLARE result INT DEFAULT 0;#声明并初始化
	
	SELECT COUNT(*) INTO result#赋值
	FROM admin
	WHERE admin.username = username
	AND admin.password = PASSWORD;
	
	SELECT IF(result>0,'成功','失败');#使用
END $

#调用
CALL myp3('张飞','8888')$


#3.创建out 模式参数的存储过程
#案例1:根据输入的女神名,返回对应的男神名

CREATE PROCEDURE myp6(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20))
BEGIN
	SELECT bo.boyname INTO boyname
	FROM boys bo
	RIGHT JOIN
	beauty b ON b.boyfriend_id = bo.id
	WHERE b.name=beautyName ;
	
END $


#案例2:根据输入的女神名,返回对应的男神名和魅力值
CREATE PROCEDURE myp7 ( IN beautyName VARCHAR ( 20 ), OUT boyName VARCHAR ( 20 ), OUT usercp INT ) BEGIN
	SELECT
		boys.boyname,
		boys.usercp INTO boyname,
		usercp 
	FROM
		boys
		RIGHT JOIN beauty b ON b.boyfriend_id = boys.id 
	WHERE
		b.NAME = beautyName;
	
END $


#调用
CALL myp7('小昭',@name,@cp)$
SELECT @name,@cp$



#4.创建带inout模式参数的存储过程
#案例1:传入a和b两个值,最终a和b都翻倍并返回
CREATE PROCEDURE myp8 ( INOUT a INT, INOUT b INT ) BEGIN
	
	SET a = a * 2;
	
	SET b = b * 2;
	
END $

#调用
SET @m=10$
SET @n=20$
CALL myp8(@m,@n)$
SELECT @m,@n$


#三、删除存储过程
#语法:drop procedure 存储过程名
DROP PROCEDURE p1;
DROP PROCEDURE p2,p3;

#四、查看存储过程的信息
DESC myp2;
SHOW CREATE PROCEDURE  myp2;
函数

含义:一组预先编译好的SQL语句的集合,理解成批处理语句
好处

  • 提高代码的重用性
  • 简化操作
  • 减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率

区别

  • 存储过程:可以有0个返回,也可以有多个返回,适合做批量插入、批量更新
  • 函数:有且仅有1 个返回,适合做处理数据后返回一个结果
示例
#一、创建语法
CREATE FUNCTION 函数名(参数列表) RETURNS 返回类型
BEGIN
	函数体
END
/*

注意:
1.参数列表 包含两部分:
参数名 参数类型

2.函数体:肯定会有return语句,如果没有会报错
如果return语句没有放在函数体的最后也不报错,但不建议

return 值;
3.函数体中仅有一句话,则可以省略begin end
4.使用 delimiter语句设置结束标记

*/

#二、调用语法
SELECT 函数名(参数列表)


#------------------------------案例演示----------------------------
#1.无参有返回
#案例:返回公司的员工个数
CREATE FUNCTION myf1() RETURNS INT
BEGIN

	DECLARE c INT DEFAULT 0;#定义局部变量
	SELECT COUNT(*) INTO c#赋值
	FROM employees;
	RETURN c;
	
END $

SELECT myf1()$


#2.有参有返回
#案例1:根据员工名,返回它的工资

CREATE FUNCTION myf2(empName VARCHAR(20)) RETURNS DOUBLE
BEGIN
	SET @sal=0;#定义用户变量 
	SELECT salary INTO @sal   #赋值
	FROM employees
	WHERE last_name = empName;
	
	RETURN @sal;
END $

SELECT myf2('k_ing') $

#案例2:根据部门名,返回该部门的平均工资

CREATE FUNCTION myf3(deptName VARCHAR(20)) RETURNS DOUBLE
BEGIN
	DECLARE sal DOUBLE ;
	SELECT AVG(salary) INTO sal
	FROM employees e
	JOIN departments d ON e.department_id = d.department_id
	WHERE d.department_name=deptName;
	RETURN sal;
END $

SELECT myf3('IT')$

#三、查看函数

SHOW CREATE FUNCTION myf3;

#四、删除函数
DROP FUNCTION myf3;

#案例
#一、创建函数,实现传入两个float,返回二者之和

CREATE FUNCTION test_fun1(num1 FLOAT,num2 FLOAT) RETURNS FLOAT
BEGIN
	DECLARE SUM FLOAT DEFAULT 0;
	SET SUM=num1+num2;
	RETURN SUM;
END $

SELECT test_fun1(1,2)$

流程控制结构

分支结构
if 函数

语法:if(条件,值1,值2)
功能:实现双分支
位置:应用在begin end中或外面

case 结构

语法
情况1:类似于switch
case 变量或表达式
when 值1 then 语句1;
when 值2 then 语句2;

else 语句n;
end case;

情况2:
case
when 条件1 then 语句1;
when 条件2 then 语句2;

else 语句n;
end case;

应用在begin end 中或外面

位置
可以放在任何位置,
如果放在begin end 外面,作为表达式结合着其他语句使用
如果放在begin end 里面,一般作为独立的语句使用

if 结构

语法
if 条件1 then 语句1;
elseif 条件2 then 语句2;

else 语句n;
end if;
功能:类似于多重if

位置:只能应用在begin end 中

示例
#案例1:创建函数,实现传入成绩,如果成绩>90,返回A,如果成绩>80,返回B,如果成绩>60,返回C,否则返回D
CREATE FUNCTION test_if ( score FLOAT ) RETURNS CHAR BEGIN
	DECLARE
		ch CHAR DEFAULT 'A';
	IF
		score > 90 THEN
			
			SET ch = 'A';
		
		ELSEIF score > 80 THEN
		
		SET ch = 'B';
		
		ELSEIF score > 60 THEN
		
		SET ch = 'C';
		ELSE 
			SET ch = 'D';
		
	END IF;
	RETURN ch;
	
END $ SELECT
test_if ( 87 )$

#案例2:创建存储过程,如果工资<2000,则删除,如果5000>工资>2000,则涨工资1000,否则涨工资500
CREATE PROCEDURE test_if_pro ( IN sal DOUBLE ) BEGIN
	IF
		sal < 2000 THEN DELETE FROM employees WHERE employees.salary = sal; ELSEIF sal >= 2000 
		AND sal < 5000 THEN
			UPDATE employees 
			SET salary = salary + 1000 
			WHERE
				employees.`salary` = sal;
			ELSE UPDATE employees 
			SET salary = salary + 500 
			WHERE
				employees.`salary` = sal;
			
		END IF;
		
	END $ CALL test_if_pro ( 2100 )$

#案例1:创建函数,实现传入成绩,如果成绩>90,返回A,如果成绩>80,返回B,如果成绩>60,返回C,否则返回D
CREATE PROCEDURE test_case ( IN score INT ) BEGIN CASE WHEN score >= 90 
		AND score < 100 THEN 'A'; WHEN score > 80 THEN
		'B';
			
			WHEN score > 60 THEN
			'C';
			ELSE SELECT
				'D';
			
		END CASE;
		
	END $ CALL test_case ( 95 ) $


CREATE FUNCTION test_case ( score FLOAT ) RETURNS CHAR BEGIN DECLARE ch CHAR DEFAULT 'A'; CASE WHEN score > 90 THEN
		
		SET ch = 'A';
		
		WHEN score > 80 THEN
		
		SET ch = 'B';
		
		WHEN score > 60 THEN
		
		SET ch = 'C';
		ELSE 
			SET ch = 'D';
		
		END CASE;
	RETURN ch;
	
END $ SELECT
test_case ( 56 ) $
循环结构
分类

loop :一般用于实现简单的死循环

  • 语法

       【标签:】loop
      	循环体;
      end loop 【标签】;
    

while :先判断后执行

  • 语法

      【名称:】while 循环条件 do
      		循环体
      end while 【名称】;
    

repeat: 先执行后判断,无条件至少执行一次

  • 语法

       【名称:】repeat
      		循环体
      until 结束条件 
      end repeat 【名称】;
    
循环控制语句

leave:类似于break,用于跳出所在的循环
iterate:类似于continue,用于结束本次循环,继续下一次

示例
#1.没有添加循环控制语句
#案例:批量插入,根据次数插入到admin表中多条记录
DROP PROCEDURE pro_while1$
CREATE PROCEDURE pro_while1(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 1;
	WHILE i<=insertCount DO
		INSERT INTO admin(username,`password`) VALUES(CONCAT('Rose',i),'666');
		SET i=i+1;
	END WHILE;
	
END $

CALL pro_while1(100)$


/*

int i=1;
while(i<=insertcount){

	//插入
	
	i++;

}

*/


#2.添加leave语句

#案例:批量插入,根据次数插入到admin表中多条记录,如果次数>20则停止
TRUNCATE TABLE admin$
DROP PROCEDURE test_while1$
CREATE PROCEDURE test_while1(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 1;
	a:WHILE i<=insertCount DO
		INSERT INTO admin(username,`password`) VALUES(CONCAT('xiaohua',i),'0000');
		IF i>=20 THEN LEAVE a;
		END IF;
		SET i=i+1;
	END WHILE a;
END $


CALL test_while1(100)$


#3.添加iterate语句

#案例:批量插入,根据次数插入到admin表中多条记录,只插入偶数次
TRUNCATE TABLE admin$
DROP PROCEDURE test_while1$
CREATE PROCEDURE test_while1(IN insertCount INT)
BEGIN
	DECLARE i INT DEFAULT 0;
	a:WHILE i<=insertCount DO
		SET i=i+1;
		IF MOD(i,2)!=0 THEN ITERATE a;
		END IF;
		
		INSERT INTO admin(username,`password`) VALUES(CONCAT('xiaohua',i),'0000');
		
	END WHILE a;
END $


CALL test_while1(100)$

/*

int i=0;
while(i<=insertCount){
	i++;
	if(i%2==0){
		continue;
	}
	插入
	
}

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值