SQL学习笔记系列(二) DQL语言的学习

打开脚本

-root@localhost-右键-执行SQL脚本-执行-完成-手动刷新

进阶1:基础查询

语法:

SELECT 要查询的东西
【FROM 表名】;

类似于Java中 :System.out.println(要打印的东西);

特点:

①通过select查询完的结果 ,是一个虚拟的表格,不是真实存在
②要查询的东西 可以是常量值、可以是表达式、可以是字段、可以是函数

查询单个字段

如:SELECT last_name FROM employees;

查询表中的多个字段

如SELECT last_name FROM employees,email FROM employees;#逗号隔开,可以不考虑顺序

查询表中所有字段

  1. 可在左边的菜单上双击选择后再用逗号隔开
  2. F12可格式化
  3. *代表所有字段(但顺序会与原表一致)

查询时的细节

  1. 建议每次都先加 “USE 库名”;
  2. 用着重号·来区分关键字和字段
  3. 选中执行/F9

查询常量值

查询某个常量值时不需要来自(from)哪个表

查询表达式

表达式:如select 188*90;

查询函数

函数:如select VERSION();

起别名

  1. 如SELECT 100%98 AS 结果;
  2. 或者 SELECT 100%98 (空格) 结果;

去重

加上 DISTINCT

SEKECT DISTINCT department_id FROM employees;

+号的作用

mysql中的+号仅仅只有一个功能:运算符。

  1. 如select 100+90;
  2. 如select “123”+90 #其中一方为字符型,试图将字符型转换成数值型,如果转换成功,则继续做加法运算,如果转换失败,则将字符型数值转换成0。
  3. 如select null+0;只要其中一方为null,则结果肯定为null.

利用contact函数实现拼接

如:查询员工名和姓连接成一个字段,并显示 姓名

 SELECT CONCAT('a','b','c') AS 姓名

IFNULL()

IFNULL()函数用于判断第一个表达式是否为NULL,如果为NULL则返回第二个参数的值。如果不为NULL则返回第一个参数的值。

IFNULL(commission_pact,0)AS 奖金率

COALESCE()函数

COALESCE(value,...)是一个可变参函数,可以使用多个参数。返回从左至右、第一个不为NULL的参数,如果所有参数都为NULL,那么返回NULL;当它使用两个参数时,和IFNULL函数作用相同。

进阶2:条件查询

条件查询:根据条件过滤原始表的数据,查询到想要的数据

语法

select 
	要查询的字段|表达式|常量值|函数
from 
	表
where 
	筛选条件 ;

条件表达式

	示例:salary>10000
	条件运算符:
	> < >= <= = != <>
	#'<>'是不等于的意思

#案例1:查询工资>12000的员工信息
SELECT * 
FROM employees
WHERE salary>12000;

#案例2:查询部门编号不等于90号的员工名和部门编号
SELECT last_name,department_id
FROM employees
WHERE department_id!=90;

#----------------------------------------------

逻辑表达式

示例:salary>10000 && salary<20000

逻辑运算符:

	and(&&):两个条件如果同时成立,结果为true,否则为false
	or(||):两个条件只要有一个成立,结果为true,否则为false
	not(!):如果条件成立,则not后为false,否则为true

#案例1:查询工资z在10000到20000之间的员工名、工资以及奖金
SELECT
last_name,
salary,
commission_pct
FROM
employees
WHERE salary>=10000
AND salary<=20000;

#案例2:查询部门变号不是在90到110之间,或者工资高于15000的员工信息
SLECT *
FROM employees
WHERE department_id<90 or department_id >110 or salary>15000;

或者:
SLECT *
FROM employees
WHERE NOT(department_id>=90 AND department_id <=110) OR salary>15000;

3.模糊查询

like 
between and 
in 
is null/is not null

(1)like

特点

(1)一般和通配符搭配
通配符:
% 任意多个字符(包含0个字符)
_任意单个字符

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

#-------------------------------------------------------

#案例2:查询员工名中第三个字符为e,第五个字符为a的员工名和工资
SELECT
    last_name,
    salary
FROM
    employees
WHERE
    last_name,LIKE'__e_a%';
    
#-------------------------------------------------------

#案例3 查询员工名中第二个字符为_员工名
SELECT
    last_name
FROM
    employees
WHERE
    last_name LIKE '_\_%';#或者last_name LIKE '_$_%'ESCAPE '$';

(2)between and

特点

i. 提高语句简洁度
ii. 包含临界值
iii. 两个临界值不能调换顺序

#案例1:查询员工编号在100到120之间的员工信息
SELECT
    *
FROM
    employees
WHERE
    employee_id BETWEEN 100 AND 120;

(3)in

含义

判断某字段的值是否属于in列表中的某项目

特点

i. 使用in提高语句简洁度
ii. in列表的值类型必须一致或兼容
iii. 不支持通配符

#案例:查询员工的工种的编号是 IT_PROT、AD_VP、AD_PRES中的一个员工名和工种编号
SELECT 
    last_name,
    job_id
FROM
    employees
WHERE
    job_id IN ('IT_PROT','AD_VP','AD_PRES');

(4)is null

特点

i. =或<>不能用于判断null值
ii. is null或is not null 可以判断null值

#案例1:查询没有奖金的员工名和奖金率
SELECT
    last_name,
    commision_pct
FROM
    employees
WHERE
    commision_pct IS NULL;

#案例2:查询有奖金的员工名和奖金率
SELECT
    last_name,
    commision_pct
FROM
    employees
WHERE
    commision_pct IS NOT NULL;

(5)安全等于 <=>

 #案例1:查询没有奖金的员工名和奖金率
SELECT
   last_name,
    commision_pct
FROM
    employees
WHERE
    commision_pct <=> NULL;
 
 #案例2:查询工资为12000的员工信息
 SELECT
    last_name
    salary
FROM
    employees
WHERE
    salary <=> 12000;

进阶3:排序查询

语法

select
	要查询的东西
from
	表
where 
	筛选条件

order by 排序的字段|表达式|函数|别名 【asc|desc】#升序或降序


#案例1:查询员工信息,要求工资从高到低排序
SELECT *
FROM employees
ORDER BY salary DESC;
#ORDER BY salary ASC;为从低到高

#案例2:查询部门编号>=90的员工信息,按入职时间的先后进行排序(添加筛选条件)
SELECT *
FROM employees
WHERE department_id >=90
ORDER BY hiredate ASC;

#案例3:按年薪的高低显示员工的信息和年薪(按表达式排序)
SELECT *,salary*12*(1+IFNULL(commission_pct,0)) 年薪
FROM employees
ORDER BY salarysalary*12*(1+IFNULL(commission_pct,0)) DESC;

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

#案例5:按姓名的长度显示员工的姓名和工资(按函数排序)
SELECT LENGTH(last_name) 字节长度,last_name,salary
FROM employees
ORDER BY LENGTH(last_name) DESC;
#这里换成字节长度也是可以的,如
ORDER BY 字节长度 DESC;

#案例6:查询员工信息,要求先按工资排序,再按员工编号排序
SELECT *
FROM employees
ORDER BY salary ASC,employee_id DESC #不可以ORDER BY两次

特点

  1. asc代表升序,desc代表减序,如果不写,默认升序
  2. order by 子句中可以支持单个字段、多个字段、表达式、函数、别名
  3. order by 子句一般是放在查询语句的最后面,但limit子句除外。

进阶4:常见函数

概念

  1. 类似于java的方法,将一组逻辑语句封装再方法体中,对外暴露方法名
  2. 好处(1)隐藏了实现细节
  3. 好处(2)提高了代码的重用性

调用

select 函数名(实参列表)
【from 表】;

特点

  1. 叫什么(函数名)
  2. 干什么(函数功能)

单行函数

1、字符函数
	concat拼接
	substr截取子串
	upper转换成大写
	lower转换成小写
	trim去前后指定的空格和字符
	ltrim去左边空格
	rtrim去右边空格
	replace替换
	lpad左填充
	rpad右填充
	instr返回子串第一次出现的索引
	length 获取字节个数

#(1)length
SELECT LENGTH('john');
SELECT LENGTH('张三丰');
#一个汉字占三个字节

#(2)concat 拼接字符串
SELECT concat(last_name,'-',first_name)FROM employees;

#(3)upper、lower
SELECT UPPER('john');
SELECT LOWER('john');
#如果原本就是小写,不变。如果有的大写有的小写,就把大写的变化。
#案例1:将姓变大写,名变小写,然后拼接
SELECT CONCAT(UPPER(last_name),LOWER(first_name)) 姓名
FROM employees;

#(4)substr/substring 截取子串
SELECT SUBSTR('李莫愁爱上了陆展元',7)#截取从指定索引处后面所有字符 out_put;
#注意:SQL索引从1开始

SELECT
SUBSTR('李莫愁爱上了陆展元',1,3) out_put;#3为长度,截取从指定索引处指定字符长度的字符

#案例:姓名中首字符大写,其他字符小写然后用_拼接,显示出来
SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)),'_',LOWER(SUBSTR(last_name,2))) out_put
FROM employees

#(5)instr 返回子串第一次出现的索引
SELECT INSTR('杨不悔爱上了殷六侠','殷六侠') AS out_put;
SELECT INSTR('杨不悔殷六侠爱上了殷六侠','殷六侠') AS out_put;
SELECT INSTR('杨不悔殷八侠爱上了殷六侠','殷六侠') AS out_put;#找不到返回0

#(6)trim 去前后指定的空格和字符
SELECT TRIM('    张翠花       ') AS out_put;

SELECT TRIM('a' FROM 'aaaaaaa张翠aaaaaa花) AS out_put;

#(7)lpad 左填充(用指定的字符实现左填充指定长度)
SELECT LPAD('殷素素',10,'*') AS out_put;

#(8)rpad 右填充(用指定的字符实现右填充指定长度)
SELECT RPAD('殷素素',12,'ab') AS out_put;

#(9)replace 替换
SELECT REPLACE('张无忌爱上了周芷若张无忌爱上了周芷若','周芷若','赵敏') AS out_put;

2、数学函数
	round 四舍五入
	rand 随机数
	floor向下取整
	ceil向上取整
	mod取余
	truncate截断

#(1)round 四舍五入
SELECT ROUND(1.65);
SELECT ROUND(-1.65);#取绝对值四舍五入后再加正负号
SELECT ROUND(1.567,2);#保留两位小数

#(2)ceil 向上取整,返回大于等于该参数的最小整数
SELECT CEIL(1.01)
SELECT CEIL(-1.01)#结果是-1,记住定义,大于等于

#(3)floor
向下取整,返回小于等于该参数的最大整数
SELECT FLOOR (-9.99);
#不要与前面round混淆,不是去绝对值后,记住定义

#(4)truncate 截断
SELECT TRUNCATE (1.69999,1); #1是保留一位小数

#(5)mod 取余
SELECT MOD(-10,-3);#被除数为+就是+,被除数为-就是-



3、日期函数
	now当前系统日期+时间
	curdate当前系统日期
	curtime当前系统时间
	str_to_date 将字符转换成日期
	date_format将日期转换成字符

#(1)now 返回当前系统日期+时间
SELECT NOW();

#(2)curdate 返回当前系统日期,不包含时间
SELECT CURDATE();

#(3)curtime
SELECT CURTIME();

#可以获取指定的部分,年、月、日、小时、分钟、秒
SELECT YEAR(NOW()) 年;
SELECT YEAR('1998-1-1') 年;
SELECT YEAR(hiredate) 年 FROM employees;
SELECT MONTH(now()) 月;#显示数字月份
SELECT MONTHNAME(NOW()) 月;#显示英文月份

#(4)str_to_date :将日期格式的字符转换成指定格式的日期
SELECT STR_TO_DATE('9-13-1999','%m-%d-%Y')#1999-09-13

#案例:查询入职日期为1992-4-3的员工信息
SELECT *
FROM employees
WHERE hiredate ='1992-4-3';

SELECT *
FROM employees
WHERE hiredate =STR_TO_DATE('4-3-1992','%c-%d-%Y');

#(5)日期格式符
 1. %Y 四位的年份
 2. %y 二位的年份
 3. %m 月份(01,02....12)
 4. %c 月份(1,2,3....12)
 5. %d 日(01,02,....)
 6. %H 小时(24小时制)
 7. %h 小时(12小时制)
 8. %i 分钟(00,01...59)
 9. %s 秒(00,01,...59)

#(6)date_format 将日期转换成字符
SELECT DATE_FORMAT(NOW(),'%y年%m月%d日') AS out
_put;

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

4、流程控制函数
	if 处理双分支
	case语句 处理多分支
		情况1:处理等值判断
		情况2:处理条件判断

#(1)if函数 :if else 的效果
SELECT IF(10>5,'大','小');#大
SELECT IF(10<5,'大','小');#小

SELECT last_name,commission_pct,IF(commission_pct IS NULL,'没奖金,呵呵','有奖金,嘻嘻')
FROM employees;

#(2)case函数使用1
case 要判断的字段或表达式
when 常量1 then 要显示的值1或语句1;
when 常量2 then 要显示的值2或语句2;
...
else 要显示的值n或语句n;
end

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

SELECT salary 原始工资, department_id,
CASE department_id
WHEN 30 THEN salary*1.1
WHEN 40 THEN salary*1.2
WHEN 50 THEN salary*1.3
ELSE salary
END AS 新工资
FROM employees;

#(3)case函数使用2 类似于多重if
case #与方法1的区别就在于这里case后不加东西
when 条件1 then 要显示的值1或语句1
when 条件2 then 要显示的值2或语句2;
...
else 要显示的值n或语句n;
end	

#案例1:查询员工工资,要求:
如果工资>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;

5、其他函数
	version版本
	database当前库
	user当前连接用户

单行函数案例讲解

#2. 查询员工号,姓名,工资,以及工资提高百分之20后的结果(new salary)
SELECT employee_id,last_name,salary,salary*1.2 'new salary' #这里别名+双引号是因为有空格

分组函数(做统计使用,又称为统计函数,聚合函数)

	sum 求和
	max 最大值
	min 最小值
	avg 平均值
	count 计数

特点

  1. 以上五个分组函数都忽略null值,count(*)统计行数除外

  2. sumavg一般用于处理数值型,max、min、count可以处理任何数据类型。

  3. 都可以搭配distinct使用,用于统计去重后的结果。

  4. count的参数可以支持:字段、、常量值,(一般放1) 建议使用 count()

  5. 和分组函数一同查询的字段要求是group by后的字段

    SELECT count(*)#统计行数,效率最高
    FROM employees;
    SELECT count(1)
    FROM employees;
    

简单的使用

SELECT SUM();
FROM employees;

SELECT AVG(salary)
FROM employees;

SELECT AVG(salary)
FROM employees;

SELECT MIN(salary)#非空个数
FROM employees;

SELECT COUNT(salary)
FROM employees;

SELECT SUM(salary) 和, AVG(salary) 平均,MAX(salary) 最高
FROM employees;

分组函数案例讲解

#2.查询员工表中的最大入职时间和最小入职时间的相差天数
SELECT DATEDIFF(now(),'2017-9-29');

进阶5:分组查询

语法:
select 分组函数,列(要求出现再group by的后面)
from 表
【WHERE 筛选条件】
group by 分组的列表
【ORDER BY 子句】

注意:查询列表必须特殊,要求是分组函数和group by后出现的字段

简单查询

#案例1:查询每个工种的最高工资
SELECT MAX(saraly),job_id
FROM employees
GROUP BY job_id;

#案例2:查询每个位置中的部门个数
SELECT COUNT(*),location_id
FROM departments
GROUP BY location_id;

添加分组前的筛选条件

#案例1:查询邮箱中包含a字符的每个部门的平均工资
SELECT AVG(salary),department_id
FROM employees
WHERE email LIKE'%a%'
GROUP BY department_id

#案例2:查询有奖金的每个领导手下员工的最高工资
SELECT MAX(salary),manager_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY manager_id;

添加分组后(复杂的)筛选条件

#案例1:查询哪个部门的员工个数>2
#(1)查询每个部门的员工个数
SELECT COUNT(*),department_id
FROM employees
GROUP BY department_id;
#(2)进行1的结果进行筛选,查询哪个部门的员工个数>2
SELECT COUNT(*),department_id
FROM employees
GROUP BY department_id
HAVING COUNT(*)>2 ;

#案例2:查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
#(1)查询每个工种有奖金的员工的最高工资
SELECt MAX(salary) ,job_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id;
#(2)根据1的结果继续筛选,最高工资>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的领导编号是哪个,以及其最低工资
#(1)查询每个领导手下的员工固定最低工资
SELCECT MIN(salary),manager_id
FROM employees
GROUP BY manager_id;
#(2)添加筛选条件,编号>102
SELCECT MIN(salary),manager_id
FROM employees
WHERE manager_id>102
GROUP BY manager_id;
#(3)添加筛选条件,最低工资大于5000
SELCECT MIN(salary),manager_id
FROM employees
WHERE manager_id>102
GROUP BY manager_id
HAVING MIN(salary)>5000;

特点

 1. 可以按单个字段分组
 2. 和分组函数一同查询的字段最好是分组后的字段
 (能用分组前筛选的,优先考虑使用分组前筛选)
 3. 分组筛选
			    数据源	        位置			     关键字
	分组前筛选:原始表	       group by的前面		where
	分组后筛选:分组后的结果集	group by的后面		having
	分组函数的条件肯定是放在having子句中
4. 可以按多个字段分组,字段之间用逗号隔开
5. 可以支持排序
6. having后可以支持别名

按照表达式或函数分组

#案例1:按员工姓名的长度分组,查询每一组员工个数,筛选员工个数>5的有哪些
#(1)查询每个长度的员工个数
SELECT COUNT(*),LENGTH(last_name) len_name
FROM employees
GROUP BY LENGTH(last_name);
#(2)添加筛选条件
SELECT COUNT(*),LENGTH(last_name) len_name
FROM employees
GROUP BY LENGTH(last_name)
HAVING COUNT(*)>5;

按多个字段分组

#案例1:查询每个部门每个工种的员工的平均工资
SELECT AVG(salary),department_id,job_id
FROM employees
GROUP by department_id,job_id;

添加排序

#案例1:查询每个部门每个工种的员工的平均工资,并且按平均工资的高低显示
SELECT AVG(salary),department_id,job_id
FROM employees
GROUP by department_id,job_id
ORDER BY AVG(salary) DESC;#order by放在最后

进阶6:多表连接查询

笛卡尔乘积:如果连接条件省略或无效则会出现
(表1有m行,表2有n行,结果有m*n行)
发生原因:没有有效的连接条件
解决办法:添加有效的连接条件

#简单例子,boys表和beauty表的连接
SELECT NAME,boyName
FROM boys,beauty
WHERE beauty.boyfriend_id=boys.id;

分类

按照年代分类

  1. sql92标准:仅仅支持内连接
  2. sql99标准(推荐):支持内连接+外连接(左外和右外)+交叉连接

按功能分类

1. 内连接
(1)等值连接
(2)非等值连接
(3)自连接
2. 外连接
(1)左外连接
(2)右外连接
(3)全外连接
3. 交叉连接

sql92标准

等值连接

#案例1:查询女神名和对应的男神名
SELECT name,boyname
FROM boys,beauty
WHERE beauty.boyfriend_id=boys_id;

#案例2:查询员工名和对应的部门名
SELECT last_name,department_name
FROM employees,departments
WHERE employees.department_id=departments.department_id;

(1)为表起别名

#查询员工名、工种名、工种号
SELECT last_name,e.job_id,job_title
FROM employees AS e,jobs AS j#一般都会起别名
WHERE e.job_id=j.job_id;

注意:如果为表起了别名,则查询的字段就不能使用原来的表名去限定。

(2)顺序调换

#查询员工名、工种名、工种号
SELECT last_name,e.job_id,job_title,commission_pct
FROM jobs AS j,employees AS e
WHERE e.job_id=j.job_id;
AND e.commission_pct IS NOT NULL #因为前面已经有了一个WHERE,所以再加筛选条件的时候用AND

(3)筛选

#案例1:查询有奖金的员工名,部门名
SELECT last_name,department_name
FROM employees e,department d
WHERE e.department_id=d.department_id

#案例2:查询城市名中第二个字符为o的部门名和城市名
SELECT department_name,city
FROM departments d,locations l
WHERE d.location_id=l.location_id
AND city LIKE '_o%';

(4)分组

#案例1:查询每个城市的部门个数
SELECT COUNT(*) 个数,city
FROM departments d,locations l
WHERE d.location_id=l.location_id
GROUP BY city;

#案例2:查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资
SELECT d.department_name,e.`manager_id`,MIN(salary)
FROM employees e,departments d
WHERE d.`department_id`=e.`department_id`
AND commission_pct  IS  NOT NULL;
GROUP BY d.department_name,e.`manager_id`;

(5)排序

#案例1:查询每个工种的工种名和员工的个数,并且按员工个数降序
SELECT job_title,COUNT(*)
FROM employees e,jobs j
WHERE e.`job_id`=j.`job_id`
GROUP BY job_title
ORDER BY COUNT(*) DESC;

(6) 三表连接

#案例1:查询员工名,部门名和所在的城市
SELECT department_name,city,last_name
FROM departments d,locations l,employees e
WHERE d.`department_id`=e.`department_id`
AND d.`location_id`=l.`location_id`;
AND city LIKE's%';#外加一个条件,city以s开头
ORDER BY last_name DESC;#再加条件:排序

(7) 特点

1.等值连接的结果 = 多个表的交集
2.n表连接,至少需要n-1个连接条件
3.多个表不分主次,没有顺序要求
4.一般为表起别名,提高阅读性和性能

非等值连接

#案例1:查询员工的工资和工资级别
#自己在文件上找到:'工资等级'这张表直接在小海豚上运行
SELECT salary ,grade_level
FROM employees e ,job_grade j
WHERE salary BETWEEN j.lowest_sal AND j.highest_sal;

自连接

#案例1:查询 员工名和上级的名称
SELECT e.employee_id,e.last_name,m.employee_id,m.last_name
FROM employees e,employees m #e代表打工人,m代表老板
WHERE e.manager_id=m.employee_id;

sql99语法:通过join关键字实现连接

含义:1999年推出的sql语法
支持:
等值连接、非等值连接 (内连接)
外连接
交叉连接

语法:

select 字段,...
from 表1 【别名】
【inner|left outer|right outer|cross】join 表2【别名】 on  连接条件
【inner|left outer|right outer|cross】join 表3 on  连接条件
【where 筛选条件】
【group by 分组字段】
【having 分组后的筛选条件】
【order by 排序的字段或表达式】

好处:语句上,连接条件和筛选条件实现了分离,简洁明了!

连接类型

内连接

inner

外连接

左外

left 【outer】

右外

right【outer】

全外

full【outer】

交叉连接

cross

1. 内连接

#语法:
SELECT 查询列表
FROM 表1 别名
INNER join 表2 别名
on 连接条件;

#(1)等值连接
#案例1:查询员工名,部门名
SELECT last_name department_name
FROM employees e
INNER JOIN departments d
ON e.department_id=d.department_id;
#与92的效果完全一致

#案例2:查询名字中包含a的员工名和工种名(添加筛选)
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:查询部门个数>3的城市名和部门个数(添加分组+筛选)
SELECT COUNT(*),city
FROM locations l
INNER JOIN departments d
ON l.`location_id`=d.`location_id`
GROUP BY city
HAVING COUNT(*)>3;

#案例4:查询哪个部门的部门员工个数>3的部门名和员工个数,并按个数降序(添加排序)
SELECT department_name,COUNT(*)
FROM departments d
INNER JOIN employees e
ON d.`department_id`=e.`department_id`
GROUP BY department_name
HAVING COUNT(*)>3 
ORDER BY COUNT(*) DESC;

#案例5:查询员工名、部门名、工种名,并按部门名降序(三表连接)
select last_name,department_name,job_title
from employees e
inner join departments d
on e.`department_id`=d.`department_id`
inner join jobs j
on j.`job_id`=e.`job_id`
order by department_name desc;

#(2)非等值连接
#案例1:查询员工的工资级别
SELECT salary,grade_level
FROM employees e
JOIN job_grades g
ON e.`salary` BETWEEN g.`lowest_sal`AND g.`highest_sal`;

#案例2:查询每个工资级别的个数>2的个数,并且按工资级别降序
SELECT grade_level,COUNT(*)
FROM employees e
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)自连接
 #案例1:查询姓名中包含字符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. 添加排序、分组、筛选
  2. inner可以省略
  3. 筛选条件放在where后面,连接条件放在on后面,提高分离性,便于阅读
  4. inner job连接和sql92的等值连接效果是一样的,都是便于查询多表的交集

2. 外连接

应用场景

用于查询一个表中有,另一个表没有的记录

特点

  1. 外连接的查询结果为主表中的所有记录,如果从表中有和它匹配的,则显示匹配的值,如果从表中没有和它匹配的,则显示null,外连接查询结果=内连接结果+主表中有而从表没有的记录
  2. 左外连接,left join的是主表,右外连接,right join右边的是主表
  3. 左外和右外交换两个表的顺序,可以实现同样的效果
    #引入:查询男朋友不在男神表的女神名
    #左外连接
    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;
    
    #案例1:查询哪个部门没有员工
    #左外
    SELECT d.*,employee_id,d.`department_name`
    FROM departments d
    LEFT OUTER JOIN employees e
    ON d.`department_id`=e.`department_id`
    GROUP BY d.`department_name`
    HAVING employee_id IS NULL;
    
    #右外
    SELECT d.*,employee_id,d.`department_name`
    FROM employees e
    LEFT OUTER JOIN departments d
    ON d.`department_id`=e.`department_id`
    GROUP BY d.`department_name`
    HAVING employee_id IS NULL;

3. 交叉连接

就是笛卡尔乘积

案例讲解

#案例1:查询编号>3的女神的男朋友信息,如果有则列出详细,如果没有,则用NULL填充
SELECT bo.*,b.id 女神编号,b.`name`
FROM boys bo
RIGHT OUTER JOIN beauty b
ON bo.`id`=b.`boyfriend_id`
WHERE b.id>3;

#案例2:查询哪个城市没有部门
SELECT city ,department_name
FROM locations l
LEFT OUTER JOIN departments d
ON d.`location_id`=l.`location_id`
WHERE department_name IS NULL;

#案例3:查询部门名为SAL或IT的员工信息
SELECT department_name,e.*
FROM departments d
LEFT OUTER JOIN employees e
ON e.`department_id`=d.`department_id`
WHERE d.`department_name` IN ('SAL' , 'IT');

进阶7:子查询

含义:

一条查询语句中又嵌套了另一条完整的select语句,其中被嵌套的select语句,(出现在其他语句中的select语句)称为子查询或内查询。

在外面的查询语句(外部的查询语句),称为主查询或外查询

分类

按子查询出现的位置

  1. select后面

仅仅支持标量子查询

  1. from后面

支持表子查询

  1. where 或having后面

支持标量子查询列子查询或行子查询(用得较少)

  1. exist后面(相关子查询)

支持表子查询

按结果集的行列数不同

  1. 标量子查询/单行子查询(结果集只有一行一列)
  2. 列子查询/多行子查询(结果集只有一列多行)
  3. 行子查询(结果集有一行多列)
  4. 表子查询(结果集一般为多行多列)

where或having后面

#1.标量子查询
#案例1:谁的工资比Abel高
#(1)查询Abel的工资
SELECT salary 
FROM employees
WHERE last_name='Abel';
#(2)查询员工的信息,满足salary大于1结果
SELECT *
FROM employees
WHERE salary>(
	SELECT salary 
	FROM employees
	WHERE last_name='Abel'
);

#案例2:返回job_id与141号员工相同,salary比143号员工多的员工 姓名,job_id 和工资
#(1)查询141号员工的job_id
SELECT job_id
FROM employees
WHERE employee_id=141;

#(2)查询143号员工的salary
SELECT salary
FROM employees
WHERE employee_id=143;

#(3)查询员工的姓名,job_id 和工资,要求job_id=1并且salary>2

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 department_id,MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary)>(
	SELECT MIN(salary)
	FROM employees e
	WHERE department_id=50	
);

#2.列子查询(多行子查询)(一列多行)
#操作符:
IN/NOT IN 等于列表中的任意一个(重要)
ANY/SOME 和子查询返回的某一个值比较
ALL 和子查询返回的所有值比较

#案例1:返回location_id 是1400或1700的部门中的所有员工姓名
#(1)location_id是1400或1700的部门编号
SELECT DISTINCT department_id
FROM departments
WHERE location_id IN(1400,1700);


#(2)查询员工姓名,要求部门号是1列表中的某一个
SELECT last_name
FROM employees
WHERE department_id IN(
	SELECT DISTINCT department_id
	FROM departments
	WHERE location_id IN(1400,1700)
);

SELECT last_name#或者用any替代in
FROM employees
WHERE department_id=ANY(
	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';

#案例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';

#3.行子查询(结果集一行多列或多行多列)
#案例:查询员工编号最小并且工资最高的员工信息
SELECT *
FROM employees
WHERE (employee_id,salary)=(
	SELECT MIN(employee_id),MAX(salary)
	FROM employees
);

#或者用上面的方法
SELECT *
FROM employees
WHERE employee_id=(
	SELECT MIN(employee_id)
	FROM employees
)
AND salary=(
	SELECT MAX(salary)
	FROM employees
);

SELECT后面

#案例1:查询每个部门的员工个数
SELECT d.*,(
SELECT COUNT(*)
FROM employees e
WHERE e.department_id=d.`department_id`) 个数
FROM departments d;

from后面

#案例1:查询每个部门的平均工资的工资等级
select av_g.*,g.`grade_level`
from (
	select avg(salary) av_s,department_id
	from employees
	group by department_id
) av_g
inner join job_grades g
on av_g.av_s between g.`lowest_sal`and g.`highest_sal`;

将子查询结果充当一张表,要求必须起别名

exist后面(相关子查询)

SELECT EXISTS(
	SELECT employee_id
	FROM employees
	);#是否存在,有值则1(true)
	
#语法:
exist(完整的查询语句)
结果: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
);

#案例2:查询没有女朋友的男神信息
#in
SELECT bo.*
FROM boys
WHERE bo.id NOT IN(
    SELECT boyfriend_id
    FROM beauty
)

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

特点

1、子查询都放在小括号内
2、子查询可以放在from后面、select后面、where后面、having后面,但一般放在条件的右侧
3、子查询优先于主查询执行,主查询使用了子查询的执行结果
4、子查询根据查询结果的行数不同分为以下两类:
① 单行子查询
	结果集只有一行
	一般搭配单行操作符使用:> < = <> >= <= 
	非法使用子查询的情况:
	a、子查询的结果为一组值(非一行一列)
	b、子查询的结果为空
	
② 多行子查询
	结果集有多行
	一般搭配多行操作符使用:any、all、in、not in
	in: 属于子查询结果中的任意一个就行
	any和all往往可以用其他查询代替

案例

#案例1:查询和Zlotkey相同部门的员工姓名和工资
SELECT last_name,salary
FROM employees
WHERE department_id=(
	SELECT department_id
	FROM employees
	WHERE last_name='Zlotkey'
);

#案例2:查询工资比公司平均工资高的员工的员工号、姓名和工资
SELECT employee_id,last_name,salary
FROM employees
WHERE salary>(
	SELECT AVG(salary)
	FROM employees
);

#案例3:查询各部门中工资比本部门平均工资高的员工的员工号、姓名和工资
    SELECT employee_id,last_name,salary,e.department_id
FROM employees e
INNER JOIN (
	SELECT AVG(salary) av_sal,department_id
	FROM employees
	GROUP BY department_id
) ag_dep
ON e.`department_id`=ag_dep.department_id
WHERE salary>av_sal;

#案例4:查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名
SELECT employee_id,last_name
FROM employees
WHERE department_id IN(
	SELECT DISTINCT department_id
	FROM employees
	WHERE last_name LIKE '%u%'
);

#案例5:查询部门的location_id为1700的部门工作的员工的员工号
#子查询
SELECT employee_id
FROM employees
WHERE department_id IN( #注意IN前面不用加等于号,只有any和all才需要加
	SELECT department_id
	FROM departments
	WHERE location_id=1700
);

#内连接
FROM employees e
INNER JOIN departments d
ON e.`department_id`=d.`department_id`
WHERE d.location_id=1700;

#案例6:查询管理者是king的员工姓名和工资
SELECT last_name,salary
FROM employees
WHERE manager_id IN(
	SELECT employee_id
	FROM employees
	WHERE last_name='K_ing'
);

#案例7:查询工资最高的员工的姓名,要求first_name和last_name显示为一列,列名为姓,名
SELECT CONCAT(first_name,last_name) '姓,名'
FROM employees
WHERE salary=(
	SELECT MAX(salary)
	FROM employees
);

进阶8:分页查询

应用场景

实际的web项目中需要根据用户的需求提交对应的分页查询的sql语句
(当要显示的数据,一页显示不全,需要分页提交sql请求)

语法

select 字段|表达式,...(查询列表)
from 表
【where 条件】
【group by 分组字段】
【having 条件】
【order by 排序的字段】
limit 【要显示的起始条目索引(起始索引从0开始),】条目数;


#案例1:查询前五条员工信息
SELECT *
FROM employees
LIMIT 0,5;#5是条目个数,不是结束索引,如果是第一条开始,起始条目可省略

#案例2 查询第11条-第25条
SELECT *
FROM employees
LIMIT 10,15;#15=25-11+1,并且注意第11条的起始条目是10而不是11

#案例3 有奖金的员工信息,并且工资较高的前10名显示出来
SELECT *
FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary DESC
LIMIT 0,10;

特点

1.起始条目索引从0开始

2.limit子句放在查询语句的最后

3.公式:select * from  表 limit (page-1)*sizePerPage,sizePerPage
假如:
每页显示条目数sizePerPage
要显示的页数 page

经典案例

#案例1:查询工资最低的员工信息:last_name,salary
SELECT last_name,salary
FROM employees
WHERE salary=(
	SELECT MIN(salary)
	FROM employees
);

#案例2:查询平均工资最低的部门信息
#方法一
SELECT d.*
FROM departments d
WHERE d.`department_id`=(
	SELECT department_id
	FROM employees
	GROUP BY department_id
	HAVING AVG(salary)=(
	SELECT MIN(ag)
		FROM (
			SELECT AVG(salary) ag,department_id
			FROM employees
			GROUP BY department_id
		) ag_dep
	)
);

#方法二
SELECT d.*
FROM departments d
WHERE d.`department_id`=(
	SELECT department_id
	FROM employees
	GROUP BY department_id
	ORDER BY AVG(salary)
	LIMIT 0,1
);

#案例3:查询平均工资最低的部门信息和该部门的平均工资

SELECT d.*,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`;

#案例4:查询平均工资最高的job信息
SELECT j.*
FROM jobs j
WHERE job_id=(
	SELECT job_id
	FROM employees
	GROUP BY job_id
	ORDER BY AVG(salary) DESC
	LIMIT 1
);

#案例5:查询平均工资高于公司平均工资的部门有哪些
SELECT department_id,AVG(salary)
FROM employees
GROUP BY department_id
HAVING AVG(salary) >(
	SELECT AVG(salary)
	FROM employees
)

#案例6:查询出公司中所有manager的详细信息
SELECT *
FROM employees
WHERE employee_id IN(
	SELECT DISTINCT manager_id
	FROM employees
);

#案例7:各个部门中,最高工资中最低的那个部门的最低工资是多少
SELECT MIN(salary)
FROM employees
WHERE department_id=(
	SELECT department_id
	FROM employees
	GROUP BY department_id
	ORDER BY MAX(salary) 
	LIMIT 1
)

#案例8:查询平均工资最高的部门的manager的详细信息:last_name,department_id,email,salary
SELECT last_name,d.department_id,email,salary
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
);#这里有歧义,题意是查出部门的最高的manager,如果只是查部门的manager会有三个。

进阶9:联合查询

引入

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;

语法

select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】
select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】
select 字段|常量|表达式|函数 【from 表】 【where 条件】 union  【all】
.....
select 字段|常量|表达式|函数 【from 表】 【where 条件】

特点

1、多条查询语句的查询的列数必须是一致的
2、多条查询语句的查询的列的类型几乎相同
3、union代表去重,union all代表不去重

应用场景

要查询的结果来自于多个表,且多个表没有直接的连接关系,但查询的信息一致时

posted @ 2020-12-30 11:06  loading_hlb  阅读(534)  评论(0编辑  收藏  举报