- 0 引用
- 1 DQL
- 案例:查询员工名和姓连接成一个字段,并显示为姓名
SELECT CONCAT(last_name,first_name) AS 姓名FROM employees;
#案例:显示出表employees的两列,各个列之间用逗号连接,列头显示成OUT
SELECT CONCAT(employee_id
, ‘,’,first_name
) AS OUT FROM employees;
#特例:在拼接的字段commission_pct中部分值存在NULL,则这部分拼接结果是NULL
SELECT CONCAT(id
, ‘,’,commission_pct
) AS OUT_PUT FROM employees; - 解决NULL:使用IFNULL(expr1,expr2)分情况讨论,字段expr1不为NULL的部分显示原来值,为NULL部分显示expr2。
SELECT IFNULL(commission_pct,0) AS 奖金率, commission_pct FROM employees;
SELECT CONCAT(id
, ‘,’, IFNULL(commission_pct,0)) AS OUT FROM employees;
#查询员工号176的员工的年薪:
SELECT salary12(1+ IFNULL(commission_pct,0)) AS 年薪FROM employees; - 案例:查询员工名,并且按名字的长度降序
SELECT LENGTH(last_name) AS 字节长度,last_name FROM employees
ORDER BY LENGTH(last_name) DESC; #按函数排序
注:按名字长度即字节长度:length(name)
按名字即名字的字母顺序:name - 案例:查询员工的工资和工资级别
SELECT last_name,salary,grade_level
FROM employees e,job_grades g
WHERE salary BETWEEN g.lowest_sal AND g.highest_sal
GROUP BY employee_id; - 案例:查询员工名和直接上级的名称(把一张表看作两张表)
SELECT e.last_name
员工名,m.last_name
上级名称
FROM employees e,employees m
WHERE e.manager_id
=m.employee_id
;#上级编号=上级的员工号 - 1.7 子查询
- 案例:查询员工号=102的部门名
SELECT (
SELECT department_name,e.department_id FROM departments d
INNER JOIN employees e
ON d.department_id=e.department_id
WHERE e.employee_id=102
) 部门名; - exists
SELECT bo.*
FROM boys bo
WHERE NOT EXISTS(
SELECT boyfriend_id
FROM beauty b
WHERE bo.id
=b.boyfriend_id
- 2 DML
- 3 DDL
- 4 创建模拟数据必需的存储函数
0 引用
0.1 SQL语句的分类
DQL(Data Query Language)数据查询语言:select
DML(Data Manipulate Language)数据操作语言:insert 、update、delete
DDL(Data Define Languge)数据定义语言:create、drop、alter
TCL(Transaction Control Language)事务控制语言:commit、rollback
0.2 例题中用到的表
1 DQL
1.1 基础查询
查看指定数据库下指定表中的结构:
USE 数据库名
SELECT 查询列表FROM 表名;
①通过select查询完的结果是一个虚拟的表格,不是真实存在
②查询列表(要查询的东西)可以是:表中的常量值、表达式、字段、函数
1.查询表中的单个字段:SELECT last_name FROM employees;
2.查询表中的多个字段:依次双击要查询的字段(会自动加着重号``用来区分字段和关键字),不同字段之间用逗号隔开(末尾不加逗号)
3.查询表中的所有字段:SELECT * FROM employees; (顺序和原始表一样)
4.查询常量值:SELECT ‘john’;
5.查询表达式:SELECT 100%98;
6.查询函数:SELECT VERSION();
1.1.1 为字段起别名AS
① 便于理解;②如果要查询的字段有重名的情况,使用别名可以区分开来
方式一:使用AS:字段名 AS 别名
SELECT 100%98 AS 结果;
SELECT last_name AS 姓,first_name AS 名 FROM employees;
方式二:使用空格(省略AS):字段名别名
SELECT last_name 姓,first_name 名 FROM employees;
#特例:别名中有特殊符号,需要使用单’’或双引号””把别名包起来
SELECT salary AS “out put” FROM employees;#案例:查询salary,显示结果为out put
1.1.2 去重DISTINCT
去重:在字段名的前面加上关键字DISTINCT
#案例:查询员工表中涉及到的所有的部门编号
SELECT DISTINCT department_id FROM employees;
1.1.3 +号的作用
(1)java中的+号:
①运算符,两个操作数都为数值型
②连接符,只要有一个操作数为字符串
(2)mysql中的+号:
仅仅只有一个功能:运算符
select 100+90; #两个操作数都为数值型,则做加法运算
select ‘123’+90;#只要其中一方为字符型,试图将字符型数值转换成数值型。如果转换成功,则继续做加法运算;
select ‘john’+90;#如果转换失败,则将字符型数值转换成0。
select null+10;#只要其中一方为null,则结果肯定为null
1.1.4 拼接CONCAT(str1,str2,…)
案例:查询员工名和姓连接成一个字段,并显示为姓名
SELECT CONCAT(last_name,first_name) AS 姓名FROM employees;
#案例:显示出表employees的两列,各个列之间用逗号连接,列头显示成OUT
SELECT CONCAT(employee_id
, ‘,’, first_name
) AS OUT FROM employees;
#特例:在拼接的字段commission_pct中部分值存在NULL,则这部分拼接结果是NULL
SELECT CONCAT(id
, ‘,’, commission_pct
) AS OUT_PUT FROM employees;
1.1.5 IFNULL(expr1,expr2)
解决NULL:使用IFNULL(expr1,expr2)分情况讨论,字段expr1不为NULL的部分显示原来值,为NULL部分显示expr2。
SELECT IFNULL(commission_pct,0) AS 奖金率, commission_pct FROM employees;
SELECT CONCAT(id
, ‘,’, IFNULL(commission_pct,0)) AS OUT FROM employees;
#查询员工号176的员工的年薪:
SELECT salary12(1+ IFNULL(commission_pct,0)) AS 年薪FROM employees;
1.2 条件查询
条件查询:根据条件过滤原始表的数据,查询到想要的数据(当筛选条件成立时)。
SELECT 查询列表 FROM 表名
WHERE 筛选条件;
注:WHERE一定放在FROM后面
1.2.1 筛选条件
根据筛选条件的不同分类:
一、条件运算符表达式
> < >= <= = !=或<>
示例:salary>10000
二、逻辑运算符表达式:用于连接条件表达式
&&和and:如果连接的条件同时成立,结果为true,否则为false
||或or:只要有一个条件成立,结果为true,否则为false
!或not:如果连接的条件本身为false,结果为true,反之为false
示例:salary>10000 && salary<20000
三、模糊查询
1.like:一般和通配符搭配使用。
案例:查询员工名中第三个字符为n,第五个字符为l:name like ‘__n_l%’
2.between and 和 not between and
between A and B:A和B之间,包含临界值,等价于>=A && <=B,
not between A and B:不在A和B之间,不包含临界值,等价于B,
A和B的类型要一致或兼容,且不能调换顺序。
案例:查询员工编号在100到120之间:employee_id BETWEEN 100 AND 120;
3.in
判断某字段的值是否属于in列表中的某一项,等价于=。
①in列表的值类型必须一致或兼容。②in列表中不支持通配符
案例:查询员工的工种编号是 IT_PROG、AD_VP、AD_PRES之一的员工:job_id IN( ‘IT_PROT’ ,’AD_VP’,’AD_PRES’);
4.is null和is not null
=或<>不能用于判断null值
is null或is not null 仅仅可以判断null值
5.<=>等价于等于,既可以判断null值,还可以判断普通类型的值
1.2.2 通配符
% :任意多个字符,包含0个字符
_ :任意单个字符
注:字符型的值必须要用’ ’引起来。
1.2.3 转义符
\或A ESCAPE ‘A’ 其中A为任意字符
作用:使得通配符可以当做普通的字符来使用。
案例:查询员工名中第二个字符为的员工名:name LIKE ‘$_%’ ESCAPE ‘$’
1.3 排序查询
1.3.1 ORDER BY
SELECT 查询列表 FROM 表名 WHERE 筛选条件
ORDER BY 排序列表【ASC| DESC】;
1、asc代表升序,可以省略;desc代表降序;如果都不写默认是升序
2、order by子句可以支持单个字段、多个字段、表达式、函数、别名(用逗号隔开)
3、order by子句一般放在查询语句的最后面,除了limit子句
#案例:按年薪降序查询员工信息和年薪:
SELECT ,salary12(1+IFNULL(commission_pct,0)) AS 年薪FROM employees
ORDER BY salary12(1+IFNULL(commission_pct,0)) DESC; #按表达式排序
或ORDER BY 年薪 ASC; #按别名排序
#案例:查询员工信息,要求先按工资降序,再按employee_id升序
SELECT FROM employees
ORDER BY salary DESC,employee_id ASC;#按多个字段排序:逗号隔开
1.3.2 LENGTH(str) 长度函数
案例:查询员工名,并且按名字的长度降序
SELECT LENGTH(last_name) AS 字节长度,last_name FROM employees
ORDER BY LENGTH(last_name) DESC; #按函数排序
注:按名字长度即字节长度:length(name)
按名字即名字的字母顺序:name
1.4 常见函数
调用函数并将返回值显示出来:select 函数名(实参列表);
注:实参列表用到表中的字段时,在后面加上【from 表】;
1.4.1 单行函数
一、字符函数(参数的类型为字符类型)
1.concat(str1,str2,…) 拼接
2.substr/substring(str,pos) 截取从指定索引处及其后面的所有字符
2.substr/substring(str,pos,len) 截取从指定索引处及其后面的指定字符长度的字符串
注:sql中索引从1开始
3.upper(str) 转换成大写
3.lower(str) 转换成小写
4.trim(str) 去前后的空格
4.trim(remstr FROM str) 去前后指定的字符remstr
4.ltrim去左边空格
4.rtrim去右边空格
5.replace(str,from,to) 用指定字符替换字符中所有的指定字符
6.lpad(str,len,padstr) 用指定字符左填充得到指定总长度字符,原字符长度超过截断
6.rpad(str,len,padstr) 用指定字符右填充得到指定总长度字符,原字符长度超过截断
7.instr(str,substr) 返回子串第一次出现的索引,如果找不到返回0
8.length(str) 获取字节个数(utf8字符集一个字母一个字节,一个汉字三个字节)
9.ifnull(expr1,expr2) 字段1不为NULL的部分显示本身;为NULL部分显示2
#案例:查询所有员工邮箱的用户名(邮箱john@126.com,@前面的字符)
select substr(email,1,instr(email,’@’)-1) from employees;
二、数学函数(参数的类型为数字类型)
1.round(X) 四舍五入为整数
1.round(X,D) 四舍五入小数点后保留D位
2.rand 随机数
3.floor(X) 向下取整,返回<=该参数的最大整数
3.ceil(X) 向上取整,返回>=该参数的最小整数
4.mod(m,n) 取余,结果的正负和被除数的正负相同
5.truncate(X,D) 截断,小数点后保留D位,长度不够补0
三、日期函数(参数的类型为日期类型)
1.now() 返回当前系统日期时间
1.curdate() 当前系统日期,不包含时间
1.curtime() 当前系统时间,不包含日期
2.获取指定的部分,年、月、日、小时、分钟、秒
year(data) month(data) monthname(data) day hour minute second
3.str_to_date(str,format) 将日期格式的字符类型转换成指定格式的日期类型
STR_TO_DATE(‘9-13-1999’,’%m-%d-%Y’) #1999-09-13
案例:输入4-3 1992,查询入职日期为1992-4-3的员工信息
SELECT FROM employees
WHERE hiredate = STR_TO_DATE(‘4-3 1992’,’%c-%d %Y’);
3.date_format(date,format) 将日期转换成指定格式的字符
DATE_FORMAT(‘2018/6/6’,’%Y年%m月%d日’) #2018年06月06日
案例:查询有奖金的员工名和入职日期(xx月/xx日 xx年)
SELECT last_name,DATE_FORMAT(hiredate,’%m月/%d日 %y年’) AS 入职日期
FROM employees WHERE commission_pct IS NOT NULL;
4.datediff(expr1,expr2) 两个日期相差天数
四、流程控制函数
1.if(expr1,expr2,expr3) 处理双分支(if else、三元运算符的效果)
其中expr1为条件表达式,条件成立返回expr2不成立返回expr3的值
2.case控制结构
(1)使用1:处理等值判断(switch case的效果)
java中:
switch(变量或表达式){
case 常量1:语句1;break;
…
default:语句n;break;
}
mysql中:
case 要判断的字段或表达式
when 常量1 then 要显示的值1或语句1;(注:值不用加;)
when 常量2 then 要显示的值2或语句2;
…
else 要显示的值n或语句n;
end
注:作为表达式和select搭配是,只能是要显示的值。
(2)使用2:处理条件判断(多重if的效果)
java中:
if(条件1){
语句1;
}else if(条件2){
语句2;
}
…
else{
语句n;
}
*mysql中:
case
when 条件1 then 要显示的值1或语句1; (注:值不用加;)
when 条件2 then 要显示的值2或语句2;
…
else 要显示的值n或语句n;
end
五、其他函数
1.version() 当前版本号
2.database() 当前库
3.user() 当前连接用户
1.4.2 分组函数(聚合函数)
1.sum([distinct] expr) 求和
2.max([distinct] expr) 最大值
3.min([distinct] expr) 最小值
4.avg([distinct] expr) 平均值
5.count([distinct] expr) 计数
特点:
1、以上五个分组函数都只计算非null值,忽略null值;除了count(*)、count(常量值)
2、参数类型:
- sum和avg一般用于处理数值型
- max、min、count可以处理任何数据类型
3、都可以和distinct搭配使用,用于统计去重后的结果
4、count的参数可以支持:字段、*、常量值(一般设置为1)
- count(*)、count(常量值) 统计表的总行数/总个数/所有条数,包含null值
- count(字段) 统计表的总行数/总个数/所有条数,不计null值
- 和分组函数一同查询的字段有限制:group by后的字段
1.5 分组查询
1.5.1 GROUP BY + HAVING
语法
SELECT 查询列表,分组函数(聚合函数)
FROM 表名
WHERE 分组前筛选条件
GROUP BY 分组的列表
HAVING 分组后筛选条件
ORDER BY 排序列表【ASC| DESC】;
注意
1、使用group by子句将表中的数据分成若干组
2、和分组函数一同查询的查询列表必须是group by子句中出现的字段。
3、分组查询中的筛选分为两类:分组前筛选和分组后筛选
筛选条件涉及到的字段来自于 | 使用方法 | 位置 | |
---|---|---|---|
分组前筛选 | from后的原始表 | where 筛选条件 | from后group by前 |
分组后筛选 | group by后的结果集 | having 筛选条件 | group by后 |
①分组函数做条件,肯定放在having子句中,属于分组后筛选
②能用分组前筛选的,优先考虑使用分组前筛选
4、group by子句支持单个字段、多个字段、表达式或函数分组(用逗号隔开)
5、group by子句、having子句都支持别名,where子句不支持别名
6、也可以添加排序,放在整个查询最后
7、出现在 select 后面的字段,要么是分组函数,要么是 group by 的
8、group by 一般和分组函数一起使用才有意义,比如 count、sum、avg等,单独查原始表中非 group by 的字段是没有意义的
题目:
- 有选课表course_relation(student_id, course_id),其中student_id表示学号,course_id表示课程编号,获取每个学生所选课程的个数信息。
select student_id, count(course_id) from course_relation group by student_id;
- 查询每个位置的部门个数。
select count(department_id) from departments group by location_id;
或
select count(*), location_id from departments group by location_id;
- 查询每个领导手下的有奖金的员工的平均工资
select manager_id, avg(salary) from employees
where commission_pct is not null
group by manager_id ;
- 查询哪个部门的员工个数>5
select department_id, count() from employees group by department_id having count()>5;
①查询每个部门的员工个数
select department_id, count() from employees group by department_id;
②根据①结果筛选
select department_id, count() from employees group by department_id having count(*)>5;
- 每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
select job_id,max(salary) from employees
where commission_pct is not null
group by job_id
having max(salary)>12000;
1.6 连接查询
1.含义:又称多表查询,当查询的字段来自于多个表时,就会用到连接查询
2.笛卡尔乘积现象:表1 有m行,表2有n行,结果=mn行。发生原因:没有有效的连接条件。如何避免:添加有效的连接条件(表名.字段名)。
3. 连接条件的分类:
①按年代分类:
sql92标准:仅仅支持内连接
sql99标准【推荐】:支持内连接+外连接(左外和右外)+交叉连接
②按功能分类:
内连接(交集):等值连接非等值连接自连接
外连接(不交集):左外连接右外连接全外连接
交叉连接
*4.总结:
1.6.1 sql92:等值连接
sql92:
SELECT 各个表的查询列表(逗号隔开),分组函数
FROM 各个表名(多表之间用逗号隔开)
WHERE 连接条件AND 连接条件AND 筛选条件
GROUP BY 分组的列表
HAVING 分组后的筛选条件
ORDER BY 排序列表【ASC| DESC】;
特点:
1.多表等值连接的结果为多表的交集部分:连接条件之间用and
2.n表连接,至少需要n-1个连接条件
3.多表的顺序没有要求,可以调换
4.一般需要为表起别名:
①反复用到表名来限定时,为表起别名(AS可省略)可以提高语句的简洁度
②区分多个重名的字段
③如果为表起了别名,则查询的字段就不能使用原来的表名去限定
5.可以搭配前面介绍的所有子句使用,比如排序、分组、筛选
1.6.2 sql92:非等值连接
案例:查询员工的工资和工资级别
SELECT last_name,salary,grade_level
FROM employees e,job_grades g
WHERE salary BETWEEN g.lowest_sal AND g.highest_sal
GROUP BY employee_id;
1.6.3 sql92:自连接
案例:查询员工名和直接上级的名称(把一张表看作两张表)
SELECT e.last_name
员工名,m.last_name
上级名称
FROM employees e,employees m
WHERE e.manager_id
=m.employee_id
;#上级编号=上级的员工号
1.6.4 sql99:内连接
sql99:#通过join关键字实现连接
SELECT 各个表的查询列表(逗号隔开),分组函数
FROM 表1 别名
【连接类型】JOIN 表2 别名ON 连接条件1
【连接类型】JOIN 表3 别名ON 连接条件2(多表之间用join on连接条件)
WHERE筛选条件AND 筛选条件
GROUP BY 分组的列表
HAVING 分组后的筛选条件
ORDER BY 排序列表【ASC| DESC】;
其中连接类型:
内连接:inner
外连接:左外:left 【outer】 右外:right 【outer】 全外:full【outer】
交叉连接:cross
内连接:
1.可以添加排序、分组、筛选
2.inner可以省略
3.筛选条件放在where后面,连接条件放在on后面,提高分离性,便于阅读
4.inner join连接和sql92语法中的连接效果是一样的
(1)等值连接
#查询员工名、部门名、工种名,并按部门名降序(添加三表连接)
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)非等值连接
#查询工资级别的个数>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;
(3)内连接
#查询姓名中包含字符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%’;
1.6.5 sql99:外连接
1.外连接用于查询一个表中有,另一个表没有的记录。
2.外连接分为主从表,它的查询结果为主表中的所有记录
如果从表中有和它匹配的,则显示匹配的值
如果从表中没有和它匹配的,则显示null
外连接的查询结果=内连接结果+主表中有而从表没有的记录
3.左外连接,left join左边的是主表
右外连接,right join右边的是主表
4.左外和右外更换连接类型及两个表的顺序,就可以实现同样的效果
5.全外连接=内连接的结果+表1中有但表2没有的+表2中有但表1没有的
(1)左外连接
#案例:查询哪个部门没有员工
SELECT d.department_id, d.department_name
FROM departments d
LEFT OUTER JOIN employees e
ON d.department_id
=e.department_id
WHERE e.department_id
IS NULL;
(2)右外连接
SELECT d.department_id
,d.department_name
FROM employees e
RIGHT OUTER JOIN departments d
ON e.department_id
=d.department_id
WHERE e.department_id
IS NULL
1.6.6 sql99:交叉连接
1.7 子查询
1.含义:出现在其他语句(增删改查语句)中的select语句,称为子查询或内查询;外部的查询语句,称为主查询或外查询
2.分类(其他语句以select查询语句为例):
按子查询出现的位置:
select后面:仅仅支持标量子查询
from后面:支持表子查询
where或having后面:标量子查询、列子查询、行子查询
exists后面(相关子查询):表子查询
按子查询结果集的行列数:
标量子查询(结果集只有一行一列)
列子查询(结果集只有一列多行)
行子查询(结果集有一行多列)
表子查询(结果集一般为多行多列)
3.特点:
①子查询都放在小括号内
②子查询可以放在from后面、select后面、where后面、having后面,但一般放在条件的右侧
③子查询优先于主查询执行,主查询使用了子查询的执行结果
④子查询根据查询结果的行数不同分为以下两类:
1.7.1 where或having后面
特点:
1.子查询放在小括号内
2.子查询一般放在条件的右侧
3.子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果
4.子查询不带封号; 分步写更容易
一、标量子查询
标量子查询,一般搭配着单行比较操作符> < >= <= = <>使用。
#案例:返回公司工资最少的员工的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、notin、any/some、all使用。
IN/NOT IN(子查询返回的列表):等于列表中的任意一个
ANY|SOME(子查询返回的列表):和子查询返回的某一个值比较
ALL(子查询返回的列表):和子查询返回的所有值比较
注:IN和= ANY都表示等于里面任意一个
NOT TN和!= ALL或<>ALL都表示不等于里面任意一个
#案例:返回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));
#案例:返回其它工种中比job_id为‘IT_PROG’工种任一工资低的员工的员工号、姓名、job_id 以及salary
SELECT last_name,employee_id,job_id,salary FROM employees
WHERE salary
WHERE job_id = ‘IT_PROG’
) AND job_id<>’IT_PROG’;
二、行子查询
#案例:查询员工编号最小并且工资最高的员工信息
#③查询员工信息
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);
1.7.2 select后面
案例:查询员工号=102的部门名
SELECT (
SELECT department_name,e.department_id FROM departments d
INNER JOIN employees e
ON d.department_id=e.department_id
WHERE e.employee_id=102
) 部门名;
1.7.3 from后面
将子查询结果充当一张表,要求必须起别名。
#案例:查询每个部门的平均工资的工资等级
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;
#②连接①的结果集和job_grades表
#筛选条件平均工资 between lowest_sal and highest_sal
#案例:查询各部门中工资比本部门平均工资高的员工的员工号, 姓名和工资
SELECT last_name,employee_id,salary
FROM employees
JOIN (SELECT AVG(salary) avgs,department_id FROM employees GROUP BY department_id) e #①查询每个各部门的平均工资
ON employees.department_id
=e.department_id #②对应部门员工的工资:连接①结果集和employees表,进行筛选
WHERE salary>avgs
1.7.4 exists、not exists后面
exists(完整的查询语句)
结果:1或0,表示( )中的查询语句有没有值。
#案例:查询有员工的部门名
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:查询没有女朋友的男神信息
#not in
SELECT bo.*
FROM boys bo
WHERE bo.id NOT IN(
SELECT boyfriend_id
FROM beauty
)
exists
SELECT bo.*
FROM boys bo
WHERE NOT EXISTS(
SELECT boyfriend_id
FROM beauty b
WHERE bo.id
=b.boyfriend_id
1.8 分页查询
SELECT 各个表的查询列表(逗号隔开),分组函数
FROM 表1 别名
【连接类型】 JOIN 表2 别名 ON 连接条件1
【连接类型】 JOIN 表3 别名 ON 连接条件2
WHERE 筛选条件 AND 筛选条件
GROUP BY 分组的列表
HAVING 分组后的筛选条件
ORDER BY 排序列表【ASC| DESC】;
LIMIT 【OFFSET,】SIZE;
# offset要显示条目的起始索引(起始索引从0开始);size 要显示的条目个数。
1.应用:实际的web项目中需要根据用户的需求提交对应的分页查询的sql语句
当要显示的数据,一页显示不全,需要分页提交sql请求
2.起始条目索引从0开始:第n条索引n-1
3.limit子句放在查询语句的最后
4.公式:要显示的页数 page,每页显示条目数size
select 查询列表 from 表
limit (page-1)*size,size;
比如:size=10
page
1 0
2 10
3 20
1.9 联合查询
SELECT 查询列表1【FROM 表】【WHERE 条件】UNION 【ALL】
SELECT 查询列表2 【FROM 表】【WHERE 条件】UNION 【ALL】
…..
SELECT 查询列表n【FROM 表】【WHERE 条件】
1.应用:将多条查询语句的结果上下合并成一个查询结果,其字段名是第一条查询语句的查询字段名。(联合是上下拼接;连接是左右拼接)
2.要查询的结果来自于多个表,且多个表没有直接的连接关系,但查询的信息一致时
3.要求多条查询语句的查询列数必须是一致的
4.要求多条查询语句的查询的每一列字段的类型和顺序最好一致
5. union关键字默认去重,如果使用union all 可以包含重复项
2 DML
2.1 插入(添加)语句
2.1.1 方式一:经典的插入
INSERT INTO 表名【(列/字段名1,…)】 VALUES(新值1,…), .., (新值1,…);
或INSERT INTO 表名【(列/字段名1,…)】SELECT查询语句;
1.新值1插入表名的列名1中(新值和列名要一一对应)
2.插入的值的类型要与列的类型一致或兼容
3.不可以为null的列必须插入值。
可以为null的列可以在对应列名处插入null,或列名和null都不写
4.列和新值的顺序可以调换,但是两者要一一对应
5.列数和值的个数必须一致
6.可以省略列名,默认所有列,此时列的顺序和表中列的顺序一致,因此新值得按表的顺序插入
2.1.2 方式二:
INSERT INTO 表名 SET 列名1=新值1,列名2=新值2,…列名N=新值N;
2.1.3 两种方式大pk
1.方式一支持插入多行,方式二不支持
VALUES(新值1,…), (新值1,…), (新值1,…), ….., (新值1,…);
2.方式一支持子查询,方式二不支持
INSERT INTO 表名【(列/字段名1,…)】SELECT查询语句;
2.2 修改
2.2.1 修改单表的记录
UPDATE 表名 SET 列/字段=新值,列/字段=新值,… 【WHERE 筛选条件】
1.修改的新值的类型要与列的类型一致或兼容
2.字符和日期型要加单引号
3.where 筛选条件:用来限制要修改表中的哪些行
筛选条件可以使用条件表达式、逻辑表达式、模糊查询
2.2.2 修改多表的记录:先连接成一个表
sql92语法:
UPDATE 表1 别名1,表2 别名2
SET 字段/列=新值,字段/列=新值,…
WHERE 连接条件 AND 筛选条件;
sql99语法:
UPDATE 表1 别名INNER|LEFT|RIGHT JOIN 表2 别名ON 连接条件 #先连接
SET 字段/列=新值,字段/列=新值,…
WHERE 筛选条件;
2.3 删除
2.3.1 方式1:delete语句
(1)单表的删除:
DELETE FROM 要删除的表名【WHERE 筛选条件】
1.筛选条件限制要删除表中的哪些行,不加就是删除所有行数据
2.删除的是整行
(2)多表的删除:先连接成一个表
sql92语法:
DELETE 要删除的表的别名
FROM 表1 别名,表2 别名
WHERE 连接条件 AND 筛选条件;
sql99语法:
DELETE 表1的别名1,表2的别名2
FROM 表1 别名1 INNER|LEFT|RIGHT JOIN 表2 别名2 ON 连接条件
WHERE 筛选条件;
2.3.2 方式2:truncate语句:清空表中全部数据
2.3.3 两种方式的区别【面试题】
1.delete 可以加where 条件,truncate不能加
2.truncate删除,效率高一丢丢
3.假如要删除的表中有自增长列/字段,
如果用delete删除后,再插入数据,自增长列的值从断点(删除前最后一个值后)开始,
而truncate删除后,再插入数据,自增长列的值从1开始
4.truncate删除没有返回值,delete删除有返回值
5.truncate删除不能回滚,delete删除可以回滚
3 DDL
3.1 库和表的管理
3.1.1 库的管理:
一、创建库
CREATE DATABASE IF NOT EXISTS 库名;
二、修改库
①更改库名
RENAME DATABASE 旧库名 TO 新库名;
②更改库的字符集
ALTER DATABASE 库名 CHARACTER SET 字符集;
三、删除库
DROP DATABASE IF EXISTS 库名;
3.1.2 表的管理:
一、创建表
USE 库名;
CREATE TABLE IF NOT EXISTS 表名(
列/字段名列/字段的类型【(长度)】【列的约束】【标识列】,
…
列/字段名列/字段的类型【(长度)】【列的约束】【标识列】);
二、修改表
ALTER TABLE 表名 ADD|MODIFY|DROP|CHANGE COLUMN 列名【列类型约束】;
①修改表名
ALTER TABLE 旧表名 RENAME TO 新表名;
②修改列/字段名【以列的类型及】
ALTER TABLE 表名 CHANGE COLUMN 旧列名新列名列的类型;
③修改列/字段类型和列级约束
ALTER TABLE 表名 MODIFY COLUMN 列名新的列类型 ;
④添加列/字段
ALTER TABLE 表名 ADD COLUMN 列名【列类型约束】;
⑤删除列/字段
ALTER TABLE 表名 DROP COLUMN 列名;
三、删除表
DROP TABLE IF EXISTS 表名;
四、表的复制
①仅仅复制表的结构(不包含数据):CREATE TABLE 生成复制表的名 LIKE 表名;
②仅仅复制表的某些字段(不包含数据):CREATE TABLE 生成复制表的名 SELECT 要复制的列1 ,…,列表n FROM 表名WHERE 1=0;
③复制表的结构+数据:CREATE TABLE 生成复制表的名 SELECT * FROM 表名;
④只复制部分字段+数据:CREATE TABLE 生成复制表的名 SELECT 要复制的列1 ,…,列表n FROM 表名WHERE 筛选条件;
注:1.可以跨库,但是要指明库名.表名
2.列类型包括:类型名(长度)
3.1.3 创建库、表通用的写法:
DROP DATABASE IF EXISTS 旧库名/旧表名;
CREATE DATABASE 新库名/表名();
3.2 常见数据类型
数值型:整型;小数(定点数;浮点数)
字符型:较短的文本(char、varchar);较长的文本:(text、blob较长的二进制数据)
日期型
一、整型
分类:tinyint、smallint、mediumint、int/integer、bigint
字节:1 2 3 4 8
1.类型名(长度) 【unsigned有无符号】【zerofill】
2.如果不设置有符号还是无符号,默认是有符号,如果想设置无符号,需要添加unsigned关键字
3.如果插入的数值超出了整型的范围,会报out of range异常,并且插入了临界值
4.如果不设置长度,会有默认的长度。
长度代表了显示结果的最大宽度。如果不够可以用0在左边填充,但0填充必须搭配zerofill使用。一加上zerofill,默认变成了无符号。
二、小数
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,则会根据插入的数值的精度来决定精度
③定点型的精确度较高,如果要求插入数值的精度较高如货币运算等则考虑使用
三、字符型
1.较短的文本:char、varchar;binary和varbinary用于保存较短的二进制;enum用于保存枚举;set用于保存集合
2.较长的文本:text、blob(较大的二进制,如图片)
3.较短的文本的特点:
写法 | M的意思:最多的字符数 | 特点 | 空间的耗费 | 效率 | |
---|---|---|---|---|---|
char | char(M) | 可省略,默认为1 | 固定长度的字符 | 比较耗费 | 高 |
varchar | varchar(M) | 不可以省略 | 可变长度的字符 | 比较节省 | 低 |
四、字符型
1.分类:
date保存日期;time 只保存时间;year只保存年
datetime保存日期+时间;timestamp保存日期+时间
2.特点
字节 | 范围 | 时区等的影响 | |
---|---|---|---|
datetime | 8 | 1000——9999 | 不受 |
timestamp | 4 | 1970-2038 | 受 |
3.3 常见约束
USE 库名;
CREATE TABLE IF NOT EXISTS 表名(
列/字段名列/字段的类型【(长度)】【列级约束】,
…
列/字段名列/字段的类型【(长度)】【列级约束】,
表级约束);
1. 含义:一种限制,用于限制表中的数据,为了保证表中的数据的准确和可靠性
2. 分类:六大约束
1) NOT NULL:非空,用于保证该字段的值不能为空。比如姓名、学号
2) DEFAULT 默认值:默认,用于保证该字段有默认值。比如性别
3) PRIMARY KEY:主键,用于保证该字段的值具有唯一性,并且非空。比如学号
4) UNIQUE:唯一,用于保证该字段的值具有唯一性,可以为空。比如座位号
5) CHECK(输入条件):检查(mysql不支持),只有满足条件的值才能添加。比如性别
CHECK(gender=’男’ OR gender =’女’)或CHECK(gender IN(‘男’,’女’))
6) FOREIGN KEY:外键,用于限制两个表的关系,用于保证该字段的值必须来自于主表的关联列的值。即,在从表添加外键约束,用于引用主表中某列的值(类型必须一致)。比如员工表的部门编号(从)和部门表(主)
3. 添加约束的时机:①创建表时;②修改表时
4. 约束的添加分类:
1) 列级约束:列的后面。六大约束语法上都支持,但外键约束没有效果。不可以起约束名
2) 表级约束:所有列的后面。除了非空、默认,其他的都支持。可以起约束名(主键没有效果)
5.结合下面两句查看约束:
SHOW INDEX FROM 表名:查看表中的所有索引,包括主键、外键、唯一
DESC 表名:查看表中的结构,看不到外键约束
6. 主键和唯一的大对比
保证唯一性 | 是否允许为空 | 一个表中可以有多少个 | 两个列是否允许组合成一个 | |
---|---|---|---|---|
主键 | √ | × | 最多有1个 | √,但不推荐 |
唯一 | √ | √ | 可以有多个 | √,但不推荐 |
注:两个列是否允许组合成一个:PRIMARY KEY /UNIQUE (列1,列2),两列都一样才认为是不唯一
7. 外键的特点
1) 要求在从表设置外键关系
2) 从表的外键列的类型和主表的关联列的类型要求一致或兼容,字段名称无要求
3) 主表的关联列必须是一个key(一般是主键或唯一)。
注:key包括主键、唯一、外键、自定义key
4) 插入数据时,先插入主表,再插入从表;删除数据时,先删除从表,再删除主表
3.3.1 创建表时添加约束
一、添加列级约束
直接在字段名的类型后面添加约束类型即可,多个约束用空格隔开。
只支持:非空、默认、主键、唯一
二、添加表级约束
在最后一个字段,的最下面添加:【CONSTRAINT约束名】约束类型(要添加约束的字段名);
1.添加外键:【CONSTRAINT 约束名从表名主表名】 FOREIGN KEY(从表的字段名)
REFERENCES 主表名(主表的字段名)
2.主键约束改名字了也没有效果,仍是PRIMARY;其他约束不改名字默认为字段名
3.约束名不可以重复
4. 多个约束用逗号隔开,最后一个约束不用逗号。
三、通用的写法
CREATE TABLE IF NOT EXISTS 表名(
字段名字段类型约束(NOT NULL、DEFAULT默认值、PRIMARY KEY、UNIQUE)
CONSTRAINT 约束名从表名主表名 FOREIGN KEY(从表的字段名)
3.3.2 修改表时添加约束
一、添加列级约束
ALTER TABLE 表名 MODIFY COLUMN 要修改的字段名字段类型新约束;
二、添加表级约束
ALTER TABLE 表名 ADD 【CONSTRAINT 约束名】约束类型(要修改的字段名)
1.修改外键:ALTER TABLE 表名 ADD 【CONSTRAINT 约束名从表名主表名】 FOREIGN KEY(从表的字段名) REFERENCES 主表名(主表的字段名)
3.3.3 修改表时删除约束
1.删除非空/默认约束
ALTER TABLE 表名 MODIFY COLUMN 字段名字段类型;
2.删除主键
ALTER TABLE 表名 DROP PRIMARY KEY;
3.删除唯一
ALTER TABLE 表名 DROP INDEX 字段名;
4.删除外键
ALTER TABLE 表名 DROP FOREIGN KEY 约束名;
3.4 标识列(自增长列)
不用手动插入值。系统提供默认的序列值(默认从1开始,每次增长1即步长为1)。
3.4.1 创建表时设置标识列
字段名约束类型AUTO_INCREMENT
1.标识列不一定必须和主键搭配,但要求是一个key(主键、唯一、外键、自定义key)
2.一个表至多可以有一个标识列
3.标识列的类型只能是数值型
4.标识列可以通过 SET auto_increment_increment=步长;设置步长
5. 设置标识列后添加数据的方法:
1) INSERT INTO 表名(标识列) VALUES(NULL);
2) 可以通过手动插入值,设置起始值:
INSERT INTO 表名(标识列) VALUES(自定义起始值); #起始值自定义
INSERT INTO 表名(标识列) VALUES(NULL);#其他的仍用NULL
3) INSERT INTO 表名() VALUES(); #省略标识列
3.4.2 修改表时设置标识列
ALTER TABLE 表名 MODIFY COLUMN 要修改的字段名字段类型约束类型
AUTO_INCREMENT;
3.4.3 修改表时删除标识列
ALTER TABLE 表名 MODIFY COLUMN 要修改的字段名字段类型
4 创建模拟数据必需的存储函数
创建函数,假如报错:This function has none of DETERMINISTIC……