优点:几乎所有DBMS 都支持SQL, 简单易学, 语言强大,灵活使用语言元素,可以进行非常复杂和高级的数据库操作
1、将数据放到表中,表再放到库中
2、一个数据库中可以有多个表,每个表都有一个的名字,用来标识自己。表名具有唯一性。
3、表具有一些特性,这些特性定义了数据在表中如何存储,类似java中 “类”的设计。
4、表由列组成,我们也称为字段。所有表都是由一个或多个列组成的,每一列类似java 中的”属性”
5、表中的数据是按行存储的,每一行类似于java中的**“对象”**。
属于Oracle 公司
优点:
DBMS 分为两类:
MySQL: 社区版和企业版
下载5.5 或者 5.6 即可
卸载:
控制面板 -> 卸载 并去C盘 -> Program Files(×86) -> MySQL 卸载 再去 隐藏文件ProgramData 删掉MySQL
还是不行就去清理注册表
安装目录下的my.ini 是配置文件 (可以点开看看, 里面有许多配置信息)
方式一:计算机——右击管理——服务
**方式二:**通过管理员身份运行cmd
net start 服务名(mysql)(启动服务)
net stop 服务名(停止服务)
方式一:通过mysql自带的客户端(Command Linme Client) 只限于root用户
**方式二:**通过管理员身份运行cmd
登录:
mysql -h主机名 -P端口号 -u用户名 -p密码
(本地的话: 主机名:localhost, 端口号:3306) 输入-p 可以直接输入密码, 也可以直接按回车
本机直接可以简写: mysql -u 用户 -p 密码
退出:
exit 或 ctrl+C
如果无法识别命令行, 就是环境没配置好
在cmd 按键盘上下键可以切换刚刚输入过的命令
1.查看当前所有的数据库
show databases; #显示的前三个数据库不能动
2.打开指定的库
use 库名
3.查看当前库的所有表
show tables;
4.查看其它库的所有表
show tables from 库名; #但是当前仍然在刚刚进入的表中,只是查看了其他库的表
5.创建表
create table 表名(
列名 列类型,
列名 列类型, 字符串用 varchar(20)
...
);
6.查看表结构
desc 表名;
7.查看自己在那个表中
select database();
8.查看版本
select version();
dos 命令不用加分号, sql 命令才加分号
一个询问就是一个sql 文件
数据冗余 -> 分类存储
可以使用图形化界面来方便我们
USE myemployees
# 基础查询
/*
语法:
select 查询列表 from 表名;
特点:
1. 查询列表可以是: 表中的字段, 常量, 表达式, 函数
2. 查询结果是虚拟的表格, 不是真实存在的
*/
#1. 查询单个字段
SELECT
`last_name`
FROM
employees;
# 查询表中多个字段 查询的可以不按表中的顺序
SELECT
`last_name`,
`salary`,
email
FROM
employees;
# 查询表中的所有顺序
SELECT
*
FROM
employees;
# 查询常量值
SELECT 100;
SELECT
# 查询表达式
SELECT 100%98
# 查询函数 调用该函数并得到返回值并显示
SELECT VERSION();
#给字段其别名(提高可读性, 如果要查询的字段有重名的情况, 这个可以帮我们区分开)
#方式一: 使用 AS
SELECT 100%98 AS 结果;
SELECT last_name AS 姓, first_name AS 名 FROM employees;
#方式二: 使用空格
SELECT last_name 姓, first_name 名 FROM employees;
#案例: 如果别名和关键字重名 需要我们加双引号或单引号
Select salary AS 'out put' from employees;
#去重
#案例: 查询涉及到的所有的部门编号
SELECT DISTINCT department_id FROM employees;
# +号的作用: 运算符
/*
SELECT 100+90;
SELECT '123'+90 # 试图把字符型转换为数值型, 成功:做加法运算 失败:字符型转化为0
只要一方为null 则结果必为null
*/
#案例: 查询员工的姓和名并连接成一个字段, 显示为姓名
SELECT CONCAT(last_name, first_name) AS '姓名' FROM employees;
**注意: **
条件查询:根据条件过滤原始表的数据,查询到想要的数据
语法:
select
要查询的字段|表达式|常量值|函数
from
表
where
条件;
分类:
一、条件表达式
示例:salary > 10000
条件运算符:
> < >= <= = != <> 最后一个也是不等于
二、逻辑表达式
示例:salary > 10000 && salary < 20000
逻辑运算符:
AND(&&):两个条件如果同时成立,结果为true,否则为false
OR(||):两个条件只要有一个成立,结果为true,否则为false
NOT(!):如果条件成立,则not后为false,否则为true
三、模糊查询
BETWEEN AND: 包含临界值, 但是不能调换顺序
LIKE: 一般搭配通配符%(任意多个字符, 包括零个字符的情况), 下划线_表示任意单个字符
IN: 判断某字段的值是否属于in 列表中的某一项, 提高了语句简洁度, IN 列表的值的类型必须一致或兼容, 不支持写通配符
IS NULL: = <> 不能用于判断null , 所以有了它, 但是它只能判断null 值
安全等于<=> :可以用于判断null, 但是符号长的丑, 用的不多
IFNULL: salary * 12 * (1 + IFNULL(commission_pct, 0)) AS ‘年薪’; 如果commission_pct 为空, 其值就为0
#案例: 查询员工中包含字符a 的员工信息
SELECT *
FROM employees
WHERE last_name LIKE '%a%'; #字符用单引号, 百分号代表通配符, 默认不区分大小写
#案例: 查询员工名第二个字符为_的员工名 用转义字符/k
SELECT *
FROM employees
WHERE last_name LIKE '_"$_%' ESCAPE '$';
#案例:员工编号在100 到120 之间的员工信息
SELECT *
FROM employees
WHERE employee_id BETWEEN 100 AND 120; # 包含临界值, 但是不能调换顺序
#案例:查询员工的工种编号是 IT_PROG, AD_VP, AD_PRES 中的员工名和工种编号
SELECTlast_name, job_id
FROM employees
WHERE job_id IN('IT_PROG', 'AD_VP', 'AD_PRES');
#案例:查询没有奖金的员工名和奖金率
SELECT last_name, commission_pct
FROM employees
WHERE commission_pct IS NULL;
#案例:查询有奖金的员工名和奖金率
SELECT last_name, commission_pct
FROM employees
WHERE commission_pct IS NOT NULL;
#案例: 查询员工号为176 的员工的姓名和年薪
SELECT last_name, department_id, salary * 12 * (1 + IFNULL(commission_pct, 0)) AS '年薪'
FROM employees
WHERE employee_id = 176;
面试题:
select * from employees; 和 select * from employees where commission_pct like ‘%%’ and last_name like ‘%%’ 的结果是否一样, 并说明原因
答: 不一样, 如果判断的字段有null值, 结果就不一样. 如果有就一样
ORDER BY 可以支持单个字段, 多个字段, 表达式, 函数, 别名
一般放在查询语句的最后面, 但 limit 子句除外
语法:
SELECt 查询列表
FROM 表
WHERE 筛选条件
ORDER BY 排序列表 ASC | DESC # 升序或降序, 不写默认是升序
#案例:查询员工信息,要求工资从高到低排序
SELECT * FROM employees ORDER BY salary DESC; #降序
#案例:查询部门编号>=90 的员工信息, 按入职时间的先后进行排序
SELECT *
FROM employees
WHERE department_id >=90
OEDER BY hiredate ASC;
#案例:按年薪的高低显示员工的信息和 年薪(按表达式排序)
SELECT *, salary * 12 * (1 + IFNULL(commission_pct, 0) AS 年薪
FROM employees
ORDER BY salary * 12 * (1 + IFNULL(commission_pct, 0)) DESC;
#按年薪的高低显示员工的信息和 年薪(按别名排序)
ELECT *, salary * 12 * (1 + IFNULL(commission_pct, 0) AS 年薪
FROM employees
ORDER BY 年薪 DESC;
#案例:按姓名的长度显示员工的姓名和工资(按函数排序)
SELECT LENGTH(last_name) 姓名长度, last_name, salary
FROM employees
ORDER BY 姓名长度 DESC;
#案例: 查询员工信息, 要求先按工资排序, 再按员工编号排序(多字段排序)
SELECT *
FROM employees
ORDER BY salary ASC, employee_id ASC;
**概念:**把逻辑语句封装在 方法体, 对外暴露方法名
语法:
SELECT 函数名()
FROM 表; #如果函数的参数用到了表中的字段就需要加FROM 表, 没用到就不用加
分类:
单行函数: CONCAT, LENGTH, IFNULL 等 有一个返回值
分组函数: 做统计使用, 又称为统计函数, 聚合函数, 组函数
单行函数:
字符函数:
# LENGTH: 获取参数值的字节个数
SELECT LENGTH('jo'); #2
SELECT LENGTH('土豆123'); #9 取决于字符集, utf-8 三字节 gbk 就是两字节
# CONCAT 拼接字符串
SELECT CONCAT(last_name, first_name) AS 姓名 FROM employees;
# UPPER, LOWER
SELECT UPPER('shit'); #SHIT
#示例:把姓变大写, 名变小写然后拼接
SELECT CONCAT(UPPER(last_name), LOWER(first_name)) 姓名 FROM employees;
#substr, substring 索引从1开始
SELECT SUBSTR('shit bro', 2); # 返回索引位置开始之后的字符
SELECT SUBSTR('shit bro', 1, 3); # 返回从指定索引处指定的字符长度的字符
#INSTR 返回字串的起始索引, 不区分大小写, 如果没有返回0
SELECT INSTR('shit', 'bro shit') AS out_put;
#TRIM 去掉前后空格
SELECT TRIM(' 是啊 ');
SELECT TRIM('a' FROM 'aaaa是啊aaaaa'); #去掉前后的字符a
#LPAD 用指定字符来左填充达到指定长度 RPAD 右填充
SELECT LPAD('shit bro', 10, '*'); #如果长度小于字符串, 也会截断
例题:
# 查询所有学院的邮箱的用户名
SELECT substr(email, 1, instr(email, '@') - 1) 用户名
FROM stuinfo;
数学函数:
#ROUND 四舍五入
SELECT ROUND(-1.55); # 绝对值四舍五入再加负号 -2
SELECT ROUND(1.567, 2); # 小数点后保留两位 1.57
#CEIL 向上取整 FLOOR 向下取整
SELECT CEIL(1.02); #2
SELECT CEIL(-1.02); #-1
SELECT FLOOR(-9.1); #-10
#TRUNCATE 截断
SELECT TRUNCATE(1.655, 1); #1.6
#MOD MOD(a, b) = a - a / b * b
SELECT MOD(10, 3); #1
SELECT MOD(-10, 3); #-1
日期函数
#NOW 返回当前系统日期 + 时间
SELECT NOW();
#CURDATE 返回当前系统日期, 不含时间
SELECT CURDATE();
#CURTIME 返回当前时间, 不包含时间
SELECT CURTIME();
#获取指定部分, 年YEAR(), 月MONTH(), MONTHNAME(), 日, 小时, 分, 秒
SELECT YEAR(NOW()) 年;
SELECT YEAR('2001-02-18') 年;
SELECT YAER(hiredate) 年 FROM employees;
#str_to_date: 把日期格式的字符转化成指定格式的日期
STR_TO_DATE('2-18-2001', '%m-%d-%Y');
#date_formate: 将日期转换成字符
DATE_FORMAT('2001/2/18', '%Y年%m月%d日');
Y 四位年份 y 两位年份
m 01, 02... c 1, 2, 3... 月
d 01, 02... 日
H 24小时 h 12小时 时
i 00, 01, 02... 分
s 00, 01... 秒
其他函数:
SELECT VERSION();
SELECT DATABASE();
SELECT USER();
流程控制函数:
# if 函数: if else 的效果
SELECT IF(10 > 5, '对', '错');
SELECT last_name, commission_pct, IF(commission_pct IS NULL, '没奖金', '有奖金')
FROM employees;
# case 函数
#使用一: switch case 的效果
CASE 判断的字段或表达式
WHEN 常量1 THEN 显示的值1或语句1;
WHEN 常量2 THEN 显示的值2或表达式2;
...
ELSE 要显示的值n或语句n;
END
#案例: 查询员工的工资,要求:
/*
部门号=30, 显示工资为1.1倍
部门号=40,显示工资为1.2倍
其他部门显示原始工资
*/
SELECT salary 原始工资, department_id,
CASE department_id
WHEN 30 THEN salary * 1.1
WHEN 40 THEN salary * 1.2
ELSE salary
END AS 新工资
FROM employees;
#使用二: 类似于多重if
CASE
WHEN 条件一 THEN 显示的值1或语句1;
WHEN 条件二 THEN 显示的值2或语句2;
...
ELSE 要显示的值n或语句n;
END
#案例: 查询员工的工资,要求:
/*
工资大于等于20000, 显示A级别
工资小于于20000,显示B级别
*/
SELECT salary,
CASE
WHEN salary >= 20000 THEN 'A'
WHEN salary < 20000 THEN 'B'
END AS 工资级别
FROM employees;
分组函数:
做统计使用, 又称为统计函数, 聚合函数, 组函数
# sum 求和, avg 平均值, max 最大值, min 最小值, count 计算个数
SELECT SUM(salary) FROM employees;
SELECT AVG(salary) FROM employees;
SELECT SUM(salary) 和, ROUND(AVG(salary), 2) 平均 FROM employees;
# 参数支持类型:
SELECT SUM(last_name), AVG(last_name) FROM employees; # 结果为0, 虽然不报错, 但是没有意义 SUM: 只支持数值型,
# MAX, MIN : 支持字符型, 日期型
# COUNT: 都支持
# 以上函数均忽略null 值
#可以和DISTINCT 搭配
SELECT SUM(DISTINCT salary) FROM employees;
...
#count 函数 详细介绍
SELECT COUNT(*) FROM employees; # 统计所有行数(只要一行有一个不为null 就计数)
SELECT COUNT(1) FROM employees; # 统计所有行数
# 效率问题
/*
MYISAM 存储引擎下 COUNT(*)效率最高
INNODB 存储引擎下 都差不多, 但是比COUNT(字段) 高
一般用COUNT(*) 来统计行数!!!
*/
# 和分组函数一同查询的字段有限制
SELECT AVG(salary), employee_id FROM employees; # 要求查询的字段是GROUP BY后的字段
#查询员工最大入职时间和最小入职时间相差天数 (DATEDIFF 函数)
SELECT DATEDIFF (MAX(hiredate), MIN(hiredate)) FROM employees;
GROUP BY 子句语法
可以把表中的数据分成若干组
特点:
分组查询的条件分两类:
#引入: 查询每个部门的平均工资
/*
语法:
SELECT 分组函数, 列(要求写在GROUP BY 后面)
FROM 表
[WHERE 条件]
GROUP BY 分组列表
[ORDER BY 子句]
注意: 查询列表必须特殊, 要求是分组函数和 GROUP BY 后出现的字段
*/
#案例: 查询每个工种的最高工资
SELCET MAX(salary), job_id
FROM employees
GROUP BY job_id;
#案例: 查询每个位置上的部门个数
SELECT COUNT(*), location_id
FROM departments
GROUP BY location_id;
#案例: 查询邮箱中包含a字符的, 每个部门的平均工资
SELECT AVG(salary), department_id
FROM employees
WHERE email LIKE '%a%'
GROUP BY department_id;
---------------------------------------------------------
#分组后筛选
#案例: 查询哪个部门的员工部门的员工个数大于2 (分组后查询)
SELECT COUNT(*), department_id
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 2;
#查询每个工种员工的最高工资大于12000 的工种编号和最高工资
SELECT MAX(salary), job_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING MAX(salary) > 12000;
#案例3: 查询领导编号大于102的每个领导手下的员工最低工资大于5000的领导编号是谁, 以及其最低工资
SELECT MIN(salary), manager_id
FROM employees
WHERE manager_id > 102g
GROUP BY manager_id
HAVING MIN(salary) > 5000;
按函数或表达式分组
#案例: 按员工姓名的长度分组, 查询每一组员工个数, 筛选员工个数大于5的有哪些
SELECT COUNT(*), LENGTH(last_name) len_name
FROM employees
GROUP BY len_name # 支持别名
HAVING COUNT(*) > 5;
按多个字段分组
#案例: 查询每个部门每个工种的员工的平均工资
SELECT AVG(salary), department_id, job_id
FROM employees
GROUP BY department_id, job_id;
添加排序
#案例: 查询每个部门每个工种且部门编号不为null 的员工的平均工资, 并且按照平均工资的高低显示
SELECT AVG(salary), depatmen_id, job_id
FROM employees
WHERE department_id IS NOT NULL # 优先考虑分组前筛选
GROUP BY department_id, job_id
ORDER BY AVG(salary) DESC;
又称多表查询, 当查询的字段来自多个表时, 就会用到连接查询
笛卡尔现象:当表1 m 行 表2 n 行, 结果 = m * n 行
原因: 如果连接条件省略或无效则会出现
解决办法:添加上连接条件
分类:
等值连接
#案例: 查询女生对应的男生名
SELECT NAME, boyName
FROM boys, beauty
WHERE beauty.boyfriend_id = boys.id;
#案例: 查询员工名对应的部门名
SELECT last_name, department_name
FROM employees, departments
WHERE employees.department_id = departments.department_id;
# 查询员工名, 工种号, 工种名
# 给表起别名
SELECT last_name, e.job_id, job_title # 这里也要限定
FROM employees e, jobs j
WHERE e.job_id = j.job_id;
# 可以加筛选
#案例: 查询有奖金的员工名, 部门名
SELECT last_name, department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id
AND e.commission_pct IS NOT NULL;
# 可以加分组
#案例: 查询每个城市的部门个数
SELECT COUNT(*) 部门个数, city
FROM departments d, locations l
WHERE d.location_id = l.location_id;
GROUP BY city;
#可以加排序
...
#三表连接
# 查询员工名, 部门名和所在的城市, 并排序
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 department_name DESC;
注意: 如果为表取了别名, 则原来的字段不能用原来的表名去限定
非等值连接
#案例: 查询员工的工资和工资级别
SELECT salary, grade_level
FROM employees e, job_grades g
WHERE salary BETWEEN g.`lowerst_sal` AND g.`highest_sal`;
自连接
同一个表找多遍
#案例: 查询员工名和上级的名称
SELECT e.employee_id, e.last_name, m.emplpoyee_id, m.last_name
FROM employees e, employees m
WHERE e.employee_id = m.employee_id;
语法:
SELECT 查询列表
FROM 表1 别名
连接类型 JOIN 表2 别名
on 连接条件
where 筛选条件
GROUP BY
HAVING
ORDER BY
# 连接类型分类:
内连接: innner
外连接: 右外: right 左外: right 全外: full
交叉连接: cross
内连接:
语法:
SELECT 查询列表
FROM 表1 别名
INNER JOIN 表2 别名
ON 连接条件;
分类: 等值, 非等值, 自连接
#案例: 查询员工名, 部门名
SELECT last_name, depaartment_name
FROM employees e
INNER JOIN departments d
ON e.department_id = d.department_id;
#案例: 查询名字种包含e的员工名和工种名(添加筛选)
SELECT last_name, job_title
FROM employees e
INNER JOIN jobs j
ON e.job_id = j.job_id
WHERE e.last_name LIKE '%e%';
#案例: 查询部门个数大于3的城市名和部门个数
SELECT city, COUNT(*) 部门个数
FROM departments d
INNER JOIN lcoations l
ON d.location_id = l.location_id
GROUP BY city
HAVING COUNT(*) > 3;
#案例:查询那个部门员工个数大于3 的部门名和员工个数, 降序排序
SELCET COUNT(*), department_name
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;
# 多表连接
#案例: 查询员工名, 部门名, 工种名, 并按部门名降序
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;
特点: INNER 可以省略, 可读性强, 可以排序, 分组, 筛选, 和92连接效果一样
非等值连接
# 查询员工的工资级别
SELECT salary, grade_level
FROM employees e
JOIN job_grades g
ON e.salary BETWEEN g.lowset_sal AND g.highest_sal;
# 查询工资级别的个数大于20的个数, 并且按工资级别降序
SELECT COUNT(*), grade_level
FROM employees e
JOIN job_grades g
ON e.salary BETWEEN g.lowset_sal AND g.highest_sal
GROUP BY grade_level
HAVING COUNT(*) > 20
ORDER BY grade_level DESC;
自连接
# 查询员工名字以及上级的名字
SELECT e.last_name, m.last_name
FROM employees e
JOIN employees m
ON e.manager_id = m.employee_id;
外连接
应用场景: 查询一个表中有, 另一个表中没有的字段时
特点:
# 查询男朋友不在男神表的女神的名字
# 左外连接
SELECT b.name, bo.*
FROM beauty b
LEFT OUTER JOIN boys bo
ON b.boyfriend_id = bo.id
WHERE bo.id IS NULL;
# 右外连接
SELECT b.name, bo.*
FROM boys bo
RIGHT OUTER JOIN beauty b
ON b.boyfriend_id = bo.id
WHERE bo.id IS NULL;
# 查询哪个部门没有员工 左外
SELECT d.*, e.employee_id
FROM departments d
LEFT OUTER JOIN employees e
ON d.department_id = e.department_id
WHERE e.employee_id IS NULL;
全外连接
SELECT b.*, bo.*
FROM beauty b
FULL OUTER JOIN boys bo
ON b.boyfriend_id = bo.id;
最终效果:
会把交集查出来,
主表有的, 从表没有的查出来,
从表有的, 主表没有的查出来
交叉连接
结果是笛卡尔乘积
SELECT b.*, bo.*
FROM beauty b
CROSS JOIN boys bo;
建议用99 语法!!!
AB 集 中间一部分重合:
总结: 求交集用内连接
交集 + A 集 用左外连接 交集 + B 集用右外连接
其他加WHERE 条件可以继续求A集 - 交集
全部集就是用全外连接, 全集 - 交集用WHERE
出现在其他语句 (主要代表查询语句) 内部中的SELECT 语句, 称为子查询或内查询
出现在外部的查询语句, 称为主查询或外查询
示例:
SELECT first_name FROM employees WHERE
department_id IN (
SELECT department_id FROM departments
WHERE location_id = 1700
)
分类:
子查询出现的位置:
SELECT 后面 : 仅支持标量子查询
FROM 后面 : 支持表子查询
WHERE 或 HAVING 后面 : 标量子查询(单行), 列子查询(多行,列子查询搭配多行操作符使用: IN, ANY/SOME, ALL), 行子查询
EXISTS 后面 (相关子查询) : 表子查询
按结果集的行列数不同:
标量子查询 (结果集只有一行一列)
列子查询 (结果集只有一列多行)
行子查询 (结果集只有一行多列)
表子查询 (都是表子查询)
WHERE 或 HAVING 后:
特点:
标量子查询:
#案例: 查询job_id 与141号员工相同, salary 比141号员工高的员工姓名, id , 工资
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 = 141
);
#案例: 返回公司工资最少的员工的last_name, job_id 和salary
SELECT last_name, job_id, salary
FROM employees
WHERE salary = (
SELECT MIN(salary)
FROM employees
);
#案例: 查询最低工资大于50号部门最低工资的部门id 和其最低工资
SELECT MIN(salary), department_id
FROM employees
GROUP BY department_id
HAVING MIN(salary) > (
SELECT MIN(salary)
FROM employees
WHERE department_id = 50
);
特点: 子查询的执行优先于主查询
注意: 如果里面不是标量子查询, 就会报错
列子查询(多行子查询)
列子查询搭配多行操作符使用: IN / NOT IN(等于列表中任意一个), ANY / SOME(和子查询返回的某一个值比较), ALL(和子查询返回的所有值比较)
# 返回location_id 是1400 或1700 的部门的所有员工的姓名
SELECT last_name
FROM employees
WHERE department_id IN(
SELECT DISTINCT department_id
FROM departments
WHERE department_id IN(1400, 1700)
);
# 返回其他工种中比job_id 为'IT_PROG' 工种任一工资低的员工的员工号, 姓名, job_id 以及salary
SELECT last_name, employee_id, job_id, salary
FROM employees
WHERE salary < ANY(
SELECT DISTINCT salary
FROM employees
WHERE job_id = 'IT_PROG'
) AND job_id <> 'IT_PROG';
#也可以这样
SELECT last_name, employee_id, job_id, salary
FROM employees
WHERE salary < (
SELECT MAX(salary)
FROM employees
WHERE job_id = 'IT_PROG'
) AND job_id <> 'IT_PROG';
**行子查询(结果集一行多列或多行多列) **
略
SELECT 后面
#案例: 查询每个部门员工个数
SELECT d.*,(
SELECT COUNT(*)
FROM employees e
WHERE e.department_id = d.department_id
) 个数
FROM departments d;
# 查询员工号 = 102 的部门名
SELECT (
SELECT department_name
FROM departments d
INNER JOIN employees e
ON d.department_id = e.department_id
WHERE e.employee_id = 102
) 部门名;
特点: 只能返回的是一行一列, 只支持标量子查询
FROM 后面
# 查询每个部门的平均工资的工资等级
# 这张表必须起别名
# ②连接①的结果集和job_grades表, 筛选条件 平均工资 between lowest_sal and highest_sal
SELECT ag_dep.*, g.grade_level
FROM (
SELECT AVG(salary) ag, department_id
FROM employees
GROUP BY department_id
) ag_dep
INNER JOIN job_grades g
ON ag_dep.ag BETWEEN lowest_sal AND highest_sal;
exists后面(相关子查询)
# 语法:
exists (完整的查询语句)
# 结果: 1 或 0
SELECT EXISTS(SELECT emopyee_id FROM employees WHERE salary = 300000);
# 查询有员工的部门名
SELECT department_name
FROM departments
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
);
JOIN 后面也可加子表
例题:
# 查询各部门比本部门平均工资高的员工的员工号, 姓名, 工资
SELECT employee_id, salary, last_name, e.department_id
FROM employees e
INNER JOIN (
SELECT AVG(salary) ag, department_id
FROM employees
GROUP BY department_id
) ag_dep
on e.deparmtent_id = ag_dep.department_id
WHERE salary > ag_dep.ag;
# 查询名字中包含字母u的员工在相同部门的员工的姓名和员工工号
SELECT last_name, department_id
FROM employees
WHERE department_id IN(
SELECT DISTINCT department_id
FROM employees
WHERE last_name LIKE '%u%'
);
# 查询管理者为King 的员工姓名和工资
SELECT last_name, salary
FROM employees
WHERE manager_id IN (
SELECT employee_id
FROM empoyees
WHERE last_name = 'King'
);
# 查询工资最低的员工信息: last_name, salary
SELECT last_name, saalry
FROM employees
WHERE salary = (
SELECT MIN(salary)
FROM employees
);
# 查询平均工资最低的部门信息
SELECT *
FROM departments
WHERE department_id = (
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1;
);
# 查询平均工资最低的部门信息和平均工资
SELECT d.*, ag_dep.ag
FROM departments d
JOIN (
SELECT AVG(salary) ag, department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1
) ag_dep
ON d.department_id = ag_dep.department_id;
# 查询平均工资高于公司平均工资的部门有哪些
SELECT AVG(salary), department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) > (
SELECT AVG(salary)
FROM employees
);
# 查询平均工资最高的部门的manager 的详细信息: last_name department_id email
SELECT last_name, d.department_id, email
FROM employees e
INNER JOIN departments d
ON d.manager_id = e.employee_id
WHERE d.department_id = (
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary) DESC
LIMIT 1
);
应用场景: 当要显示的数据一页显示不全, 需要分页提交sql 请求
# 语法
SELECT 查询列表
FROM 表
[JOIN TYPE] JOIN 表2
WHERE 条件
GROUPE BY 分组字段
HAVING 分组后筛选条件
ODER BY 排序字段 ASC
LIMIT 起始索引, 条目数; # 这个地方索引从0 开始
# 查询前五条员工信息
SELECT * FROM employees LIMIT 0, 5;
SELECT * FROM employees LIMIT 5; # 如果是起始索引为0, 也可以省略
# 查询有奖金的员工信息, 并且工资高的在前面
SELECT * FROM employees WHERE commission_pct IS NOT NULL ORDER BY salary DESC LIMIT 10;
特点:
LIMIT 在执行和语法上都是最后
公式, page 为要显示的页数, size 为每一页的条目数:
SELECT 查询列表
FROM 表
LIMIT (page - 1) * size, size;
union : 将多条查询语句的结果合并成一个结果
应用场景:
查询所有中国男性的信息以及外国男性信息, 两个国家的人员信息存在两个表中
当我们查询的结果来自多个表, 但多个表没有直接的连接关系,
注意:
# 案例: 查询部门编号大于90 或邮箱包含a 的员工信息
SELECT * FROM employees WHERE email LIKE '%a%' OR department_id > 90;
# UNION
SELECT * FROM employees WHERE email LIKE '%a%'
UNION
SELECT * FROM employees WHERE department_id > 90;
数据操作语言: 插入(INSERT), 修改(UPDATE), 删除(DELETE)
插入方式一:
语法:
INSERT INTO 表名(列名, ...) VALUES(值1, ...);
**注意: **
插入方式二:
语法:
INSERT INTO 表名
SET 列名 = 值, 列名 = 值, ...;
区别:
方式一支持插入多行, 方式二不支持
INSERT INTO beauty
VALUES (13, ...), (17, ...) ...;
方式一支持子查询, 方式二不支持
INSERT INTO beauty(id, NAME, phone)
SELECT id, boyName, '123456' FROM boys WHERE id < 3;
修改单表记录
语法:
UPDATE 表名
SET 列 = 新值, 列 = 新值 ...
WHERE 筛选条件;
修改多表的记录
语法:
92:
UPDATE 表1 别名, 表2, 别名 SET 列 = 值 WHERE 连接条件 AND 筛选条件
99:
UPDATE 表1 别名
[JOIN TYPE] JOIN 表2 别名
ON 连接条件
SET 列 = 值, ...
WHERE 筛选条件
#案例: 修改张无忌的女朋友的手机号为123456789
UPDATE boys b
INNER JOIN beauty b
ON bo.`id` = b.`boyfriend_id`
SET b.`phone` = '123456789'
WHERE bo.`boyName` = '张无忌';
#案例: 修改没有男朋友的女神的男朋友的编号都为2 号
UPDATE boys b
INNER JOIN beauty b
ON bo.`id` = b.`boyfriend_id`
SET b.`boyfriend_id` = 2
WHERE bo.`id` IS NULL;
语法:
单表删除:
DELETE FROM 表名 WHERE 筛选条件;
多表删除:
99语法:
DELETE 表1的别名, 表2的别名 # 删除谁的记录这里就写谁的别名
FROM 表1 别名
[JOIN TYPE] JOIN 表2 别名
ON 连接条件
WHERE 筛选条件;
# 删除张无忌的女朋友的信息
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` = bo.`id`
WHERE bo.`boyName` = '黄晓明';
删掉整个表的数据:
TRUNCATE TABLE 表名; # 不能加 WHERE
区别:
数据定义语言
库和表的管理
库的管理: 创建, 修改, 删除
表的管理: 创建, 修改, 删除
创建: create
修改: alter
删除: drop
库的创建
语法:
CREATE DATABASE IF NOT EXISTS 库名;
库的修改
更改库的字符集
ALTER DATABASE 库名 CHARACTER SET utf-8;
库的删除
DROP DATABASE IF EXISTS 库名;
表的创建
语法:
CREATE TABLE 表名(
列名 列的类型[(长度) 约束],
列名 列的类型[(长度) 约束],
列名 列的类型[(长度) 约束]
);
案例:
# 创建book 表
CREATE TABLE book(
id INT,
bName VARCHAR(20),
price DOUBLE,
authorId INT,
publishDate DATETIME
);
# 创建作者表
CREATE TABLE EXISTS author(
id INT,
au_name, VARCHAR(20),
nation VARCHAR(10)
);
表的修改
修改列名, 表名, 类型或约数, 添加列, 删除列
ALTER TABLE 表名 add|drop|modify|change COLUMN 列名 列类型 约束
# 修改列名
ALTER TABLE book CHANGE COLUMN publishdate pubDate DATETIME; #COLUMN 可以省略
# 修改列的类型
ALTER TABLE book MODIFY COLUMN pubdate TIMESTAMP;
# 添加新列
ALTER TABLE author ADD COLUMN annual DOUBLE; # 添加年薪列
# 删除列
ALTER TABLE author DROP COLUMN annual; # 不能用IF EXISTS
# 修改表名
ALTER TABLE author RENAME TO book_author;
表的替换
DROP TABLE IF EXISTS book_author;
SHOW TABLES; # 查看所有表
DROP DATABSE IF EXISTS 旧库名;
CREATE DATABASE 新库名;
DROP TABLE IF EXISTS 旧表名;
CREATE TABLE 表名();
表的复制:
仅仅复制结构:
CREATE TABLE copy LIKE author;
结构 + 数据:
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;
例:
# 根据表 employees 创建 employees2
CREATE TABLE employees2 LIKE myemployees.employees;
数值型: 整形, 小数(定点数, 浮点数)
字符型: 较短文本(char, varchar), 较长文本(text, blob(较长的二进制数据))
日期型:
整数类型 | 字节 | 范围 |
---|---|---|
Tinyint | 1 | 有符号: -128 ~ 127 无符号: 0 ~ 255 |
Smallint | 2 | 有符号: -32768 ~ 32767 无符号: 0 ~ 65535 |
Mediumint | 3 | 有符号: -8388608 ~ 8388607 无符号: 0 ~ 1677215 |
Int, integer | 4 | 有符号: -2147483648 ~ 2147483647无符号: 0 ~ 4294967295 |
Bigint | 8 | 有符号: -9223372036854775808 ~ 9223372036854775807 无符号: 0 ~ 9223372036854775807 * 2 + 1 |
设置无符号类型
CREATE TABLE tab_int(
t1 UNSIGNED INT
);
浮点数类型 | 字节 | 范围 |
---|---|---|
float | 4 | ±1.75494351E-38 ~ ±3.402823466E+38 |
double | 8 | ±2.2250738585072014E-308 ~ ±1.7976931348623157E+308 |
定点数类型 | 字节 | 范围 |
DEC(M,D) DECIMAL(M,D) | M + 2 | 最大取值范围和double 相同, 给定decimal 的有效取值范围由M 和O 决定 |
特点:
原则: 所选择的类型越简单越好, 能保存的数值的类型越小越好
char, varchar
text, blob (JDBC 会讲如何保存二进制数据)
字符串类型 | 最多字符数 | 描述及存储需求 |
---|---|---|
char(M) M可以省略, 默认为1 | M | M为0 ~ 255 之间的整数 |
varchar(M) M 不可以省略 | M | M为0 ~ 65535 之间的整数 |
区别: char(M) 长度固定为M(哪怕只用1个长度), 更耗费空间
varchar(M) 长度根据你输入的定 但是效率比char 低
一个M 可以省略, 一个不行
BINARY, VARBINARY, 类似char 和 varchar, 不同的是它们包含二进制字符串而不包含非二进制字符串
ENUM 枚举类型, 要求插入的值必须属于列表指定的值之一. 如果列表成员为1 ~ 255 则需要一个字节存储, 如果为255 ~ 65535 则需要两个字节, 最多65535个成员
CREATE TABLE tab_char(
c1 ENUM('a', 'b', 'c')
);
INSERT INTO tab_char VALUES('a');
SET 集合类型: 和ENUM 类似, 可以保存0 ~ 64个成员. 区别为: SET 类型一次可以选取多个成员, 但ENUM 只能选一个, 根据成员个数不同, 占用的字节不同
成员数 | 字节数 |
---|---|
1 ~ 8 | 1 |
9 ~ 16 | 2 |
17 ~ 24 | 3 |
25 ~ 32 | 4 |
33 ~ 64 | 8 |
CREATE TABLE tab_set(
s1 SET('a', 'b', 'c')
);
INSERT INTO tab_set VALUES('a, b');
日期和时间类型 | 字节 | 最小值 | 最大值 |
---|---|---|---|
DATE | 4 | 1000-01-01 | 9999-12-31 |
DATETIME | 8 | 1000-01-01 00:00:00 | 9999-12-31 23:29:29 |
TIMESTAMP | 4 | 1970010108001 | 2038年的某个时刻 |
TIME | 3 | -838:59:59 | 838:59:59 |
YEAR | 1 | 1901 | 2155 |
区别:
1、Timestamp支持的时间范围较小,取值范围:19700101080001 ~ 2038年的某个时间, Datetime的取值范围:1000-1-1 ~ 9999-12-31
2、timestamp和实际时区有关,更能反映实际的日期,而datetime则只能反映出插入时的当地时区
3、timestamp的属性受Mysql版本和SQLMode的影响很大
CREATE TABLE tab_date(
t1 DATETIME,
t2 TIMESTAMP
);
INSERT INTO tab_date VALUES(NOW(), NOW());
SHOW VARIABLES LIKE 'time_zone'; # 查看时区?
SET time_zone = '+9:00'; # 设置时区
一种限制, 用于限制表中的数据, 为了保证表中数据的可靠性
分类:
NOT NULL 非空
DEFAULT 默认, 保证该字段有默认值
PRIMARY KEY 主键, 该字段的值具有唯一性, 且自动非空
组合主键: PRIMARY KEY(id, name) 代表id 和name 不能同时重复, 但不推荐
UNIQUE 唯一, 可以为空, 但一个表只能一个为空, 也可以组合唯一, 但是不推荐
CHECK 检查约束[mysql 不支持, 但是不报错]
FOREIGN KEY REFERENCES 外键, 用于限制两个表的关系, 用于保证该字段的值必须来自主表关联列的值 (在从表添加外表约束, 用于引用主表某列的值) 如工种编号, 专业编号等
添加约束的时机: 创建表时, 修改表时
分类: 列级约束: 六大约束语法都支持, 但外键约束没有效果
表级约束: 除了非空, 默认 其他都支持, 语法如下
CREATE TABLE info(
name VARCHAR(20) NOT NULL,
gender CHAR(1) CHECK(gender = '男' OR gender = '女'),
majorId INT FOREIGN KEY REFERENCES major(id), # 外键
age INT DEFAULT 18
);
CREATE TABLE major(
id INT PRIMARY KEY,
majorName VARCAHR(20)
);
DESC info;
SHOW INDEX FROM info;
#表级约束
DROP TABLE IF EXISTS info;
CREATE TABLE info(
id INT,
name VARCHAR(20),
gender CHAR(1),
age INT,
majorId INT,
CONSTRAINT pk PRIMARY KEY(id), # 主键
CONSTRAINT uq UNIQUE(seat), #唯一键
CONSTRAINT ck CHECK(gender = '男' OR gender = '女'),
CONSTRAINT fk_info_major FOREIGN KEY(majorId) REFERENCES major(id) # 外键
);
[CONSTRAINT 名字] 可以省略, 名字不可以重复
一般外键会写成表级约束, 其他列级约束, 约束可以同时加多个, 空格分开就行
修改表时添加约束:
CREATE TABLE info(
id INT,
name VARCHAR(20),
gender CHAR(1),
age INT,
majorId INT,
);
ALTER TABLE info MODIFY COLUMN name VARCHAR(20) NOT NULL;
ALTER TABLE info MODIFY COLUMN name VARCHAR(20); # 删除约束
ALTER TABLE info MODIFY COLUMN age INT DEFAULT 18;
# 也支持表级约束
ALTER TABLE info ADD PRIMARY KEY(id);
ALTER TABLE info ADD [CONSTRAINT fk_info_major] FOREIGN KEY(majorId) REFERENCES major(id) # 外键
...
删除约束
ALTER TABLE info MODIFY COLUMN name VARCHAR(20);
# 删除主键, 唯一
ALTER TABLE info DROP PRIMARY KEY;
ALTER TABLE info DROP INDEX seat; # SHOW INDEX FROM info 可以查看约束的名字, 这里seat 是默认的名字
#删除外键
ALTER TABLE info DROP FOREIGN KEY fk_info_major;
又称为自增长列, 可以不用手动的插入值, 系统提供默认的序列值(从1开始) AUTO_INCREMENT
创建表时设置标识列:
CREATE TABLE tab_identity(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20)
);
INSERT INTO tab_identity(name) VALUES(1, 'shit');
INSERT INTO tab_identity(id, name) VALUES(null, 'shit'); # 前面写了id 就写null
#设置变量来设置步长, 和起始值, mysql 中不能设置起始值
SHOW VARIABLES LIKE '%auto_increment%';
SET auto_increment_increment = 3;
# 但是如果想给改变起始值, 可以手动插入一个你想要的起始值, 后面的值会依次递增
修改表时设置删除标识列
ALTER TABLE tab_identity MODIFY COLUMN id INT PRIMARY KEY AUTO_INCREMENT;
ALTER TABLE tab_identity MODIFY COLUMN id INT; # 删除标识列, 但是主键仍然保留
Transaction Control Language 事务控制语言
事务: 一个或一组sql语句组成的一个执行单元, 每个sql语句相互依赖, 如果某个语句出现问题, 整个单元将回滚. 所有受影响的数据回到以前的状态. 这个执行单元要么全部执行, 要么全部不执行
1、概念:在mysql中的数据用各种不同的技术存储在文件(或内存)中。
2、通过**show engines;**来查看mysql支持的存储引擎。
3、 在mysql中用的最多的存储引擎有:innodb,myisam ,memory 等。其中innodb支持事务,而myisam、memory等不支持事务
事务的ACID(acid) 属性:
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
隔离性(Isolation)
事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
事务的创建
隐式事务: 事务没有明显的开启和结束的标记 : INSERT UPDATE DELETE 语句
显式事务: 事务有明显的开启和结束的标记 前提: 先设置自动提交功能为禁用
SET autocommit = 0; # 开启
SHOW VARIABLES LIKE 'autocommit';
START TRANSACTION; # 可以不写
# 编写sql 语句(SELECT INSERT UPDATE DELETE) DDL语言没有事务
# 转账
UPDATE account SET balance = 500 WHERE username = 'lmy';
UPDATE account SET balance = 1500 WHERE username = 'yy';
ROLLBACK; # 回滚事务
COMMIT; # 结束事务
# 演示savepoint 和 rooback 可以搭配
SET autocommit = 0;
SEART TRANSACTION;
DELETE FROM account WHERE id = 25;
SAVEPOINT a; # 设置保存点
DELETE FROM account WHERE id = 28;
ROLLBACK TO a; # 回滚到保存点
数据库的隔离级别
对于同时运行的多个事务, 当这些食物访问到数据库中相同的数据时, 如果没有采取必要的隔离机制, 会导致并发问题
数据库提供的4种隔离级别:
隔离级别 | 描述 |
---|---|
READ UNCOMMITTED(读未提交数据) | 允许事务读取未被其他事务提交的变更, 三个问题都会出现 |
READ COMMITED(读已提交数据) | 只允许事务读取已经被其他事务提交的变更, 可以避免脏读 , 但其他两个问题可能出现 |
REPEATABLE READ(可重复读) | 确保事务可以多次从一个字段读取相同的值, 在这个事务持续期间, 禁止其他事务对这个字段进行更新, 幻读的问题仍然存在 |
SERIALIZABLE(串行化) | 确保一个事务可以从一个表中读取相同的行, 在这个事务期间, 禁止其他的DML 语言操作, 可以避免所有问题, 但效率十分低下 |
Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE。 Oracle 默认的事务隔离级别为: READ COMMITED
Mysql 支持 4 种事务隔离级别. Mysql 默认的事务隔离级别为: REPEATABLE READ
每启动一个 mysql 程序, 就会获得一个单独的数据库连接. 每个数据库连接都有一个全局变量 @@tx_isolation, 表示当前的事务隔离级别.
DELETE 和 TRUNCATE 在事务使用时的区别
DELETE 可以回滚, TRUNCATE 不能回滚
# DELETE 可以回滚
SET autocommit = 0;
START TRANSACTION;
DELETE FROM account;
ROLLBACK;
#TRUNCATE 不能回滚
SET autocommit = 0;
START TRANSACTION;
TRUNCATE TABLE account;
ROLLBACK;
含义: 虚拟表, 和普通表一样使用, 通过表动态生成的数据, 只保存了sql 逻辑, 不保存查询结果
应用: 多个地方用到同样的查询结果, 该查询结果使用的sql语句较复杂
# 查询姓张的学生名和专业名
# 原始写法
SELECT name, majorName
FROM stuinfo s
INNER JOIN major m ON s.majorId = m.id;
WHERE s.name LIKE '张%';
# 视图
CREATE VIEW v
AS
SELECT name, majorName
FROM stuinfo s
INNER JOIN major m ON s.majorId = m.id;
# 使用视图后的写法
SELECT * FROM v WHERE name LIKE '张%';
创建视图:
CREATE VIEW 视图名
AS
查询语句;
修改视图:
CREATE OR REPLACE VIEW 视图名
AS
查询语句;
ALTER VIEW 视图名
AS
查询语句;
删除视图:
DROP VIEW 视图名1, 视图名2, ...;
查看视图
DESC 视图名;
SHOW CREATE VIEW 视图名; # 命令行用这个
例题:
# 创建一个视图, 要求查询部门的最高工资高于12000的部门信息
CREATE OR REPLACE VIEW v2
AS
SELECET MAX(salary) mx, department_id
FROM employees
GROUP BY department_id
HAVING MAX(salary) > 12000;
SELECT d.*, m.mx
FROM deparmtents d
JOING v2 m # 视图也可以取别名
ON m.department_id = d.deparmtnet_id;
视图的更新
更改视图的数据
1. 向视图插入数据
INSERT INTO 视图名 VALUES('yy', '[email protected]', 100); # 原始表也会有这样的数据
2. 修改
UPDATE 视图名 SET last_name = '张无忌' WHERE last_name = '张飞'; # 原始表也会改
2. 删除
DELETE FROM 视图名 WHERE last_name = '张无忌'; # 原始表也会删
所以一般会添加只读的权限, 不是所有视图可以更新, 一般也不会去更新视图, 以下不能更新:
视图和表的对比
分类:
系统变量
自定义变量
系统变量:
变量由系统提供, 属于服务器层面
语法: SESSION可以省略
1. 查看所有系统变量
SHOW SESSION VARIABLES; #会话 SESSION 可以省略
SHOW GLOBAL VARIABLES; #全局
2. 查看满足条件的部分系统变量
SHOW GLOBAL VARIABLES LIKE '%char%';
3. 查看指定系统变量
SELECT @@GLOBAL|SESSION.系统变量名;
4. 赋值
SET GLOBAL|SESSION 系统变量名 = 值;
SET @@GLOBAL|SESSION.系统变量名 = 值;
注意: 如果时全局需要加GLOBAL 如果是会话就可以不写, 全局变量跨连接有效, 不能跨重启
服务器每次启动会为所有全局变量赋初始值
会话变量仅仅针对于当前连接有效
自定义变量
用户变量:
作用域: 针对于当前连接有效, 和会话变量的作用域, 变量的类型为弱类型, 可以修改变量类型
# 声明必须初始化
SET @用户变量名 = 值;
SET @用户变量名 := 值; # 用这个
SELECT @用户变量名 := 值;
# 赋值
再用一次 SET 或者 SELECT
SELECT 字段 INTO @用户变量名 FROM 表; # 要求得到的是一个值
# 查看
SELECT @用户变量名;
局部变量:
作用域: 仅仅在局部有效:, 只在定义它的begin end 中有效
# 声明
DECLARE 变量名 类型;
DECLARE 变量名 类型 DEFAULT 值;
# 赋值
SET 局部变量名 = 值;
SET 局部变量名 := 值;
SELECT @局部变量名 := 值;
SELECT 字段 INTO 局部变量名 FROM 表; # 注意@ 的有无
#
SELECT 局部变量名;
对比:
作用域不同, 定义和使用位置不同, 语法也不同, 局部变量需要指明类型
SET @m = 1;
SET @n = 2;
SET @sum = @n + @m;
类似 JAVA 中的方法
存储过程: 一组预先编译好的sql 语句的集合, 理解为批处理语句
减少了编译次数且减少了和数据库服务器连接的次数, 提高了效率
创建:
参数列表包含三部分: 参数模式 参数名 参数类型
参数模式: IN: 参数可以作为输入, 也就是需要调用方传入值
OUT: 作为输出, 可以作为返回值
INOUT: 既可以做输入, 又可以输出, 既需要传入值, 又可以返回值
存储过程体的每个sql 语句必须加分号.
存储过程的结尾可以用 DELIMITER 重新设置
语法: DELEMITER 结束标记 : DELEMITER $
CREATE PROCEDURE 存储过程名(参数列表)
BEGIN
存储过程体(sql语句)
END
调用:
CALL 存储过程名(实参列表) 结束标记;
1. 空参
# 插五条到表中
DELIMITER $
CREATE PROCEDURE myp1()
BEGIN
INSERT INTO admin(username, `password`) VALUES('shit', '0000'), ('s', '0000'), ('ss', '0000'), ('sss', '0000'), ('ssss', '0000');
END $
2. IN 模式参数的存储过程
# 创建存储过程实现根据女神名来查询对应男朋友信息
CREATE PROCEDURE myp2(IN name VARCHAR(20))
BEGIN
SELECT bo.*
FROM boys bo
RIGHT JOIN beauty b ON bo.id = b.boyfriend_id # 右外连接, 考虑男朋友不存在
WHERE b.name = name;
END $
# 创建存储过程 实现 判断用户账号密码是否正确
CREATE PROCEDURE myp3(IN uersname 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 $
如果报string 错误 可能是字符集的问题
设置客户端的编码 set character_set_client=gbk
设置连接器编码 set character_set_connection=gbk
设置返回值编码 set character_set_results=gbk
如果client(客户端)、connection(连接器)、results(返回值)都设置成gbk的话,可以简写成set names gbk
带OUT 模式的存储过程
可以带多个返回值
# 根据女生名返回对应的男生名
CREATE PROCEDURE myp4(IN beautyName VARCAHR(20), OUT boyName VARCHAR(20))
BEGIN
SELECT bo.boyName INTO boyName
FROM boys bo
INNER JOIN beauty b ON bo.id = b.boyfriend_id
WHERE b.name = beautyName;
END $
# 调用
SET @bName$
CALL myp4('小昭', @bName)$
SELECT @bName$
# 根据女生名, 返回对应的男生名和男生魅力值
CREATE PROCEDURE myp6(IN beautyName VARCHAR(20), OUT boyName VARCHAR(20), OUT userCP INT)
BEGIN
SELECT bo.boyName, bo.uerCP INTO boyName, userCP
FROM boys bo
INNER JOIN beauty b ON bo.id = b.boyfriend_id
WHERE b.name = beautyName;
END $
# 调用
CALL myp6('小昭', @bName, @userCP)$
带 INOUT 模式的存储过程
# 传入a 和 b 两个值, 要求都翻倍并返回
CREATE PROCEDURE myp7(INOUT a INT, INOUT b INT)
BEGIN
SET a = a * 2;
SET b = b * 2;
END $
删除存储过程
DROP PROCEDURE 存储过程名; # 一次只能删除一个
查看存储结构
SHOW CREATE PROCEDURE 存储过程名;
# 实现传入一个日期, 格式化成xx年xx月xx日返回
CREATE PROCEDURE test_pro4(IN myDate DATETIME, OUT strDate VARCHAR(50))
BEGIN
SELECT DATE_FORMAT(myDate, '%y年%m月%d日') INTO strDate;
END $
# 创建存储过程或函数实现传入女神名称, 返回: 女神 and 男神 格式的字符串
CREATE PROCEDURE test(IN beautyName VARCHAR(20), OUT str VARCHAR(50))
BEGIN
SELECT CONCAT(beautyName, ' and ', IFNULL(boyName, 'null')) INTO str
FROM boys bo
RIGHT JOIN beauty b ON b.boyfriend_id = bo.id
WHERE b.name = beautyName;
END $
# 根据传入的条目数, 和起始索引, 查询beauty 表的记录
CREATE PROCEDURE test6( IN sIndex INT, IN size INT)
BEGIN
SELECT * FROM beauty LIMIT sIndex, size;
END $
类似 JAVA 中的方法
函数: 一组预先编译好的sql 语句的集合, 理解为批处理语句
区别: 有且仅有一个返回值, 存储过程适合做批量的插入, 更新,
函数适合做处理数据后返回的一个结果
函数的创建
CREATE FUNCTION 函数名(参数列表) RETURNS 返回类型
BEGIN
函数体
END
注意:
参数列表: 参数名 参数类型
函数体: 要求有return 语句, 如果return 语句没有放在最后, 也不会报错
要使用DELIMITER 设置结束标记
调用:
SELECT 函数名(参数列表)
# 返回公司员工 无参有返回
CREATE FUNCITON myf1() RETURNS INT
BEGIN
DECLARE c INT DEFAULT 0;
SELECT COUNT(*) INTO c
FROM employees;
RETURN C;
END $
SELECT myf1()$
# 根据员工名返回工资
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('Kochhar')$
# 根据部门名 返回部门的平均工资
CREATE FUNCITON 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
函数的查看和删除
# 查看
SHOW CREATE FUNCTION myf3;
# 删除
DROP FUNCTION myf3;
顺序结构: 顺序执行
分支结构: 从多条路径选一条执行
循环结构: 重复执行一段代码
# IF 函数 实现简单的双分支
SELECT IF(表达式1, 表达式2, 表达式3); # 如果表达式1 成立 返回表达式2的值
# case 结构
情况1: 类似switch 语句, 一般用于实现等值判断
CASE 变量|表达式|字段
WHEN 要判断的值 THEN 返回的值1 或语句1;
WHEN 要判断的值 THEN 返回的值2 或语句2;
...
ELSE 要返回的值n
END CASE;
情况2: 类似 java 中的多重IF 语句, 一般用于实现区间判断
CASE
WHEN 要判断的条件1 THEN 返回的值1 或语句1;
WHEN 要判断的条件2 THEN 返回的值2 或语句2;
...
ELSE 要返回的值n
END CASE;
特点: 可以作为表达式, 嵌套在其他语句中使用, BEGIN END 中或外面
可以作为独立语句使用, 只能放在BEGIN END 中
如果WHEN 中的条件成立, 则执行对应的THEN 后面的语句, 并且结束CASE. ELSE 可以省略, 省略了其他语句都不满足返回 NULL 可以结合AND, NOT NOT 使用
# 根据传人的成绩来显示等级, 90 - 100 A 80 - 90 B ...
CREATE PROCEDURE test_case(IN socre INT)
BEGIN
CASE
WHEN socre >= 90 THEN SELECT 'A';
WHEN socre >= 80 THEN SELECT 'B';
WHEN socre >= 70 THEN SELECT 'C';
ELSE SELECT 'C';
END CASE;
END $
IF 条件1 THEN 语句1;
ELSEIF 条件2 THEN 语句2;
ELSE 语句n;
END IF;
只能应用在BEGIN END 中
分类: WHILE, LOOP, REPEAT
循环控制: ITERATE 类似 continue
LEAVE 类似 break;
# while
[标签:] WHILE 循环条件 do
循环体;
END WHILE [标签]; # 写上了可以搭配循环控制用
# loop
[标签:] loop
循环体;
END LOOP [标签]; # 可以模拟死循环
# repeat
[标签:] repeat
循环体;
UNTIL 结束循环的条件
END REPEAT [标签];
# 根据次数插入admin表中多条记录
CREATE PROCEDURE pro_while1(IN c INT) # 没有返回值就用存储过程
BEGIN
DECLARE i INT DEFAULT 1;
a:WHILE i <= c DO
INSERT INTO admin(username, 'password') VALUES(CONCATE('ROSE', i), '123456');
SET i = i + 1;
END WHILE a;
END $
# 添加leave 语句, 当插入了20条则停止
CREATE PROCEDURE pro_while1(IN c INT) # 没有返回值就用存储过程
BEGIN
DECLARE i INT DEFAULT 1;
a:WHILE i <= c DO
INSERT INTO admin(username, 'password') VALUES(CONCATE('ROSE', i), '123456');
IF i == 20 THEN LEAVE a; # 退出循环
SET i = i + 1;
END WHILE a;
END $
# 只插入偶数次
CREATE PROCEDURE pro_while1(IN c INT) # 没有返回值就用存储过程
BEGIN
DECLARE i INT DEFAULT 0;
a:WHILE i <= c DO
INSERT INTO admin(username, 'password') VALUES(CONCATE('ROSE', i), '123456');
IF i % 2 <> 0 THEN ITERATE a; # 退出循环
SET i = i + 1;
END WHILE a;
END $
# 已知表stringcontent 其中字段id 自增长 content VARCHAR(20) 向表插入指定个数的随机字符串
CREATE TABLE stringcontent(
id INT PRIMARY KEY AUTO_INCREMENT,
content VARCHAR(20)
);
DELIMITER $
CREATE PROCEDURE test(IN c INT)
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE str VARCHAR(26) DEFAULT 'abcdefghijklmnopqrstuvwxyz';
DECLARE strIndex INT DEFAULT 1; # 起始索引
DECLARE len INT DEFAULT 1; # 截取长度
WHILE i <= c DO
SET len = FLOOR(RAND() * (26 - strIndex + 1) + 1); # 产生随机数, 代表截取长度
SET strIndex = FLOOR(RAND() * 26 + 1); # 随机整数 1 - 26
INSERT INTO stringcontent(content) VALUES (SUBSTR(str, strIndex, len));
SET i = i + 1;
END $