数据库MySQL学习笔记
学习链接:数据库 MySQL 视频教程全集
查看当前所有的数据库:show databases;
打开指定的库:use 库名
查看当前的所有表:show tables;
查看其他库的所有表:show tables from 库名;
创建表:
create table 表名(
列名 列类型,
列名 列类型,
…
);
查看表结构:desc 表名;
语法:
select 查询列表
from 表名;
特点:
注意:在进行查询操作之前要指定所有的库:use myemployees;
查询表中的单个字段:select last_name from employees;
查询表中的多个字段:select last_name, salary, email from employees;
查询表中的所有字段:select * from employees;
按F12进行格式化
着重号`用来区分是否是关键字或者字段
选中语句进行执行或F9
查询常量值:
select 100;
select ‘john’;
查询表达式:select 100*98;
查询函数:select version();
起别名:
方式1:
select 100%98 as 结果;
select last_name as 姓, first_name as 名 from employees;
方式2:
select last_name 姓, first_name 名 from employees;
如果别名有特殊符号要加双引号:
select salary as “out put” from employees;
去重:
查询员工表中涉及到的所有部门编号:select distinct department_id from employees;
+号的作用:
使用concat连接字符串:
查询员工的名和姓连接成一个字段,并显示为姓名:select concat(last_name,first_name) as 姓名 from employees;
ifnull函数检测是否为null,如果为null,则返回指定的值,否则返回原本的值:
select ifnull(commission_pct, 0) as 奖金率, commission_pct from employees;
isnull函数判断某字段或表达式是否为null,如果是,则返回1,否则返回0
语法:select 查询列表 from 表明 where 筛选条件;
分类:
按条件表达式筛选:
按逻辑表达式筛选:
模糊查询
like
between and
in
is null
按条件表达式筛选:
按逻辑表达式筛选:
模糊查询
like
一般和通配符搭配使用,可以判断字符型数值或数值型
通配符:
查询员工名中包含字符a的员工信息:
SELECT * FROM employees WHERE last_name LIKE '%a%';
查询员工名中第三个字符为e,第五个字符为a的员工名和工资:
SELECT last_name, salary FROM employees WHERE last_name LIKE '__n_l%';
查询员工名中第二个字符为_
的员工名:
SELECT last_name FROM employees WHERE last_name LIKE '_\_ %';
指定转义字符:
SELECT last_name FROM employees WHERE last_name LIKE '_$_%' ESCAPE '$';
between and
使用between and可以提高语句的简洁度;
包含临界值;
两个临界值不能替换顺序;
查询员工编号在100到120之间的员工信息:
SELECT * FROM employees WHERE employee_id >= 100 AND employee_id <= 120;
SELECT * FROM employees WHERE employee_id BETWEEN 100 AND 120;
in
含义:判断某字段的值是否属于in列表中的某一项
使用in提高语句简洁度
in列表的值类型必须一致或兼容
in相当于等于,所以不支持通配符(like才支持)
查询员工的工种编号是 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';
SELECT last_name, job_id FROM employees WHERE job_id IN ('IT_PROG', 'AD_VP', 'AD_PRES');
is null
用于判断null值
=或者<>不能用于判断null值
查询没有奖金的员工名和奖金率:
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;
安全等于 <=>
测试题
查询没有奖金,且工资小于18000的salary, last_name:
SELECT
salary,
last_name
FROM
employees
WHERE commission_pct IS NULL
AND salary < 18000;
查询employees表中,job_id不为‘IT’或者工资为12000的员工信息:
SELECT
*
FROM
employees
WHERE job_id <> 'IT'
OR salary = 12000 ;
查看部门表的结构:
DESC departments;
查询部门表中涉及到了哪些位置编号:
SELECT DISTINCT
location_id
FROM
departments ;
经典面试题:select * from employees;
和 select * from employees where commission_pct like ‘%%’ and last_name like ‘%%’;
结果是否一样?并说明原因:不一样!如果判断的字段中有null值,如果查询是select * from employees where commission_pct like ‘%%’ or last_name like ‘%%’ or ...;
把所有字段都or写齐了就一样了。
语法:
select 查询列表
from 表
【where 筛选条件】
order by 排序列表 【asc|desc】
asc代表的是升序,desc代表的是降序,如果不写,默认是升序
order by子句中可以支持单个字段、多个字段、表达式、函数、别名
order by子句一般是放在查询语句的最后面,但limit子句除外
查询员工的信息,要求工资从高到低排序:
SELECT
*
FROM
employees
ORDER BY salary DESC ;
从低到高是ASC(默认是ASC)
查询部门编号>=90的员工信息,按入职时间的先后进行排序:
SELECT
*
FROM
employees
WHERE department_id >= 90
ORDER BY hiredate ASC ;
按年薪的高低显示员工的信息和年薪【按表达式(别名)排序】
SELECT
*,
salary * 12 * (1+ IFNULL(commission_pct, 0)) AS 年薪
FROM
employees
ORDER BY 年薪 DESC ;
按姓名的长度显示员工的姓名和工资【按函数排序】
SELECT
LENGTH(last_name) AS 字节长度,
last_name,
salary
FROM
employees
ORDER BY 字节长度 DESC;
查询员工信息,要求先按工资排序,再按员工编号排序
SELECT
*
FROM
employees
ORDER BY salary ASC,
employee_id DESC ;
测试题
查询员工的姓名和部门号和年薪,按年薪降序,按姓名升序
SELECT
last_name,
department_id,
salary * 12 * (1+ IFNULL(commission_pct, 0)) AS 年薪
FROM
employees
ORDER BY 年薪 DESC,
last_name ASC ;
选择工资不在8000到17000的员工的姓名和工资,按工资降序
SELECT
last_name,
salary
FROM
employees
WHERE salary NOT BETWEEN 8000
AND 17000
ORDER BY salary DESC ;
查询邮箱中包含e的员工信息,并先按邮箱的字节数降序,再按部门号升序
SELECT
*
FROM
employees
WHERE email LIKE '%e%'
ORDER BY LENGTH(email) DESC,
department_id ASC ;
功能:类似于java中的方法,将一组逻辑语句
好处:
调用:select 函数名(实参列表) 【from 表】;
特点:
分类:
字符函数
length:获取参数值的字节个数
concat:拼接字符串
upper/lower:将字符串变成大写/小写
将姓变成大写,名变成小写,然后拼接:
SELECT
CONCAT(UPPER(last_name), LOWER(first_name)) AS 姓名
FROM
employees ;
substr/substring:截取字符串
注意:索引从1开始
截取从指定索引处后面所有字符
SELECT
SUBSTR(
'李莫愁爱上了陆展元',
6
) AS output ;
截取从指定索引处指定字符长度的字符
SELECT
SUBSTR(
'李莫愁爱上了陆展元',
1,
3
) output ;
案例:姓名中首字母大写,其他字符小写,然后用_拼接,显示出来:
SELECT
CONCAT(
UPPER(SUBSTR(last_name, 1, 1)),
'_',
LOWER(SUBSTR(last_name, 2))
) AS output
FROM
employees ;
instr:返回子串第一次出现的索引,如果找不到返回0
SELECT
INSTR(
'杨不悔爱上了殷六侠',
'殷六侠'
) AS output ;
trim:去掉字符串前后的空格或子串
SELECT
LENGTH(TRIM(' 张翠山 ')) AS output ;
SELECT
TRIM('a' FROM 'aaa张a翠aa山aaaaa') AS output ;
lpad:用指定的字符实现左填充指定长度
rpad:用指定的字符实现右填充指定长度
replace:替换,替换所有的子串
数学函数
日期函数
now:返回当前系统日期+时间
curdate:返回当前系统日期,不包含时间
curtime:返回当前时间,不包含日期
可以获取指定的部分,年、月、日、小时、分钟、秒
SELECT
YEAR(hiredate) 年
FROM
employees ;
str_to_date:将日期格式的字符转换成指定格式的日期
SELECT
STR_TO_DATE('1998-3-2', '%Y-%c-%d') AS output ;
查询入职日期为1992-4-3的员工信息
SELECT
*
FROM
employees
WHERE hiredate = STR_TO_DATE('4-3 1992', '%c-%d %Y') ;
date_format:将日期转换成字符串
SELECT
DATE_FORMAT(NOW(), '%y年%m月%d日)') AS output ;
查询有奖金的员工名和入职日期(xx月/xx日 xx年)
SELECT
last_name,
DATE_FORMAT(hiredate, '%m月/%d日 %y年') AS 入职日期
FROM
employees
WHERE commission_pct IS NOT NULL ;
datediff:返回两个日期相差的天数
monthname:以英文形式返回月
其他函数
SELECT VERSION(); 当前数据库服务器的版本
SELECT DATABASE(); 当前打开的数据库
SELECT USER(); 当前用户
password('字符'); 返回该字符的密码形式
md5('字符'); 也是加密的一种形式(MD5)
流程控制函数
if函数:if else的效果
SELECT
last_name,
commission_pct,
IF(
commission_pct IS NULL,
'没奖金,呵呵',
'有奖金,嘻嘻'
) 备注
FROM
employees ;
case函数的使用1:switch case的效果
语法:
case 要判断的字段或表达式
when 常量1 then 要显示的值1或语句1;
when 常量2 then 要显示的值2或语句2;
...
else 要显示的值n或语句n;
end
查询员工的工资,要求:
部门号=30,显示的工资为1.1倍
部门号=40,显示的工资为1.2倍
部门号=50,显示的工资为1.3倍
其他部门,显示的工资为原工资
SELECT
salary AS 原始工资,
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函数的使用2:类似于多重if
case
when 条件1 then 要显示的值1或语句1
when 条件2 then 要显示的值2或语句2
...
else 要显示的值n或语句n
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 工资级别
FROM
employees ;
测试题
显示系统时间(日期+时间)
SELECT NOW();
查询员工号,姓名,工资,以及工资提高20%后的结果(new salary)
SELECT
employee_id,
last_name,
salary,
salary * 1.2 AS "new salary"
FROM
employees ;
将员工的姓名按首字母排序,并写出姓名的长度(length)
SELECT
last_name,
LENGTH(last_name)
FROM
employees
ORDER BY SUBSTR(last_name, 1, 1) ;
做一个查询
SELECT
CONCAT(
last_name,
' earns ',
salary,
' monthly but wants ',
salary * 3
) AS "Dream Salary"
FROM
employees ;
case-when训练
SELECT
last_name,
job_id AS job,
CASE
job_id
WHEN 'AD_PRES'
THEN 'A'
WHEN 'ST_MAN'
THEN 'B'
WHEN 'IT_PROG'
THEN 'C'
WHEN 'SA_PRE'
THEN 'D'
WHEN 'ST_CLERK'
THEN 'E'
END AS Grade
FROM
employees
WHERE job_id = 'AD_PRES' ;
功能:用作统计使用,又称为聚合函数或统计函数或组函数
分类:sum 求和、avg 平均值、max 最大值、min 最小值、count 计数(非空)
SELECT SUM(salary) FROM employees;
特点
可以和distinct搭配实现去重的运算
SELECT
SUM(DISTINCT salary),
SUM(salary)
FROM
employees ;
SELECT
COUNT(DISTINCT salary),
COUNT(salary)
FROM
employees ;
count函数的单独介绍
效率
使用count(*) 统计一共有多少行
SELECT COUNT(salary) FROM employees;
SELECT COUNT(*) FROM employees;
SELECT COUNT(1) FROM employees;
和分组函数一同查询的字段有限制,要求是group by后的字段
训练题
查询公司员工工资的最大值,最小值,平均值,总和
SELECT
MAX(salary),
MIN(salary),
AVG(salary),
SUM(salary)
FROM
employees ;
查询员工表中的最大入职时间和最小入职时间的相差天数(difference)
SELECT
DATEDIFF(MAX(hiredate), MIN(hiredate)) DIFFERENCE
FROM
employees ;
查询部门编号为90的员工个数
SELECT
COUNT(*)
FROM
employees
WHERE department_id = 90 ;
语法:
select 分组函数,列(要求出现在group by的后面)
from 表
【where 筛选条件】
group by 分组的列表
【having 分组后的筛选】
【order by 子句】
注意:查询列表比较特殊,要求是分组函数和group by后出现的字段
特点:
分组查询中的筛选条件分为两类:
数据源 位置 关键字
分组前筛选 原始表 group by子句的前面 where
分组后筛选 分组后的结果集 group by子句的后面 having
分组函数做条件肯定是放在having子句中
能用分组前筛选的,就优先考虑使用分组前筛选
group by子句支持单个字段分组,多个字段分组(多个字段之间用逗号隔开没有顺序要求),表达式或函数(用得较少)
也可以添加排序(排序放在整个分组查询最后位置)
查询每个工种的最高工资
SELECT
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 ;
查询有奖金的每个领导手下员工的最高工资
SELECT
MAX(salary),
manager_id
FROM
employees
WHERE commission_pct IS NOT NULL
GROUP BY manager_id ;
查询那个部门的员工个数>2
查询每个部门的员工个数
SELECT
COUNT(*) AS 员工个数,
department_id
FROM
employees
GROUP BY department_id ;
根据上面的结果进行筛选,查询哪个部门的员工个数>2
SELECT
COUNT(*) AS 员工个数,
department_id
FROM
employees
GROUP BY department_id
HAVING 员工个数 > 2 ;
添加分组后的筛选用having,分组前的用where
查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
查询每个工种有奖金的员工的最高工资
SELECT
MAX(salary),
job_id
FROM
employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id ;
根据上面的结果继续筛选,最高工资>12000
SELECT
MAX(salary) AS 最高工资,
job_id
FROM
employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING 最高工资 > 12000 ;
查询领导编号>102的每个领导手下的最低工资>5000的领导编号是哪个,以及其最低工资
SELECT
MIN(salary) AS 最低工资,
manager_id
FROM
employees
WHERE manager_id > 102
GROUP BY manager_id
HAVING 最低工资 > 5000 ;
按表达式或函数分组
按员工姓名的长度分组,查询每一组的员工个数,筛选员工个数>5的有哪些
查询每个长度的员工个数
SELECT
COUNT(*) 员工个数,
LENGTH(last_name) 姓名长度
FROM
employees
GROUP BY 姓名长度 ;
添加筛选条件
SELECT
COUNT(*) 员工个数,
LENGTH(last_name) 姓名长度
FROM
employees
GROUP BY 姓名长度
HAVING 员工个数 > 5 ;
按多个字段分组
查询每个部门每个工种的员工的平均工资
SELECT
AVG(salary),
department_id,
job_id
FROM
employees
GROUP BY department_id,
job_id ;
添加排序
查询每个部门每个工种的员工的平均工资,并按平均工资的高低显示
SELECT
AVG(salary) AS 平均工资,
department_id,
job_id
FROM
employees
GROUP BY department_id,
job_id
ORDER BY 平均工资 DESC ;
练习题
查询各job_id的员工工资的最大值、最小值、平均值,总和,并按job_id升序
SELECT
MAX(salary),
MIN(salary),
AVG(salary),
SUM(salary),
job_id
FROM
employees
GROUP BY job_id
ORDER BY job_id ;
查询员工最高工资和最低工资的差距(DIFFERENCE)
SELECT
MAX(salary) - MIN(salary) AS DIFFERENCE
FROM
employees ;
查询各个管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内
SELECT
MIN(salary) AS 最低工资
FROM
employees
WHERE manager_id IS NOT NULL
GROUP BY manager_id
HAVING 最低工资 >= 6000 ;
查询所有部门的编号,员工数量和工资平均值,并按平均工资降序
SELECT
department_id,
COUNT(*) AS 员工数量,
AVG(salary) AS 工资平均值
FROM
employees
GROUP BY department_id
ORDER BY 工资平均值 DESC ;
查询具有各个job_id的员工人数
SELECT
COUNT(*),
job_id
FROM
employees
GROUP BY job_id ;
含义:又称多表查询,当查询的字段来自于多个表时,就会用到连接查询
笛卡尔乘积现象:表1有m行,表2有n行,结果=m*n
分类:
sql92标准
等值连接
多表等值连接的结果为多表的交集部分
n表连接,至少需要n-1个连接条件
多表的顺序没有要求
一般需要为表起别名
可以搭配前面介绍的所有子句使用,比如排序、分组、筛选
查询女神名和对应的男神名:
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,
e.`job_id`,
job_title
FROM
jobs j ,
employees e
WHERE e.`job_id` = j.`job_id` ;
可以加筛选
查询有奖金的员工名、部门名
SELECT
last_name,
department_name
FROM
employees AS e,
departments AS d
WHERE e.`department_id` = d.`department_id`
AND e.`commission_pct` IS NOT NULL ;
查询城市名中第二个字符为o的部门名和城市名
SELECT
department_name,
city
FROM
departments d,
locations l
WHERE d.`location_id` = l.`location_id`
AND city LIKE '_o%' ;
可以加分组
查询每个城市的部门个数
SELECT
COUNT(*) 个数,
city
FROM
departments d,
locations l
WHERE d.`location_id` = l.`location_id`
GROUP BY city ;
查询有将近的每个部门的部门名和部门的领导编号和该部门的最低工资
SELECT
department_name,
d.manager_id,
MIN(salary)
FROM
departments d,
employees e
WHERE d.`department_id` = e.`department_id`
AND commission_pct IS NOT NULL
GROUP BY department_name,
d.manager_id ;
可以加排序
查询每个工种的工种名和员工的个数,并且按员工个数降序
SELECT
job_title,
COUNT(*) AS 个数
FROM
employees e,
jobs j
WHERE e.`job_id` = j.`job_id`
GROUP BY job_title
ORDER BY 个数 DESC ;
可是实现三表连接:
查询员工名、部门名和所在的城市
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` ;
非等值连接
查询员工的工资和工资级别
SELECT
salary,
grade_level
FROM
employees e,
job_grades g
WHERE salary BETWEEN g.lowest_sal
AND g.highest_sal ;
自连接
查询 员工名和上级的名称
SELECT
e.employee_id,
e.last_name,
m.employee_id,
m.last_name
FROM
employees e,
employees m
WHERE e.`manager_id` = m.`employee_id` ;
测试题:
显示员工表的最大工资,工资平均值
SELECT
MAX(salary),
AVG(salary)
FROM
employees ;
查询员工表的employee_id,job_id,last_name,按department_id降序,salary升序
SELECT
employee_id,
job_id,
last_name
FROM
employees
ORDER BY department_id DESC,
salary ASC ;
查询员工表的job_id中包含a和e的,并且a在e的前面
SELECT
job_id
FROM
employees
WHERE job_id LIKE '%a%e%' ;
显示当前日期,以及去前后空格,截取子字符串的函数
select now();
select trim();
select substr(str, startIndex, [length])
sql99语法
语法:
select 查询列表
from 表1 别名 【连接类型】
join 表2 别名
on 连接条件
【where 筛选条件】
【group by 分组】
【having 筛选条件】
【order by 排序列表】
内连接(同上):连接类型是inner
外连接
交叉连接:cross
内连接:
语法:
select 查询列表
from 表1 别名
inner join 表2 别名
on 连接条件
…
分类:
等值连接
非等值连接
自连接
特点:
等值连接:
查询员工名、部门名
SELECT
last_name,
department_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 last_name LIKE "%e%" ;
查询部门个数>3的城市名和部门个数
SELECT
city,
COUNT(*) 部门个数
FROM
departments d
INNER JOIN locations l
ON d.`location_id` = l.`location_id`
GROUP BY city
HAVING 部门个数 > 3 ;
查询哪个部门的部门员工个数>3的部门名和员工个数,并按个数降序排序
SELECT
department_name,
COUNT(*) 员工个数
FROM
departments d
INNER JOIN employees e
ON d.`department_id` = e.`department_id`
GROUP BY d.`department_id`
HAVING 员工个数 > 3
ORDER BY 员工个数 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 d.`department_id` DESC ;
非等值连接
查询员工的工资级别
SELECT
salary,
grade_level
FROM
employees e
INNER JOIN job_grades g
ON e.`salary` BETWEEN g.`lowest_sal`
AND g.`highest_sal` ;
查询每个工资级别>20的个数,并且按工资级别降序
SELECT
COUNT(*),
grade_level
FROM
employees e
INNER JOIN job_grades g
ON e.`salary` BETWEEN g.`lowest_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
INNER JOIN employees m
ON e.`manager_id` = m.`employee_id` ;
查询姓名中包含字符k的员工的名字、上级的名字
SELECT
e.last_name,
m.last_name
FROM
employees e
INNER JOIN employees m
ON e.`manager_id` = m.`employee_id`
WHERE e.`last_name` LIKE "%k%" ;
外连接
应用场景:用于查询一个表中有,另一个表没有的记录
特点:
查询没有男朋友的女神名
SELECT
b.name,
bo.*
FROM
beauty b
LEFT JOIN boys bo
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
d.*,
e.employee_id
FROM
employees e
RIGHT OUTER JOIN departments d
ON d.`department_id` = e.`department_id`
WHERE e.`employee_id` IS NULL ;
全外连接
mysql不支持
案例:
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 ;
sql92 和 sql99 pk
练习:
查询编号>3的女神的男朋友信息,如果有则列出详细信息,如果没有,则用null填充
SELECT
a.id,
a.name,
b.*
FROM
beauty a
LEFT JOIN boys b
ON a.`boyfriend_id` = b.`id`
WHERE a.`id` > 3 ;
查询哪个城市没有部门
SELECT
city,
d.*
FROM
departments d
RIGHT JOIN locations l
ON d.location_id = l.location_id
WHERE d.department_id IS NULL ;
查询部门名为SAL或IT的员工信息
SELECT
d.`department_name`,
e.*
FROM
departments d
LEFT JOIN employees e
ON d.`department_id` = e.`department_id`
WHERE d.`department_name` = 'SAL'
OR d.`department_name` = 'IT' ;
含义:出现在其他语句中的select语句,称为子查询或内查询;外部的查询语句,称为主查询或外查询
嵌套在其他语句内部的select语句成为子查询或内查询
外面的语句可以是insert、update、delete、select等,一般select作为外面语句较多
外面如果为select语句,则此语句称为外查询或主查询
分类:
标量子查询(单行子查询)
列子查询(多行子查询)
行子查询(多列多行)
特点:
案例1:谁的工资比Abel高?
SELECT
salary
FROM
employees
WHERE last_name = 'Abel' ;
案例2:返回job_id与141号员工相同,salary比143员工多的员工,姓名,job_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 = 143) ;
案例3:返回公司工资最少的员工的last_name, job_id和salary
SELECT
last_name,
job_id,
salary
FROM
employees
WHERE salary =
(SELECT
MIN(salary)
FROM
employees) ;
案例4:查询最低工资大于50号部门的最低工资的部门id和其最低工资
SELECT
MIN(salary),
e.`department_id`
FROM
employees e
GROUP BY e.`department_id`
HAVING MIN(salary) >
(SELECT
MIN(salary)
FROM
employees
WHERE department_id = 50) ;
(多行子查询)
多行比较操作符:
IN/NOT IN:等于列表中的任意一个
ANY|SOME:和子查询返回的某一个值比较,用的较少
ALL:和子查询返回的所有值比较
案例1:返回location_id是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
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' ;
或者用max代替any
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' ;
或者用min代替all
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' ;
结果集一行多列或多行多列
案例1:查询员工编号最少并且工资最高的员工信息
SELECT
*
FROM
employees
WHERE (employee_id, salary) =
(SELECT
MIN(employee_id),
MAX(salary)
FROM
employees) ;
仅仅支持标量子查询
案例1:查询每个部门的员工个数
SELECT
d.*,
(SELECT
COUNT(*)
FROM
employees e
WHERE e.department_id = d.department_Id) 个数
FROM
departments d ;
案例2:查询员工号=102的部门名
SELECT
(SELECT
department_name
FROM
departments d
INNER JOIN employees e
ON d.department_id = e.department_id
WHERE e.employee_id = 102) 部门名 ;
将子查询结果充当一张表,要求必须起别名
案例1:查询每个部门的平均工资的工资等级
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 g.`lowest_sal`
AND g.`highest_sal` ;
相关子查询
语法:exists(完整的查询语句)
结果:1或0
案例1:查询有员工的部门名
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 e) ;
习题集
查询和zlotkey相同部门的员工姓名和工资
SELECT
last_name,
salary
FROM
employees
WHERE department_id =
(SELECT
department_id
FROM
employees e
WHERE e.`last_name` = 'Zlotkey') ;
查询工资比公司平均工资高的员工的员工号,姓名和工资
SELECT
employee_id,
last_name,
salary
FROM
employees e
WHERE e.`salary` >
(SELECT
AVG(salary)
FROM
employees) ;
查询各部门中工资比本部门平均工资高的员工的员工号,姓名和工资
SELECT
employee_id,
last_name,
salary
FROM
employees e
INNER JOIN
(SELECT
AVG(salary) ag,
department_id
FROM
employees
GROUP BY department_id) nt
ON nt.department_id = e.department_id
WHERE salary > ag ;
查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名
SELECT
employee_id,
last_name
FROM
employees
WHERE department_id IN
(SELECT DISTINCT
department_id
FROM
employees
WHERE last_name LIKE '%u%') ;
查询在部门的location_id为1700的部门工作的员工的员工号
SELECT
employee_id
FROM
employees
WHERE department_id IN
(SELECT DISTINCT
department_id
FROM
departments
WHERE location_id = 1700) ;
查询管理者是King的员工姓名和工资
SELECT
last_name,
salary
FROM
employees
WHERE manager_id IN
(SELECT
employee_id
FROM
employees
WHERE last_name = 'K_ing') ;
查询工资最高的员工的姓名,要求first_name和last_name显示为一列,列名为 姓.名
SELECT
CONCAT(nt.first_name, nt.last_name) "姓.名"
FROM
(SELECT
first_name,
last_name
FROM
employees
WHERE salary =
(SELECT
MAX(salary)
FROM
employees)) nt ;
应用场景:当要显示的数据,一页显示不全,需要分页提交sql请求
语法:
select 查询列表
from 表
【join type】 join 表2
on 连接条件
where 筛选条件
group by 分组字段
having 分组后的筛选
order by 排序的字段】
limit offset,size;
offset:要显示条目的起始索引(从0开始)
size:要显示的条目个数
特点:
limit语句放在查询语句的最后
公式:
要显示的页数page,每页的条目数size
select 查询列表
from 表
limit (page - 1)* size, size;
案例1:查询前5条员工信息
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 ;
经典案例1:
查询工资最低的员工信息:last_name, salary
SELECT
last_name,
salary
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) ASC
LIMIT 1) ;
查询平均工资最低的部门信息和该部门的平均工资
SELECT
d.*,
dd.ag
FROM
departments d
INNER JOIN
(SELECT
AVG(salary) ag,
department_id
FROM
employees
GROUP BY department_id
ORDER BY ag
LIMIT 1) dd
ON d.`department_id` = dd.department_id ;
查询平均工资最高的job信息
SELECT
*
FROM
jobs j
WHERE j.`job_id` =
(SELECT
job_id
FROM
employees
GROUP BY job_id
ORDER BY AVG(salary) DESC
LIMIT 1) ;
查询平均工资高于公司平均工资的部门有哪些
SELECT
AVG(salary) ag,
department_id
FROM
employees
GROUP BY department_id
HAVING ag >
(SELECT
AVG(salary)
FROM
employees) ;
查询出公司中所有manager的详细信息
SELECT
*
FROM
employees
WHERE employee_id IN
(SELECT DISTINCT
manager_id
FROM
employees
WHERE manager_id IS NOT NULL) ;
各个部门中,最高工资中,最低的那个部门的最低工资是多少
SELECT
MIN(salary)
FROM
employees
WHERE department_id =
(SELECT
department_id
FROM
employees
GROUP BY department_id
ORDER BY MAX(salary) ASC
LIMIT 1) ;
查询平均工资最高的部门的manager的详细信息
SELECT
last_name,
department_id,
email,
salary
FROM
employees
WHERE employee_id =
(SELECT DISTINCT
manager_id
FROM
employees
WHERE department_id =
(SELECT
department_id
FROM
employees
GROUP BY department_id
ORDER BY AVG(salary) DESC
LIMIT 1)
AND manager_id IS NOT NULL) ;
union:联合,合并,将多条查询语句的结果合并成一个结果
引入案例:查询部门编号>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;
语法:
查询语句1
union 【ALL】
查询语句2
union 【ALL】
…
应用场景:要查询的结果来自于多个表,且多个表没有直接的连接关系,但查询的信息一致
特点:
语法:
select 查询列表 7
from 表1 别名 1
连接类型 join 表2 2
on 连接条件 3
where 筛选 4
group by 分组列表 5
having 筛选 6
order by 排序列表 8
limit 排序列表 9
方式1:
语法:insert into 表名(列名,…) values(值1,…)
示例1:插入的值的类型要与列的类型一致或兼容
INSERT INTO beauty (
id,
NAME,
sex,
borndate,
phone,
photo,
boyfriend_id
)
VALUES
(
13,
'唐艺昕',
'女',
'1990-4-23',
'18934531234',
NULL,
2
);
示例2:不可以为null的列必须插入值。可以为null的列如何插入值?
方式1:字段的值写null
方式2:不写该字段
示例3:列的顺序是否可以调换
INSERT INTO beauty(NAME, sex, id, phone)
VALUES('蒋欣', '女', 16, '110');
示例4:列数和值的个数必须一致
示例5:可以省略列名,默认所有列,而且列的顺序和表中列的顺序一致
INSERT INTO beauty
VALUES(18, '李易峰', '男', NULL, '19', NULL, NULL);
方式2:
语法:insert into 表名 set 列名=值,列名=值,…
INSERT INTO beauty SET id = 19,
NAME = '刘涛',
phone = '999' ;
两种方式大pk
方式1支持插入多行,但是方式2不支持
INSERT INTO beauty
VALUES
(20, '李易峰', '男', NULL, '19', NULL, NULL),
(21, '李易峰', '男', NULL, '19', NULL, NULL),
(22, '李易峰', '男', NULL, '19', NULL, NULL);
方式1支持子查询,方式2不支持
INSERT INTO beauty(id, NAME, phone)
SELECT 26, '送钱', '12341234';
语法:
修改单表的记录
update 表名
set 列=新值,列=新值…
where 筛选条件;
修改多表的记录
sql92语法
update 表1 别名,表2 别名
set 列=值…
where 筛选条件
and 筛选条件;
sql99语法:
update 表1 别名
inner | left | right join 表2 别名
on 连接条件
set 列=值,…
where 筛选条件;
案例1:修改beauty表中姓唐的女神电话为109090909
UPDATE
beauty
SET
phone = '109090909'
WHERE NAME LIKE '唐%' ;
案例2:修改boys表中id号位2的名称为张飞,魅力值为10
UPDATE
boys
SET
boyname = '张飞',
usercp = 10
WHERE id = 2 ;
案例1:修改张无忌的女朋友的手机号为114
UPDATE
boys b
INNER JOIN beauty be
ON b.`id` = be.`boyfriend_id` SET be.`phone` = '114'
WHERE b.`boyName` = '张无忌' ;
案例2:修改没有男朋友的女神的男朋友编号都为 2号
UPDATE
boys b
RIGHT JOIN beauty be
ON b.`id` = be.`boyfriend_id` SET be.`boyfriend_id` = 2
WHERE be.`boyfriend_id` IS NULL ;
方式1:delete
语法
单表的删除
delete from 表名 where 筛选条件
多表的删除
sql92语法
delete 别名(要删哪个表就写哪个表的别名,都删就都写)
from 表1 别名,表2 别名
where 连接条件
and 筛选条件
limit 条目数;
sql99语法
delete 别名(要删哪个表就写哪个表的别名,都删就都写)
from 表1 别名
inner | left | right join 表2 别名 on 连接条件
where 筛选条件
limit 条目数;
案例1:删除手机号以9结尾的女神信息
DELETE
FROM
beauty
WHERE phone LIKE '%9' ;
案例2:删除张无忌的女朋友的信息
DELETE
be
FROM
beauty be
INNER JOIN boys b
ON b.`id` = be.`boyfriend_id`
WHERE b.`boyName` = '张无忌' ;
案例3:删除黄晓明的信息以及他女朋友的信息
DELETE
b,
be
FROM
beauty be
INNER JOIN boys b
ON b.`id` = be.`boyfriend_id`
WHERE b.`boyName` = '黄晓明' ;
方式2:truncate
语法
truncate table 表名;
truncate语句中不许加where
一删全删
TRUNCATE TABLE boyes ;
delete pk truncate
库的创建
语法:create database [if not exists] 库名 [character set 字符集名];
案例:创建库book
CREATE DATABASE IF NOT EXISTS books;
库的修改
修改库名的语句【已停用】
RENAME DATABASE books TO new_books;
更改库的字符集
ALTER DATABASE books CHARACTER SET gbk;
库的删除
DROP DATABASE IF EXISTS books;
表的创建
create table 【if not exists】 表名(
列名 列的类型【(长度) 约束】,
列名 列的类型【(长度) 约束】,
…
)
案例1:创建表 book
CREATE TABLE book (
id INT,
bname VARCHAR (20),
price DOUBLE,
authorid INT,
publishdate DATETIME
) ;
案例2:创建表author
CREATE TABLE author (
id INT,
au_name VARCHAR (20),
nation VARCHAR (10)
) ;
案例3:查看创建的表
DESC author;
表的修改
语法:alter table 表名 add | drop | modify | change column 列名 【列类型 约束】;
添加列:alter table 表名 add column 列名 类型 【first | after 字段名】;
修改列的类型或约束:alter table 表名 modify column 列名 新类型 【新约束】;
修改列名:alter table 表名 change column 旧列名 新列名 类型;
删除列:alter table 表名 drop column 列名;
修改表名:alter table 表名 rename 【to】 新表名;
修改列名
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 ;
表的删除
语法:drop table if exists 表名;
查看有哪些表:show tables;
if exists 只能在库,表的创建和删除的时候使用,列的操作不能使用。
通用的写法:
DROP DATABASE IF EXISTS 旧库名;
CREATE DATABASE 新库名;
DROP TABLE IF EXISTS 旧表名;
CREATE TABLE 表名();
表的复制
仅仅复制表的结构
CREATE TABLE copy LIKE book_author ;
复制表的结构+数据
CREATE TABLE copy2
SELECT
*
FROM
book_author ;
只复制部分数据
CREATE TABLE copy3
SELECT
id,
au_name
FROM
book_author
WHERE nation = '中国' ;
仅仅复制某些字段(部分结构):设置where不满足,那么就没有数据
CREATE TABLE copy4
SELECT
id,
au_name
FROM
book_author
WHERE 0 ;
习题集
创建表dept1
USE myemployees;
CREATE TABLE dept1 (id INT (7), NAME VARCHAR (25)) ;
将表departments中的数据插入新表dept2中
CREATE TABLE dept2
SELECT
department_id,
department_name
FROM
departments ;
创建表emp5
CREATE TABLE emp5 (
id INT (7),
first_name VARCHAR (25),
last_name VARCHAR (25),
dept_id INT (7)
) ;
将
last_name的长度修改为50
ALTER TABLE emp5 MODIFY COLUMN last_name VARCHAR(50);
根据表employees创建employee2
CREATE TABLE employee2 LIKE employees ;
删除表emp5
DROP TABLE IF EXISTS emp5;
将表empoyees2重命名为emp5
ALTER TABLE employee2
RENAME TO emp5 ;
在表dept和emp5中添加新列test_column,并检查所做的操作
ALTER TABLE emp5
ADD COLUMN test_column INT ;
DESC emp5;
直接删除表emp5中的列dept_id
ALTER TABLE emp5
DROP COLUMN dept_id ;
enum('男', '女')
set('a', 'b', 'c', 'd')
含义:一种限制,用于限制表中的数据,为了保证表中的数据的准确和可靠性
分类:六大约束
添加约束的时机:
约束添加的分类:
列级约束:六大约束语法上都支持,但外键约束没有效果
表级约束:除了非空、默认,其他的都支持
位置 | 支持的约束类型 | 是否可以起约束名 | |
---|---|---|---|
列级约束 | 列的后面 | 语法都支持,但外键没有效果 | 不可以 |
表级约束 | 所有列的下面 | 默认和非空不支持,其他支持 | 可以(主键没有效果) |
添加列级约束
CREATE DATABASE students;
USE students;
CREATE TABLE stuinfo (
id INT PRIMARY KEY,
stuname VARCHAR (20) NOT NULL, # 非空
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)
) ;
DESC stuinfo;
SHOW INDEX FROM stuinfo;
添加表级约束
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;
语法:在各个字段的最下面
【constraint 约束名】 约束类型(字段名)
除了非空、默认,其他的都支持
通用的写法:
CREATE TABLE IF NOT EXISTS stuinfo (
id INT PRIMARY KEY,
stuname VARCHAR (20) NOT NULL,
gender CHAR(1),
seat INT UNIQUE,
age INT DEFAULT 18,
majorID INT,
CONSTRAINT fk_stuinfo_major FOREIGN KEY (majorid) REFERENCES major (id)
) ;
表级约束pk列级约束
支持类型 | 可以起约束名与否 | |
---|---|---|
列级约束 | 除了外键 | 不可以 |
表级约束 | 除了非空和默认 | 可以,但对主键无效 |
列级约束可以在一个字段上追加多个,中间用空格隔开,没有顺序要求
主键和唯一的大对比
保证唯一性 是否允许为空 一个表中可以有多少个 是否允许组合
外键特点
要求在从表设置外键关系
从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求
主表的关联列必须是一个key(一般是主键或唯一)
插入数据时,先插入主表,再插入从表
删除数据时,先删除从表,再删除主表
方式1:级联删除
ALTER TABLE stuinfo ad CONSTRAINT fk_stu_major FOREIGN KEY(majorid) REFERENCES major(id) ON DELETE CASCADE;
删除的时候,主表和从表对应的行都删了。
方式2:级联置空
ALTER TABLE stuinfo ad CONSTRAINT fk_stu_major FOREIGN KEY(majorid) REFERENCES major(id) ON DELETE SET NULL;
删除的时候,主表对应的行被删除了,从表引入的地方变为空值null。
添加列级约束:alter table 表名 modify column 字段名 字段类型 新约束;
添加表级约束:alter table 表名 add 【constraint 约束名】 约束类型(字段名)【外键的引用】;
添加非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NOT NULL;
添加默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18;
添加主键
# 列级约束的写法
ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY;
# 表级约束的写法
ALTER TABLE stuinfo ADD PRIMARY KEY(id);
添加唯一
# 列级约束的写法
ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE;
# 表级约束的写法
ALTER TABLE stuinfo ADD UNIQUE(seat);
添加外键
ALTER TABLE (CONSTRAINT fk_stuinfo_major) stuinfo ADD FOREIGN KEY(majorid) REFERENCES major(id);
删除非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NULL;
删除默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT;
删除主键
ALTER TABLE stuinfo DROP PRIMARY KEY;
删除唯一
ALTER TABLE stuinfo DROP INDEX seat;
删除外键
ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major;
总结
非空
添加非空:alter table 表名 modify column 字段名 字段类型 not null;
删除非空:alter table 表名 modify column 字段名 字段类型;
默认
添加默认:alter table 表名 modify column 字段名 字段类型 default;
删除默认:alter table 表名 modify column 字段名 字段类型;
主键
添加主键:alter table 表名 add 【constraint 约束名】 primary key(字段名);
删除主键:alter table 表名 drop primary key;
唯一
添加唯一:alter table 表名 add 【constraint 约束名】unique(字段名);
删除唯一:alter table 表名 drop index 索引名;
外键
添加外键:alter table 表名 add 【constraint 约束名】foreign key(字段名) references 主表(被引用列);
删除唯一:alter table 表名 drop foreign key 约束名;
又称为自增长列
含义:可以不用手动的插入值,系统提供默认的序列值
特点:
创建表时设置表时列
create table 表(
字段名 字段类型 约束 auto_increment
)
CREATE TABLE tab_identity (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME varcahr (20)
) ;
设置表时列的步长
SHOW VARIABLES LIKE '%auto_increment%';
SET auto_increment_increment = 3;
设置表时列的起始值:想在什么地方更改自增长列的值,则手动插入值(不手动的时候,值是null)。
修改表时设置标识列
alter table 表 modify column 字段名 字段类型 约束 auto_increment
ALTER TABLE tab_identity MODIFY COLUMN id INT PRIMARY KEY AUTO_INCREMENT;
修改表时删除标识列
alter table 表 modify column 字段名 字段类型 约束
ALTER TABLE tab_identity MODIFY COLUMN id INT;
事务的含义:一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行。
存储引擎:在mysql中的数据用各种不同的技术存储在文件(或内存)中。
事务的ACID属性
事务的创建
隐式事务:事务没有明显的开启和结束的标记。比如insert、update、delete语句
显式事务:事务具有明显的开启和结束的标记
前提:必须先设置自动提交功能为禁用
SET autocommit=0;
步骤1:开启事务
SET autocommit=0;
START TRANSACTION;(可选)
步骤2:编写事务中的sql语句(select insert update delete,只有增删改查,不包括DDL语言)
语句1;
语句2;
…
步骤3:结束事务
commit;提交事务
rollback;回滚事务
savepoint 结点名:设置保存点
SET autocommit = 0 ;
START TRANSACTION;
DELETE FROM account WHERE id=25;
SAVEPOINT a;
DELETE FROM accound WHERE id=28;
ROLLBACK TO a;
delete和truncate在事务使用时的区别
SET autocommit = 0 ;
START TRANSACTION;
DELETE FROM account;
ROLLBACK;
SET autocommit = 0 ;
START TRANSACTION;
TRUNCATE TABLE account;
ROLLBACK;
数据库的隔离级别
含义:虚拟表,和普通表一样使用。并且使用视图时动态生成的,值保存了sql逻辑,不保存成查询结果。
mysql5.1版本出现的新特性,是通过表动态生成的数据
应用场景:
案例1:查询姓张的学生名和专业名
# 普通写法
SELECT
stuname,
majorname
FROM
suinfo s
INNER JOIN major m
ON s.majorid = m.`id`
WHERE s.stuname LIKE '张%' ;
# 视图写法
CREATE VIEW v1 AS
SELECT
stuname,
majorname
FROM
suinfo s
INNER JOIN major m
ON s.majorid = m.`id` ;
SELECT
*
FROM
v1
WHERE stuname LIKE '张%' ;
创建视图
语法:
create view 视图名 as 查询语句;
视图的好处:
练习题:
习题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:查询平均工资最低的部门信息
# 用习题2的视图myv2
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` ;
视图的修改
删除视图
查看视图
语法:
show create view 视图名;
desc 视图名;
案例1:创建一个视图emp_v1,要求查询电话号码以‘011’开头的员工姓名和工资、邮箱
CREATE OR REPLACE VIEW emp_v1 AS
SELECT
last_name,
salary,
email
FROM
employees
WHERE phone_number LIKE '011%' ;
案例2:创建视图emp_v2,要求查询部门的最高工资高于12000的部门信息
CREATE OR REPLACE VIEW emp_v2 AS
SELECT
MAX(salary) mx,
department_id
FROM
employees
GROUP BY department_id
HAVING MAX(salary) > 12000 ;
SELECT
*
FROM
emp_v2 ;
SELECT
d.*,
m.mx
FROM
departments d
JOIN emp_v2 m
ON m.department_id = d.`department_id` ;
视图的更新
增、删、改(视图基于的表也会发生更改)
CREATE OR REPLACE VIEW myv1 AS
SELECT
last_name,
email
FROM
employees;
SELECT * FROM myv1;
# 插入
INSERT INTO myv1 VALUES('张飞', 'zhangfei');
# 修改
UPDATE myv1 SET last_name='张无忌' WHERE last_name = '张飞';
# 删除
DELETE FROM myv1 WHERE last_name='张无忌';
视图的可更新性和视图中查询的定义有关系,以下类型的视图是不能更新的。(注意:视图一般用于查询,而不是更新。)
视图和表的对比
创建语法的关键字 | 是否实际占用物理空间 | 使用 | |
---|---|---|---|
视图 | create view | 只是保存了sql逻辑 | 增删改查,一般不能增删改 |
表 | create table | 保存了实际的数据 | 增删改查 |
测试题
题1:创建表
CREATE TABLE book (
bid INT PRIMARY KEY,
bname VARCHAR (20) UNIQUE nut NULL,
price FLOAT DEFAULT 10,
btypeid INT,
FOREIGN KEY (btypeid) REFERENCES booktype (id)
) ;
题2:开启事务,向表中插入1行数据,并结束
SET autocommit = 0 ;
INSERT INTO book(bid, bname, price, btypeid)
VALUES(1, '小李飞刀', 100, 1);
COMMIT;
题3:创建视图,实现查询价格大于100的书名和类型名
CREATE VIEW myv1 AS
SELECT
bname,
NAME
FROM
book b
INNER JOIN booktype t
ON b.btypeid = t.id
WHERE price > 100 ;
题4:修改视图,实现查询价格在90-120之间的书名和价格
CREATE OR REPLACE VIEW myv1 AS
SELECT
bname,
price
FROM
book
WHERE price BETWEEN 90
AND 120 ;
题5:删除刚才创建的视图
DROP VIEW myv1;
系统变量
说明:变量由系统提供,不是用户定义,属于服务器层面
注意:如果是全局级别,则需要加global;如果是会话级别,则需要加session;如果不写,则默认session
使用的语法:
查看所有的系统变量
SHOW GLOBAL|【SESSION】 VARIABLES;
查看满足条件的部分系统变量
SHOW GLOBAL|【SESSION】 VARIABLES LIKE '%char%';
查看指定的某个系统变量的值
SELECT @@GLOBAL|【SESSION】.系统变量名;
为某个系统变量赋值
方式一
set GLOBAL|【SESSION】 系统变量名 = 值;
方式二
set @@GLOBAL|【SESSION】.系统变量名 = 值;
分类:
全局变量
服务器层面上的,必须拥有super权限才能为系统变量赋值。
作用域:服务器每次启动将为所有的全局变量赋初始值,针对于所有的会话(连接)有效,但不能跨重启。
查看所有的全局变量
SHOW GLOBAL VARIABLES;
- 查看部分的全局变量
SHOW GLOBAL VARIABLES LIKE ‘%char%’;
- 查看指定的全局变量的值
SELECT @@global.autocommit;
SELECT @@global.tx_isolation;
- 为某个指定的全局变量赋值
- 方式1:
```
set global autocommit=0;
```
- 方式2:
```
SET @@global.autocommit=0;
```
会话变量
服务器为每一个连接的客户端都提供了系统变量。
作用域:仅仅针对于当前会话(连接)有效。
查看所有的会话变量
SHOW 【SESSION】 VARIABLES;
```
查看部分的会话变量
SHOW 【SESSION】 VARIABLES LIKE ‘%char%’;
```
查看指定的某个会话变量
SELECT @@【SESSION.】autocommit;
```
为某个会话变量赋值
方式1:
set session autocommit=0;
```
- 方式2:
```
SET @@【session.】autocommit=0;
```
自定义变量
变量是用户自定义的,不是由系统定义的
使用步骤:声明 赋值 使用(查看、比较、运算等)
分类
用户变量
声明并初始化(三种方式)
set @用户变量名=值;
set @用户变量名:=值;(推荐)
select @用户变量名:=值;
赋值(更新用户变量的值)
方式1:通过set或select(同上)
set @用户变量名=值;
set @用户变量名:=值;(推荐)
select @用户变量名:=值;
案例1:
SET @name='John';
SET @name=100;
方式2:通过select into
select 字段 into 变量名
from 表;
案例1:
SELECT
COUNT(*) INTO @count
FROM
employees ;
使用(查看用户变量的值)
select @用户变量名;
局部变量
声明
declare 变量名 类型;
declare 变量名 类型 default 值;
赋值
方式1:通过set或select(同上)
set 局部变量名=值;
set 局部变量名:=值;(推荐)
select @局部变量名:=值;
方式2:通过select into
select 字段 into 局部变量名
from 表;
使用
select 局部变量名;
对比用户变量和局部变量:
作用域 | 定义和使用的位置 | 语法 | |
---|---|---|---|
用户变量 | 当前会话 | 会话中的任何地方 | 必须加@符号,不用限定类型 |
局部变量 | begin end中 | 只能在begin end中,且为第一句话 | 一般不用加@符号,需要限定类型 |
案例1:声明两个变量并赋初始值,求和,并打印
用户变量
SET @m=1;
SET @n=2;
SET @sum=@m+@n;
SELECT @sum;
局部变量
# 报错
DECLARE m INT DEFAULT 1;
DECLARE n INT DEFAULT 2;
DECLARE SUM INT;
SET SUM=m+n;
SELECT SUM;
含义:一组预先编译好的sql语句的集合,理解成批处理语句
好处:
语法:
创建语法
CREATE PROCEDURE 存储过程名(参数列表)
BEGIN
存储过程体(一组合法的SQL语句)
END
注意
参数列表包含三部分:参数模式,参数名,参数类型
如果存储过程体仅仅只有一句话,begin end可以省略
存储过程体中的每条SQL语句的结尾要求必须加分号
存储过程的结尾可以使用 DELIMITER 重新设置
语法:DELIMITER 结束标记
案例:
DELIMITER $
调用语法
空参列表
案例1:插入到admin表中五条记录
SELECT * FROM admin;
DELIMITER $
CREATE PROCEDURE myp1()
BEGIN
INSERT INTO admin(username, `password`)
VALUES
('john1', '0000'),
('asd', '0000'),
('joqqhn1', '0000'),
('qa', '0000'),
('ww', '0000');
END $
CALL myp1()$
创建带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('柳岩')$
CALL myp2('王语嫣')$
案例2:创建存储过程实现,用户是否登录成功
CREATE PROCEDURE myp4(IN username VARCHAR(20), IN passward 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 myp4('张飞', '8888')$
创建带out模式的存储过程
案例1:根据女神名,返回对应的男神名
CREATE PROCEDURE myp5(IN beautyname VARCHAR(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 $
CALL myp5('王语嫣', @bname)$
SELECT @bname$
案例2:根据女神名,返回对应的男神名和男神魅力值
CREATE PROCEDURE myp6(IN beautyname VARCHAR(20), OUT boyname VARCHAR(20), OUT usercp INT)
BEGIN
SELECT bo.boyname, bo.usercp 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)$
SELECT @bname, @usercp$
创建带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$
练习题
习题1:创建存储过程实现传入用户名和密码,插入到admin表中
CREATE PROCEDURE test_pro1(IN username VARCHAR(20), IN loginpwd VARCHAR(20))
BEGIN
INSERT INTO admin(admin.`username`, PASSWORD)
VALUES(username, loginpwd);
END $
CALL test_pro1('litian', '1234')$
SELECT * FROM admin$
习题2:创建存储过程实现传入女神编号,返回女神名称和女神电话
CREATE PROCEDURE test_pro2(IN id INT, OUT NAME VARCHAR(20), OUT phone VARCHAR(20))
BEGIN
SELECT b.name, b.phone INTO NAME, phone
FROM beauty b
WHERE b.id=id;
END $
SET @n=''$
SET @m=''$
CALL test_pro2(1, @n, @m)$
SELECT @m,@n$
习题3:创建存储过程来实现传入两个女神生日,返回大小
CREATE PROCEDURE test_pro3(IN birth1 DATETIME, IN birth2 DATETIME, OUT result INT)
BEGIN
SELECT DATEDIFF(birth1, birth2) INTO result;
END $
CALL test_pro3('1990-2-3', NOW(), @result)$
SELECT @result$
存储过程的删除
语法:drop procedure 存储过程名
DROP PROCEDURE myp1;
不能同时删除多个存储过程
查看存储过程的信息
语法:show create procedure 存储过程名
SHOW CREATE PROCEDURE myp2;
不能修改存储过程中的语句,需要修改的话,就删了重写。
练习题
练习题1:创建存储过程实现传入一个日期,格式化成xx年xx月xx日并返回
CREATE PROCEDURE test_pro4(IN mydate DATETIME, OUT strdate VARCHAR(20))
BEGIN
SELECT DATE_FORMAT(mydate, '%y年%m月%d天') INTO strdate;
END $
CALL test_pro4(NOW(), @str)$
SELECT @str $
练习题2:创建存储过程实现传入女神名称,返回:女神 and 男神 格式的字符串
CREATE PROCEDURE test_pro5(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 $
CALL test_pro5('王语嫣', @result)$
SELECT @result$
练习题3:创建存储过程,根据传入的起始索引和条目数,查询beauty表的记录
CREATE PROCEDURE test_pro6(IN startindex INT, IN size INT)
BEGIN
SELECT * FROM beauty LIMIT startindex, size;
END $
CALL test_pro6(3, 3)$
好处:
与存储过程的区别:
创建语法:
create function 函数名(参数列表) returns 返回类型
begin
函数体
end
调用语法:select 函数名(参数列表)
无参有返回
案例1:返回公司的员工个数
CREATE FUNCTION myf1() RETURNS INT
BEGIN
DECLARE c INT DEFAULT 0;# 定义变量
SELECT COUNT(*) INTO c# 赋值
FROM employees;
RETURN c;
END $
SELECT myf1()$
有参有返回
案例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('kochhar')$
案例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;
案例1:创建函数,实现传入两个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函数
功能:实现简单的双分支
语法:
select if(表达式1,表达式2,表达式3)
执行顺序:
如果表达式1成立,则if函数返回表达式2的值,否则返回表达式3的值
应用环境:任何地方
case结构
情况1:类似于java中的switch语句,一般用于实现等值判断
语法:
case 变量|表达式|字段
when 要判断的值 then 返回的值1或语句1;
when 要判断的值 then 返回的值2或语句2;
…
else 要返回的值n或语句n;
end case;
情况2:类似于java中的多重if语句,一般用于实现区间判断
语法:
case
when 要判断的条件1 then 返回的值1或语句1;
when 要判断的条件2 then 返回的值2或语句2;
…
else 要返回的值n或语句n;
end case;
特点:
案例1:创建存储过程,根据传入的成绩,来显示等级,比如传入的成绩:90-100,显示A;80-90:显示B;60-80:显示C;否则显示D
CREATE PROCEDURE test_case(IN score INT)
BEGIN
CASE
WHEN score >= 90 AND score <= 100 THEN SELECT 'A';
WHEN score >=80 THEN SELECT 'B';
WHEN score >= 60 THEN SELECT 'C';
ELSE SELECT 'D';
END CASE;
END $
CALL test_case(95)$
if结构
功能:实现多重分支
语法:
if 条件1 then 语句1;
elseif 条件2 then 语句2;
…
【else 语句n;】
end if;
应用场合:应用在begin end中
案例2:创建存储过程,根据传入的成绩,来返回等级,比如传入的成绩:90-100,返回A;80-90:返回B;60-80:返回C;否则返回D
CREATE FUNCTION test_if(score INT) RETURNS CHAR
BEGIN
IF score >= 90 AND score <= 100 THEN RETURN 'A';
ELSEIF score >=80 THEN RETURN 'B';
ELSEIF score >= 60 THEN RETURN 'C';
ELSE RETURN 'D';
END IF;
END $
SELECT test_if(86)$
循环结构
分类:while、loop、repeat
循环控制:
while
语法:
[标签:】while 循环条件 do
循环体;
end while 【标签】;
loop
语法:
【标签:】loop
循环体;
end loop 【标签】;
可以用来模拟简单的死循环
repeat
语法:
【标签:】repeat
循环体;
until 结束循环的条件
end repeat【标签】;
对比:
案例1:没有添加循环控制语句
批量插入,根据次数插入到admin表中多条记录
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)$
SELECT * FROM admin;
案例2:添加leave语句
批量插入,根据次数插入到admin表中20条记录
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), '000');
IF i>=20 THEN LEAVE a;
END IF;
SET i=i+1;
END WHILE a;
END $
CALL test_while1(100)$
SELECT * FROM admin;
案例3:添加iterate语句
批量插入,根据次数插入到admin表中多条记录,只插入偶数次
CREATE PROCEDURE test_while2(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), '000');
END WHILE a;
END $
CALL test_while2(100)$
SELECT * FROM admin;
案例4:已知表stringcontent,其中字段id 自增长;content varchar(20),向该表中插入指定个数的随机的字符串
USE test;
DROP TABLE IF EXISTS stringcontent;
CREATE TABLE stringcontent(
id INT PRIMARY KEY AUTO_INCREMENT,
content VARCHAR(20)
);
DELIMITER $
CREATE PROCEDURE test_randstr_insert(IN insertcount INT)
BEGIN
DECLARE i INT DEFAULT 1;# 定义一个循环变量i,表示插入次数
DECLARE str VARCHAR(26) DEFAULT 'abcdefghijklmnopqrstuvwxyz';
DECLARE startindex INT DEFAULT 1;# 代表起始索引
DECLARE len INT DEFAULT 1;# 代表截取的字符的长度
WHILE i<=insertcount DO
SET len=FLOOR(RAND()*(20-startindex+1)+1);# 产生一个随机的整数,代表截取长度,1-(26-startindex+1)
SET startindex=FLOOR(RAND()*26+1);# 产生一个随机的整数,代表起始索引1-26
INSERT INTO stringcontent(content) VALUES(SUBSTR(str, startindex, len));
SET i=i+1;
END WHILE;
END $
CALL test_randstr_insert(10)$
SELECT * FROM stringcontent;
CALL test_randstr_insert(10)$
SELECT * FROM stringcontent;
我的CSDN:https://blog.csdn.net/qq_21579045
我的博客园:https://www.cnblogs.com/lyjun/
我的Github:https://github.com/TinyHandsome
纸上得来终觉浅,绝知此事要躬行~
欢迎大家过来OB~
by 李英俊小朋友