DQL语句

分组查询

讲解

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
/*
	语法:
		select 查询列表
		from 表
		【where 筛选条件】
		group by 分组的字段
		【order by 排序的字段】;
	特点:
		1、和分组函数一同查询的字段必须是group by后出现的字段
		2、筛选分为两类:分组前筛选和分组后筛选
						    针对的表			   位置		 连接的关键字
			分组前筛选	      原始表			  group by前	     where
				
			分组后筛选	 group by后的结果集    	 group by后	   having

			问题1:分组函数做筛选能不能放在where后面
				答:不能

			问题2:where——group by——having
				一般来讲,能用分组前筛选的,尽量使用分组前筛选,提高效率
		3、分组可以按单个字段也可以按多个字段
		4、可以搭配着排序使用
*/

#引入:查询每个部门的员工个数
	SELECT AVG(salary) 
    FROM employees
	WHERE department_id=90;
 
#1.简单的分组
	#案例1:查询每个工种的员工平均工资
		SELECT job_id, AVG(salary) AS'各部门平均工资'
        FROM employees
        GROUP BY job_id
        ORDER BY 各部门平均工资;
        
	#案例2:查询每个位置的部门个数
		SELECT COUNT(*), location_id
        FROM departments
        GROUP BY location_id;

#2、可以实现分组前的筛选
	#案例1:查询邮箱中包含a字符的 每个部门的最高工资        
		SELECT MAX(salary), department_id
        FROM employees
        WHERE email LIKE '%a%'
        GROUP BY department_id;
        
    #案例2:查询有奖金的每个领导手下员工的平均工资
		SELECT AVG(salary), manager_id
        FROM employees
        WHERE commission_pct IS NOT NULL
        ORDER BY manager_id;
        
#3、分组后筛选
	#案例1:查询哪个部门的员工个数>30
		#①查询每个部门的员工个数  
			SELECT COUNT(*), department_id
			FROM employees
			GROUP BY department_id;
		#② 筛选刚才①结果
			SELECT COUNT(*), department_id
			FROM employees
			GROUP BY department_id
			HAVING COUNT(*) > 30;
			
	#案例2:每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
		SELECT job_id, MAX(salary)
		FROM employees
		WHERE commission_pct IS NOT NULL
		GROUP BY job_id
		HAVING MAX(salary)> 12000;
		
	#案例3:领导编号>102的每个领导手下的最低工资大于5000的领导编号和最低工资
		SELECT manager_id, MIN(salary)
		FROM employees
		WHERE manager_id > 102
		GROUP BY manager_id
		HAVING MIN(salary) > 5000;
		
#4.添加排序
	#案例:每个工种有奖金的员工的最高工资>6000的工种编号和最高工资,按最高工资升序
		SELECT job_id, MAX(salary)
        FROM employees
        WHERE commission_pct IS NOT NULL 
        GROUP BY job_id
        HAVING MAX(salary) > 6000
        ORDER BY MAX(salary);
        
 #5.按多个字段分组
	#案例:查询每个工种每个部门的最低工资,并按最低工资降序
		SELECT job_id, department_id, MIN(salary) AS '最低工资'
        FROM employees
        GROUP BY job_id, department_id
        ORDER BY 最低工资 DESC;

#使用WITH ROLLUP关键字,可以得到每个分组以及每个分组汇总级别(针对每个分组)的值
    SELECT department_id, COUNT(*) AS NUM_DEPART
    FROM employees
    GROUP BY department_id WITH ROLLUP;

习题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#1.查询各job_id的员工工资的最大值,最小值,平均值,总和,并按job_id升序
	SELECT job_id, MAX(salary), MIN(salary), AVG(salary), SUM(salary)
    FROM employees
    GROUP BY job_id
    ORDER BY job_id;
 
 #2.查询员工最高工资和最低工资的差距(DIFFERENCE)
	SELECT (MAX(salary) - MIN(salary)) AS 'DIFFERENCE'
    FROM employees;
    
#3.查询各个管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内
	SELECT manager_id, MIN(salary) AS '最低工资'
    FROM employees
    WHERE manager_id IS NOT NULL
    GROUP BY manager_id
    HAVING 最低工资 >= 6000;
    
#4.查询所有部门的编号,员工数量和工资平均值,并按平均工资降序
	SELECT DISTINCT department_id, COUNT(*), AVG(salary) AS '平均工资'
    FROM employees
    GROUP BY department_id
    ORDER BY 平均工资 DESC;
 
 #5.选择各个job_id的员工人数
	SELECT job_id, COUNT(*)
    FROM employees
    GROUP BY job_id;

连接查询

总述

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
	含义:又称多表查询,当查询的字段来自于多个表时,就会用到连接查询
	笛卡尔乘积现象:表1 有m行,表2有n行,结果=m*n行
        发生原因:没有有效的连接条件
        如何避免:添加有效的连接条件
	分类:
		按年代分类:
			sql92标准:仅仅支持内连接
			sql99标准【推荐】:支持内连接+外连接(左外和右外)+交叉连接		
		按功能分类:
			内连接:
				等值连接
				非等值连接
				自连接
			外连接:
				左外连接
				右外连接
				全外连接			
			交叉连接
*/
SELECT * FROM beauty;
SELECT * FROM boys; 
SELECT * FROM admin;

SELECT 
	NAME,
    boyName FROM boys,
    beauty
WHERE beauty.boyfriend_id= boys.id;

sql92标准

讲解
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
#一、等值连接
	/*
		① 多表等值连接的结果为多表的交集部分
		②n表连接,至少需要n-1个连接条件
		③ 多表的顺序没有要求
		④一般需要为表起别名
		⑤可以搭配前面介绍的所有子句使用,比如排序、分组、筛选
	*/
	#案例1:查询女神名和对应的男神名
		SELECT NAME,boyName 
		FROM boys,beauty
		WHERE beauty.boyfriend_id= boys.id;		
    #案例2:查询员工名和对应的部门名
		USE myemployees;
		SELECT first_name, department_name
		FROM employees, departments
		WHERE employees.department_id = departments.department_id;

	#2、为表起别名
		/*
			①提高语句的简洁度
			②区分多个重名的字段

			注意:如果为表起了别名,则查询的字段就不能使用原来的表名去限定
		*/
		#查询员工名、工种号、工种名
			SELECT e.first_name, e.job_id, j.job_title
			FROM employees e, jobs j
			WHERE e.job_id = j.job_id;
		
	#3、两个表的顺序可以调换
		#查询员工名、工种号、工种名
			SELECT e.first_name, e.job_id, j.job_title
			FROM jobs j, employees e
			WHERE e.job_id = j.job_id;
	
	#4、可以加筛选
		#案例1:查询有奖金的员工名、部门名
			SELECT e.first_name, d.department_name, commission_pct
			FROM employees e, departments d
			WHERE e.department_id = d.department_id
			AND commission_pct IS NOT NULL;
		#案例2:查询城市名中第二个字符为o的部门名和城市名
			SELECT d.department_name, l.city
			FROM departments d, locations l
			WHERE d.location_id = l.location_id
			AND l.city LIKE '_o%';
			
	  #5、可以加分组
		#案例1:查询每个城市的部门个数  
			SELECT COUNT(*) AS '个数', city
			FROM departments d, locations l
			WHERE d.location_id = l.location_id
			GROUP BY city;
		 #案例2:查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资
			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 d.department_name;
	
	#6、可以加排序
		#案例:查询每个工种的工种名和员工的个数,并且按员工个数降序
			SELECT job_title, COUNT(*) AS '个数'
			FROM employees e, jobs j
			WHERE e.job_id = j.job_id
			GROUP BY job_title
			ORDER BY 个数 DESC;
	
	#7、可以实现三表连接?
		#案例:查询员工名、部门名和所在的城市
			SELECT first_name, department_name, city
			FROM departments d, employees e, locations l
			WHERE e.department_id = d.department_id
			AND d.location_id = l.location_id
			AND city LIKE 's%'
			ORDER BY department_name DESC;
	
#二、非等值连接
	#案例1:查询员工的工资和工资级别
		CREATE TABLE job_grades
			(grade_level VARCHAR(3),
			 lowest_sal  int,
			 highest_sal int);

		INSERT INTO job_grades
		VALUES 
			('A', 1000, 2999),
			('B', 3000, 5999),
			('C', 6000, 9999),
			('D', 10000, 14999),
			('E', 15000, 24999),
			('F', 25000, 40000);
        
		SELECT first_name, salary, grade_level
		FROM employees e, job_grades g
		WHERE salary BETWEEN g.`lowest_sal` AND g.`highest_sal`
		AND g.`grade_level`='B';

#三、自连接
	#案例:查询 员工名和上级的名称
		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`;		
习题
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#1.显示所有员工的姓名,部门号和部门名称。
	SELECT first_name, d.department_id, department_name
    FROM employees e, departments d
    WHERE d.department_id = e.department_id;
    
#2.查询90号部门员工的job_id和90号部门的location_id
	SELECT job_id, location_id
    FROM employees e, departments d
    WHERE e.department_id = d.department_id
    AND e.department_id = 90;
	
#3.	选择所有有奖金的员工的last_name , department_name , location_id , city
	SELECT last_name , department_name , d.location_id , city
    FROM employees e, departments d, locations l
    WHERE e.department_id = d.department_id
    AND d.location_id = l.location_id
    AND commission_pct IS NOT NULL;
	
#4.选择city在Toronto工作的员工的last_name , job_id , department_id , department_name
	SELECT last_name, job_id, e.department_id, department_name
	FROM employees e, departments d, locations l
    WHERE e.department_id = d.department_id
    AND l.location_id = d.location_id
    AND l.city = 'Toronto';
        
#5.查询每个工种、每个部门的部门名、工种名和最低工资
	SELECT department_name, job_title, MIN(salary) AS '最低工资'
    FROM employees e, departments d, jobs j
    WHERE e.job_id = j.job_id
    AND e.department_id = d.department_id
    GROUP BY department_name, job_title;
    
#6.查询每个国家下的部门个数大于2的国家编号
	SELECT country_id, COUNT(*)
    FROM locations l, departments d
    WHERE l.location_id = d.location_id
    GROUP BY country_id
    HAVING COUNT(*) > 2;
	
#7、选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号,结果类似于下面的格式
    /*
        employees	Emp#	manager	Mgr#
        kochhar		101		k_ing	100
    */
	SELECT 
		e.last_name AS 'employees', 
        e.employee_id AS 'Emp#',
        m.last_name AS 'manager', 
        m.employee_id AS 'Mgr#'
	FROM employees e, employees m
    WHERE e.manager_id = m.manager_id
    AND e.last_name = 'kochhar';

sql99语法

总述

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
    含义: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
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*
    语法:
        SELECT 查询列表
        FROM 表1 别名
        INNER JOIN 表2 别名
        ON 连接条件;
    分类:
        等值
        非等值
        自连接
    特点:
        ①添加排序、分组、筛选
        ②INNER可以省略
        ③筛选条件放在WHERE后面,连接条件放在ON后面,提高分离性,便于阅读
        ④INNER JOIN连接和sql92语法中的等值连接效果是一样的,都是查询多表的交集
*/

#1、等值连接
	#案例1.查询员工名、部门名
		SELECT first_name, department_name
        FROM employees e
        INNER JOIN departments d
        ON e.department_id = d.department_id;
	#案例2.查询名字中包含e的员工名和工种名(添加筛选)
		SELECT first_name, job_title
        FROM employees e
        INNER JOIN jobs j
        ON e.job_id = j.job_id
        WHERE first_name LIKE '%e%';
	#案例3. 查询部门个数>3的城市名和部门个数,(添加分组+筛选)
		#①查询每个城市的部门个数
		#②在①结果上筛选满足条件的
        SELECT city, COUNT(*)
        FROM departments d
        INNER JOIN locations l
        ON d.location_id = l.location_id
        GROUP BY city
        HAVING COUNT(*) > 3;
	#案例4.查询哪个部门的员工个数>3的部门名和员工个数,并按个数降序(添加排序)
		#①查询每个部门的员工个数
			SELECT department_name, COUNT(*)
            FROM departments d
            INNER JOIN employees e
            ON e.department_id = d.department_id
            GROUP BY department_name;
		#② 在①结果上筛选员工个数>3的记录,并排序
			SELECT department_name, COUNT(*)
            FROM departments d
            INNER JOIN employees e
            ON e.department_id = d.department_id
            GROUP BY department_name
            HAVING COUNT(*) > 3
            ORDER BY COUNT(*) DESC;			
    #案例5.查询员工名、部门名、工种名,并按部门名降序(添加三表连接)
		SELECT first_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;

#二)非等值连接
	#案例1:查询员工的工资级别
		SELECT first_name, grade_level
        FROM employees e
        INNER JOIN job_grades g
        ON e.salary BETWEEN g.lowest_sal AND g.highest_sal;
	#案例2:查询工资级别的个数>20的个数,并且按工资级别降序
		SELECT grade_level, COUNT(*)
        FROM employees e
        INNER JOIN job_grades j
        ON e.salary BETWEEN j.lowest_sal AND j.highest_sal
        GROUP BY grade_level
        HAVING COUNT(*) > 20
        ORDER BY grade_level;
	
#三)自连接
	#案例1:查询员工的名字、上级的名字
		SELECT e.first_name, m.first_name
        FROM employees e
        INNER JOIN employees m
        ON e.manager_id = m.manager_id;
	#案例2:查询姓名中包含字符k的员工的名字、上级的名字
		SELECT e.first_name, m.first_name
        FROM employees e
        INNER JOIN employees m
        ON e.manager_id = m.manager_id
        WHERE e.first_name LIKE '%k%';

外连接

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 /*
	 应用场景:用于查询一个表中有,另一个表没有的记录	 
	 特点:
		 1、外连接的查询结果为主表中的所有记录
			如果从表中有和它匹配的,则显示匹配的值
			如果从表中没有和它匹配的,则显示null
			外连接查询结果=内连接结果+主表中有而从表没有的记录
		 2、左外连接,left join左边的是主表
			右外连接,right join右边的是主表
		 3、左外和右外交换两个表的顺序,可以实现同样的效果 
		 4、全外连接 = 内连接的结果 + 表1中有但表2没有的 + 表2中有但表1没有的
 */

#引入:查询男朋友 不在男神表的的女神名
	USE girls;
	SELECT * FROM beauty;
	SELECT * FROM boys;

#左外连接
	SELECT b.*,bo.*
	FROM beauty b
	LEFT OUTER JOIN boys bo
	ON b.`boyfriend_id` = bo.`id`
	WHERE bo.`id` IS NULL;
#右外连接
	SELECT b.*,bo.*
	FROM boys bo
	RIGHT OUTER JOIN beauty b
	ON b.`boyfriend_id` = bo.`id`
	WHERE bo.`id` IS NULL;

	USE myemployees;
	#案例1:查询哪个部门没有员工
		#左外
			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不支持)
			USE girls;
			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和 sql99pk
	/*
		功能:sql99支持的较多
		可读性:sql99实现连接条件和筛选条件的分离,可读性较高
	*/  

习题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#一、查询编号>3的女神的男朋友信息,如果有则列出详细,如果没有,用null填充
	USE girls;
    SELECT b.id, b.name, bo.*
    FROM beauty b
    LEFT OUTER JOIN boys bo
    ON b.boyfriend_id = bo. id
    WHERE b.id > 3;

#二、查询哪个城市没有部门
	USE myemployees;
	SELECT city
    FROM locations l
    LEFT OUTER JOIN departments d
    ON l.location_id = d.location_id
    WHERE d.department_id IS NULL;
	#或者
    SELECT city
	FROM departments d
	RIGHT OUTER JOIN locations l 
	ON d.`location_id`=l.`location_id`
	WHERE  d.`department_id` IS NULL;

#三、查询部门名为SAL或IT的员工信息
	SELECT e.*, d.department_name, d.`department_id`
	FROM departments  d        #用departments表做主表,因为部门一定存在,但部门不一定有员工
	LEFT JOIN employees e
	ON d.`department_id` = e.`department_id`
	WHERE d.`department_name` IN('SAL','IT');