MySQL 整理
一 初识MySQL
数据库和SQL概述
数据库简介
数据库指的是长期存在计算机内、有组织、可共享的、大量数据的集合。数据是按照特定的数据模型来组织、存储在数据库中的。
数据库分类
关系型数据库
关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织。
常见的关系型数据库有:SQLServer,Oracle,MySQL,PostgreSQL
优点:
- 易于维护:都是使用表结构,格式一致;
- 使用方便:SQL语言通用,可用于复杂查询;
- 复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。
缺点:
- 读写性能比较差,尤其是海量数据的高效率读写;
- 固定的表结构,灵活度稍欠;
- 高并发读写需求,传统关系型数据库来说,硬盘I/O是一个很大的瓶颈。
非关系型数据库
非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合,可以是文档或者键值对等。
常见的非关系型数据库有:MongoDB,Redis,CouchDB
优点:
- 格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型;
- 速度快:NoSQL可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
- 高扩展性:方便扩展,大数据量性能高,数据类型多样;
- 成本低:NoSQL数据库部署简单,基本都是开源软件。
缺点:
- 不提供sql支持,学习和使用成本较高;
- 无事务处理;
- 数据结构相对复杂,复杂查询方面稍欠缺。
数据库相关概念
- 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 AS 名
FROM
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 排序列表】
特点
- 和分组函数一同查询的字段必须是GROUP BY后出现的字段
- 筛选分为两类:分组前筛选和分组后筛选
针对的表 | 位置 | 连接的关键字 | |
---|---|---|---|
分组前筛选 | 原始表 | GROUP BY前 | WHERE |
分组后筛选 | GROUP BY后的结果集 | GROUP BY后 | HAVING |
问题1:分组函数做筛选能不能放在WHERE后面
答:不能,要放在HAVING子句
问题2:WHERE——GROUP BY——HAVING
一般来讲,能用分组前筛选的,尽量使用分组前筛选,提高效率
- 分组可以按单个字段也可以按多个字段
- 可以搭配着排序使用
示例
#引入:查询每个部门的平均工资?
#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 查询列表 ⑦
FROM 表1 别名 ①
【JOIN TYPE】JOIN 表2 ②
ON 连接条件 ③
WHERE 筛选条件 ④
GROUP BY 分组字段 ⑤
HAVING 分组后的筛选 ⑥
ORDER BY 排序的字段 ⑧
LIMIT 【OFFSET,】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个 | √,但不推荐 |
唯一 | √ | √ | 可以有多个 | √,但不推荐 |
外键
- 要求在从表设置外键关系
- 从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求
- 主表的关联列必须是一个key(一般是主键或唯一)
- 插入数据时,先插入主表,再插入从表;删除数据时,先删除从表,再删除主表
示例
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;
}
插入
}
*/