MySQL尚硅谷学习笔记之DQL语言

一、DQL语言的学习

  DQL(Data Query Language 数据查询语言):查询数据库数据,一般采用select语句

  1.1 myemployees(员工)库介绍

  该库当中存在五张表,分别是员工表、部门表、位置表、工种表和工资等级

  1. employees :员工表

列名类型注释
employee_id【主键】int(6)员工编号
first_namevarchar(20)
last_namevarchar(25)姓【一般用这个】
emailvarchar(25)邮箱
phone_namevarchar(20)电话号码
job_idvarchar(10)工种编号【职业类别】
salarydouble(10,2)薪资
commission_pctdouble(4,2)奖金率【年薪=月薪×12(1+奖金率)】
manager_idint(6)上级领导的员工编号
department_idint(4)部门编号
hiredatedatetime入职日期

  2. departments:部门表

列名类型注释
department_id【主键】int(4)部门编号
department_namevarchar(3)部门名称
manager_idint(6)部门领导的员工编号
location_idint(4)位置编号

  3. locations:位置表

列名类型注释
location_id【主键】int(11)位置编号
street_addressvarchar(40)街道
postal_codevarchar(12)邮编
cityvarchar(30)城市
state_provincevarchar(25)州/省
country_idvarchar(2)国家编号

  4. jobs:工种表

列名类型注释
job_id【主键】varchar(10)工种编号
job_titlevarchar(35)工种名称
min_salaryint(6)最低工资
max_salaryint(6)最高工资

  5. job_grades:工资等级表

列名类型注释
grade_levelvarchar(3)工资等级
lowest_salint(11)最低工资
highest_salint(11)最高工资

1.2 基础查询

  • 语法:

    select 查询列表from 表名;

  • 特点:

    1、查询列表可以是:表中的字段、常量值、表达式、函数 ​ 2、查询的结果是一个虚拟的表格

USE myemployees; #使用myemployees库
DESC employees; #显示表employees的结构
​
#1.查询表中的单个字段
    SELECT last_name FROM employees;
#2.查询表中的多个字段
    SELECT last_name,salary,email FROM employees;
#3.查询表中的所有字段
    SELECT * FROM employees;
#4.查询常量值
    SELECT 100;
    SELECT 'john';
#5.查询表达式
    SELECT 100*98; #9800
#6.查询函数
    SELECT VERSION(); #查询当前版本
    
#7.起别名
/*
    ①便于理解,列头名可以变为想要的别名
    ②如果要查询的字段有重名的情况,使用别名可以区分开来
*/
  #方式一:使用as
    SELECT 100%98 AS 结果;
    SELECT last_name AS 姓,first_name AS 名 FROM employees;
  #方式二:使用空格
    SELECT last_name 姓,first_name 名 FROM employees;
​
  #案例:查询salary,显示结果为out put 
    SELECT salary AS `out put` FROM employees;
    
#8.distinct去重
  #案例:查询员工表中涉及到的所有的部门编号
    SELECT DISTINCT department_id FROM employees;
    
#9.+号的作用
/*
    mysql中的+号:
    仅仅只有一个功能:只能用于运算符
​
    select 100+90:两个操作数都为数值型,则做加法运算
    select '123'+90:其中一方为字符型,试图将字符型数值转换成数值型
                            如果转换成功,则继续做加法
    select 'john'+90        如果失败,则将字符型数值转换成0
    select null+0;只要其中一方为null,则结果肯定为null
*/
​
#10.使用concat实现连接
    SELECT CONCAT('a','b','c') AS 结果; #abc
  #案例:查询员工名和姓连接成一个字段,并显示为 姓名
    SELECT CONCAT(last_name,first_name) AS 姓名 FROM employees;
    
#11. 判断是否为空
  #案例:若奖金率为null,则赋值为0
    SELECT IFNULL(commission_pct,0) AS 奖金率 FROM employees;

1.3 条件查询

  • 语法:

        select 查询列表 from 表名where 筛选条件;

  • 分类:

        一、按条件表达式筛选

                条件运算符: > < = != <> >= <=

        二、按逻辑表达式筛选

                  逻辑运算符: 用于连接条件表达式

                        1、&& 或 and: 两个条件都为true, 结果为true, 反之为false

                        2、|| 或 or: 只要有一个条件为true,结果为true,反之为false

                        3、!或 not:如果连接的条件本身为false,结果为true,反之为false

        三、模糊查询(不是精确匹配)

                1、like: 一般和通配符搭配使用(通配符:% 任意多个字符,包含0个字符; _ 任意单个字符)

                2、between and

                        ①使用between and 可以提高语句的简洁度

                        ②包含临界值

                        ③两个临界值不要调换顺序

                3、 in: 判断某字段的值是否属于in列表中的某一项

                        ①使用in提高语句的简洁度

                        ②in列表的值类型必须一致或者兼容

                4、is null | is not null:是否为null

                        ①=或<>不能用于判断null值

                        ②is null 或 is not null 可以判断null

#一、按条件表达式筛选
​
    #案例1:查询工资>12000的员工信息
    SELECT * FROM employees WHERE salary >12000;
        
    #案例2:查询部门编号不等于90号的员工名和部门编号
    SELECT last_name,department_id FROM employees 
    WHERE department_id != 90;
        
#二、按照逻辑表达式筛选
​
    #案例1:查询工资在10000到20000之间的员工名、工资以及奖金
    SELECT last_name,salary,commission_pct
    FROM employees
    WHERE salary>=10000 AND salary<=20000;
        
    #案例2:查询部门编号不是在90到110之间,或者工资高于15000的员工信息
    SELECT * FROM employees
    WHERE NOT(department_id >=90 AND department_id<=110) OR salary >15000;      
       
#三、模糊查询
​
  #1.like
    #案例1:查询员工名中包含字符a的员工信息
    SELECT * FROM employees 
    WHERE last_name LIKE '%a%';
        
    #案例2:查询员工名中第三个字符为e,第五个字符为a的员工名和工资
    SELECT last_name,salary FROM employees
    WHERE last_name LIKE '__n_l%';
        
    #案例3:查询员工名中第二个字符为_的员工名
    SELECT last_name FROM employees
    WHERE last_name LIKE '_\_%';
        
  #2.between and
    #案例1:查询员工编号在100到120之间的员工信息
    SELECT * FROM employees
    WHERE employee_id BETWEEN 100 AND 120;
        
  #3.in(有可能的值)
    #案例1:查询员工的工种编号是IT_PROG、AD_VP、AD_PRES中的一个员工名和工种编号
    SELECT last_name,job_id FROM employees
    WHERE job_id IN ('IT_PROG','AD_VP','AD_PRES')
        
  #4.is null
    #案例1:查询没有奖金的员工名和奖金率
    SELECT last_name,commission_pct FROM employees
    WHERE commission_pct IS NULL
        
#四、额外补充
    #1. 查询员工号为176的员工姓名和部门号和年薪
    SELECT last_name,department_id,
    salary*12*(1+IFNULL(commission_pct,0)) 年薪
    FROM employees 
    WHERE employee_id=176
    #注意年薪是12*月工资*(1+奖金率)

1.4 排序查询

  • 语法:

            select 查询列表 from 表名 【where 筛选条件order by 排序列表【asc|desc】

  • 特点:

                1、asc代表的是升序,desc代表的是降序,如果不写,默认是升序

                2、order by 子句中可以支持单个字段、多个字段、表达式、函数、别名

                3、order by子句一般是放在查询语句的最后面,limit子句除外

#1:查询员工信息,要求工资从高到低排序
    SELECT * FROM employees ORDER BY salary DESC;
​
#2、添加筛选条件
    #查询部门编号>=90的员工信息,按入职时间的先后进行排序
    SELECT * FROM employees
    WHERE department_id >=90
    ORDER BY hiredate;
        
#3、按表达式排序
    #按年薪的高低显示员工的信息和年薪
    SELECT *,salary*12*(1+IFNULL(commission_pct,0)) 年薪
    FROM employees
    ORDER BY salary*12*(1+IFNULL(commission_pct,0)) DESC
        
#4、按别名排序
    #按年薪的高低显示员工的信息和年薪
    SELECT *,salary*12*(1+IFNULL(commission_pct,0)) 年薪
    FROM employees
    ORDER BY 年薪 DESC
        
#5、按函数排序
    #按姓名的长度显示员工的姓名和工资
    SELECT  last_name,salary FROM employees
    ORDER BY LENGTH(last_name) DESC;    
        
#6、按多个字段排序
    #查询员工信息,要求先按工资升序,再按员工编号降序
    SELECT * FROM employees
    ORDER BY salary ASC,employee_id DESC;

1.5 常见函数

  • 语法

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

  • 概念:

                将一组逻辑语句封装在方法体中,对外暴露方法名,因此能隐藏实现细节,并提高代码的重用性

  • 分类:

    一、单行函数

            字符函数、数学函数、日期函数,每一行对应一个值

    二、分组函数

            1、功能:用于做统计使用,又称为统计函数/聚合函数/组函数,最终给出一个值

            2、分类:sum 求和,avg 平均值, max最大值,min最小值,count 计算个数

            3、特点

                    ①sum,avg一般用于处理数值型,max,min,count可以处理任何类型

                    ②以上分组函数都忽略null值

                    ③可以和distinct搭配实现去重的运算

                    ④一般使用count(*)用作统计行数

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

###单行函数
#一、字符函数
    #1.length:获取参数值的字节个数
    SELECT LENGTH('john');# 4
    SELECT LENGTH('张三丰'); # 9:一个汉字占3个字节
        
    #2.concat:拼接字符串
    SELECT CONCAT(last_name,'_',first_name) 
    FROM employees; # k_ing
        
    #3.upper、lower
    SELECT UPPER('john'); #变大写,JOHN
    SELECT LOWER('JOHN'); #变小写,john
    
    #案例:将姓变大写,名变小写,然后拼接
    SELECT CONCAT(UPPER(last_name) ,LOWER(first_name) ) 姓名
    FROM employees
            
    #4. substr/substring:
    #substr(str,pos)截取从指定索引处后面所有字符,注意:索引从1开始
    SELECT SUBSTR('李莫愁爱上了陆展元',6) out_put; #了陆展元
    #substr(str,pos,len)截取从指定索引处指定长度的字符 
    SELECT SUBSTR('李莫愁爱上了陆展元',1,3) out_put;#从1开始,截取3个字符,李莫愁
​
    #案例:姓名中首字符大写,其他字符小写,然后用_拼接,显示出来
    SELECT  CONCAT(UPPER(SUBSTR(last_name,1,1)),'_',LOWER(SUBSTR(last_name,2))) 姓名
    FROM employees;
    
    #5. instr(str,substr):返回子串第一次出现的索引,如果找不到返回0
    SELECT INSTR('杨不悔爱上了殷六侠','殷六侠') AS out_put
​
    #6. trim:去掉前后空格
    SELECT LENGTH(TRIM('   张翠山    ')) AS out_put; # 9
    SELECT TRIM('a' FROM 'aaaaa张翠山aaa') AS out_put;#把a去掉,张翠山
    
    #7.lpad(str,len,padstr):用指定的字符实现左填充指定长度
    SELECT LPAD('殷素素',6,'*') AS out_put; #***殷素素
    
    #8.rpad(str,len,padstr):用指定的字符实现右填充指定长度
    SELECT RPAD('殷素素',6,'ab') AS out_put;#殷素素aba
    
    #9. replace(str,from_str,to_str):替换
    SELECT REPLACE('周芷若张无忌爱上了周芷若','周芷若','赵敏') AS out_put;#赵敏赵敏张无忌爱上了赵敏
​
#二、数学函数
    #1.round:四舍五入
    SELECT ROUND(1.65);# 2
    SELECT ROUND(1.567,2);#小数点后保留2位并四舍五入,1.57
    
    #2.ceil:向上取整,返回>=该参数的最小整数
    SELECT CEIL(1.52); #2
    
    #3.floor:向下取整,返回<=该参数的最小整数
    SELECT FLOOR(1.52); #1
    
    #4.truncate:截断,不会四舍五入
    SELECT TRUNCATE(1.65,1); #1.6
    
    #5.mode:取余
    #mod(a,b):a-a/b*b
    SELECT MOD(10,-3); #-1
​
#三、日期函数
    #1.now:返回当前系统日期+时间
    SELECT NOW();#2022-11-16 14:11:14
​
    #2.curdate:返回当前系统日期,不包含时间
    SELECT CURDATE();#2022-11-16
​
    #3.curtime:返回当前时间,不包含日期
    SELECT CURTIME(); #14:12:34
​
    #4.可以获取指定的部分,年、月、日、小时、分钟、秒
    SELECT YEAR/MONTH(NOW())年;
    
    #5.str_to_date:将日期格式的字符转换成指定格式的日期
    SELECT STR_TO_DATE('1999-1-1','%Y-%d-%m')
​
    #查询入职日期为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');
​
    #6.date_format:将日期转换成字符
    SELECT DATE_FORMAT(NOW(),'%y年%m月%d日') AS output;
    #查询有奖金的员工名和入职日期(xx月/xx日 xx年)
    SELECT last_name,DATE_FORMAT(hiredate,'%m月/%d日 %Y年') 入职日期 FROM employees
    WHERE commission_pct IS NOT NULL;
​

###分组函数
#1.简单使用
    SELECT SUM(salary) FROM employees;
    SELECT SUM(salary) 和,AVG(salary) 平均, COUNT(salary) 个数
    FROM employees;
​
    #2. 和distinct搭配
    SELECT SUM(DISTINCT salary),SUM(salary) FROM employees;
    SELECT COUNT(DISTINCT salary) FROM employees; #判断有几类工资
​
    #3.count函数的详细介绍
    SELECT COUNT(salary) FROM employees;
    #查询表里总行数
    SELECT COUNT(*) FROM employees; 
    SELECT COUNT(1) FROM employees;

1.6 分组查询

  • 语法:可以使用group by子句将表中的数据分成若干组

  select 分组函数, 列名(要求出现在group by 的后面),

  from 表名

【where 筛选条件】 ​      

  group by 分组的列表

【order by 排序列表【asc|desc】  

  • 注意:

       查询列表必须特殊,要求是分组函数group by 后出现的字段, “每个”后面的字段就是group by后面的分组列表。

  • 关键词:

      出现【每个】、【哪个】,分组查询主要就是先分组,然后再去组里面使用分组函数进行筛选,这样每一组都只会有一条数据,然后把所有组的数据再显示出来。

  • 特点:

        1、分组查询中的筛选条件分为两类:

数据源位置关键字
分组前筛选原始表group by子句的前面where
分组后筛选分组后的结果集group by 子句的后面having

                ① 分组函数做条件肯定是放在having子句中

                ②能用分组前筛选的,就有限考虑使用分组前筛选

        2、group by子句支持单个字段分组,多个字段分组(多个字段之间用逗号隔开,没有顺序要求),表达式或函数(用得较少)

        3、也可以添加排序(排序放在整个分组查询的最后)

#引入:查询每个部门的平均工资
    SELECT AVG(salary),department_id
    FROM employees
    GROUP BY department_id;
    
#1.简单的分组查询
    #案例1:查询每个工种的最高工资
    SELECT MAX(salary),job_id FROM employees
    GROUP BY job_id;
    
    #案例2:查询每个位置上的部门个数
    SELECT location_id,COUNT(*) FROM departments
    GROUP BY location_id;
​
#2.添加筛选条件
    #案例1:查询邮箱中包含a字符的,每个部门的平均工资
    SELECT department_id,AVG(salary)
    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;
    
#3.添加分组后的筛选
    #案例1:查询哪个部门的员工个数>2
        #①查询每个部门的员工个数
        SELECT department_id,COUNT(*) 
        FROM employees 
        GROUP BY department_id;
        
        #②根据①的结果进行筛选,查询哪个部门的员工个数>2
        SELECT department_id,COUNT(*) 
        FROM employees 
        GROUP BY department_id 
        HAVING COUNT(*)>2;
        
    #案例2:查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
        #①查询每个工种有奖金的员工的最高工资
        SELECT MAX(salary),employee_id
        FROM employees
        WHERE commission_pct IS NOT NULL
        GROUP BY job_id
​
        #②根据①的结果继续筛选,看最高工资>12000
        ①...
        HAVING MAX(salary)>12000
        
    #案例3:查询领导编号>102的每个领导手下的最低工资>5000的领导编号是哪个,以及其最低工资
        #①查询领导编号>102的每个领导手下的最低工资
        SELECT MIN(salary),manager_id
        FROM employees
        WHERE manager_id >102
        GROUP BY manager_id
        HAVING MIN(salary)>5000

1.7 连接查询(sql199)

  • 含义:

        又称多表查询,当查询的字段来自多个表时,就会用到连接查询

        笛卡尔乘积现象:表1 有m行,表2有n行,结果=m*n行

        发生原因:没有有效的连接条件

        如何避免:添加有效的连接条件

  • 语法:

      select 查询列表

      from 表1 别名

    【连接类型】join 表2 别名

      on 连接条件

    【where 筛选条件】

    【group by 分组】

    【having 筛选条件】

    【order by 排序列表】

  • 分类

            一、内连接:

                 等值连接、非等值连接、自连接,查询有交集的部分

                1、语法:

                    select 查询列表from 表1 别名inner join 表2 别名on 连接条件

                2、等值连接

                        ①添加排序、分组、筛选

                        ②inner可以省略

                        ③筛选条件放在where后面,连接条件放在on后面,提高分离性,便于阅读

            二、外连接

                左外连接、右外连接、全外连接

                1、应用场景

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

                2、特点

                        ① 外连接的查询结果为主表中的所有记录,如果从表中有和它匹配的,则显示匹配的值,如果从表中没有和它匹配的,则显示null

                        🐇外连接查询结果=内连接结果+主表中有而从表没有的记录

                        ② 左外连接,left左边的是主表右外连接,rght join 右边的是主表

                        ③左外和右外交换两个表的顺序可以实现同样的效果

                        左外:left【outer】

                        右外:right【outer】

                        全外:full 【outer】

  • girls库介绍:

                该库中存在三张表,分别是admin表、beauty表和boys表

  1. admin :员工表

列名类型注释
id【主键】int(11)编号
usernamevarchar(10)用户名
passwordvarchar(10)密码

  2. beauty :女生表

列名类型注释
id【主键】int(11)女生编号
namevarchar(50)姓名
sexchar(1)性别
borndatedatetime出生日期
phonevarchar(11)电话号码
photoblob照片
boyfriend_idint(11)男朋友编号

  3. boys :男生表

列名类型注释
id【主键】int(11)男生编号
boynamevarchar(50)姓名
userCPint(11)配对CP号
  • sql语句:

🏷内连接

#一、等值连接
    #1.调换位置
        #查询员工名、部门名
        SELECT last_name,department_name
        FROM employees e
        INNER JOIN departments d
        ON e.`department_id`=d.`department_id`
    
    #2.添加筛选
        #查询名字中包含e的员工名和工种名
        SELECT last_name,job_title
        FROM employees e
        INNER JOIN jobs j
        ON e.`job_id`=j.`job_id`
        WHERE e.`last_name` LIKE '%e%'
        
    #3.添加分组+筛选
        #查询部门个数>3的城市名和部门个数
        SELECT city,COUNT(*) 部门个数
        FROM locations l
        INNER JOIN departments d
        ON l.`location_id`=d.`location_id`
        GROUP BY city
        HAVING COUNT(*)>3
    
    #4.添加分组+筛选+排序
        #查询哪个部门的部门员工个数>3的部门名和员工个数,并按个数降序(排序)
        #思路:首先需要查询每个部门的员工个数,然后在其结果上筛选员工个数>3的记录,并排序
        SELECT department_name,COUNT(*) 员工个数
        FROM departments d
        INNER JOIN employees e
        ON d.`department_id`=e.`department_id`
        GROUP BY department_name
        HAVING 员工个数>3
        ORDER BY 员工个数 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 e.`job_id`=j.`job_id`
        ORDER BY department_name DESC;
        
#二、非等值连接
    #查询员工的工资级别
    SELECT salary,grade_level
    FROM employees e
    JOIN job_grades g
    ON e.salary BETWEEN g.lowest_sal AND g.highest_sal
    
    #查询工资级别的个数>2的个数,并且按工资级别降序
    SELECT COUNT(*) 工资级别个数,grade_level
    FROM job_grades g
    JOIN employees e
    ON e.salary BETWEEN g.lowest_sal AND g.highest_sal
    GROUP BY grade_level
    HAVING 工资级别个数>20
    ORDER BY grade_level ASC;
    
#三、自连接
    #查询员工的名字、上级的名字
    SELECT e1.last_name,e2.last_name
    FROM employees e1
    JOIN employees e2
    ON e1.manager_id=e2.employee_id

🏷外连接

        我们以boys表为主表, beauty表为从表,然后主表每一行与从表的所有行进行匹配,匹配的列是男生id,如果有则匹配成功,如果没有则显示null.

#引入:查询没有男朋友(不在男神表中)的女神名
    SELECT b.`name`,bo.*
    FROM beauty b
    LEFT JOIN boys bo ON b.boyfriend_id=bo.id
    WHERE bo.id IS NULL #选从表中的主键列

        查看一下以女生表为主表,男生表为从表的连接记录,然后再选择以男生表的主键为null的行,这是因为主键不为空。

#案例1:查询哪个部门没有员工
    SELECT d.department_name,e.*
    FROM departments d
    LEFT JOIN employees e
    ON d.department_id=e.department_id
    WHERE e.employee_id IS NULL

        看没有加上where语句的部分,则把部门表和员工表连接起来,以部门表为基准,则获取如下表格,然后再用where判断:

         全外连接=内连接的结果+表1中有单表2没有的+表2中有但表1没有的

  • 各种连接图示

    1、左连接、内连接、右连接

        2、左连接挖空重合、右连接挖空重合、外连接、外连接挖空重合

#全外连接
    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;
    
#案例1:查询编号>3的女神的男朋友信息,如果有则列出详细,如果没有则用Null填充
    SELECT b.id,b.name,bo.* #②
    FROM beauty b  #①
    LEFT JOIN boys bo 
    ON b.`boyfriend_id`=bo.id
    WHERE b.id>3
    #由于先查表,所以起了别名之后用select查询的时候还是得用别名,不然系统识别不了表
   
#案例2:查询哪个城市没有部门
    SELECT city,d.*
    FROM locations l
    LEFT JOIN departments d
    ON l.location_id=d.location_id
    WHERE department_id IS NULL;
    
#案例3:查询部门名为SAL或IT的员工信息
    #可能会出现部门名中没有员工信息的情况,所以要用外连接
    SELECT e.*,d.`department_name`
    FROM employees e
    LEFT JOIN departments d
    ON e.department_id=d.department_id
    WHERE department_name IN ('SAL','IT');

1.8 子查询

  • 含义:

      出现在其他语句中的select语句,称为子查询或内查询;

      外部的查询语句,称为主查询或外查询分类

  • 分类:

          一、按子查询出现的位置:

                1、select后面: 仅仅支持标量(单行)子查询

                2、from后面:支持表子查询

                3、where或having后面: 🐰

                        ① 标量子查询(单行) 🤒

                        ② 列子查询(多行)🤒

                        ③ 行子查询

                        🍰 特点:

                                1)子查询放在小括号内

                                2)子查询一般放在条件的右侧

                                3)标量(单行)子查询,一般搭配单行操作符使用【单行操作符:> < >= <= <>】

                                4) 列(多行)子查询,一般搭配多行操作符使用【in, any/some,all】

                                5)子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果

                4、exists后面(相关子查询):表子查询

        二、按结果集的行列数不同划分:

                1、标量子查询(结果集只有一行一列)

                2、列子查询(结果集只有一列多行)

                3、行子查询(结果集有一行多列)

                4、表子查询(结果集一般为多行多列)

  • 单/多行子查询介绍:

        一、单行子查询

                1、返回单行

                2、一般搭配单行操作符使用

        二、多行子查询

                1、返回多行

                2、使用多行操作符

操作符含义
in/not in等于列表中的任意一个
any/some和子查询返回的某一个值比较
all和子查询返回的所有值比较

        ps:<any:等价于小于最大的那一个:<max

        >any:等价于大于最小的那一个 :>min

        in:等价于=any

        not in:等价于!=all

  • sql详解:

    一、where或having后面(重点)

        🏷标量(单行)子查询

#1、使用单行操作符
    #案例1:谁的工资比Abel高?
        #①查询Abel的工资
        SELECT salary 
        FROM employees 
        WHERE last_name='Abel'
        
        #②查询员工的信息,满足 salary>①的结果
        SELECT *
        FROM employees 
        WHERE salary> (
            SELECT salary 
            FROM employees 
            WHERE last_name='Abel'
        );
    
#2、使用多个单行操作符
    #案例2:返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资
        #①查询141号员工的job_id
        SELECT job_id j
        FROM employees
        WHERE employee_id=141;
​
        #②查询143号员工的工资
        SELECT salary
        FROM employees 
        WHERE employee_id=143;
​
        #③查询员工的姓名,job_id和salary,要求job_id=①并且salary>②
        SELECT last_name,job_id,salary
        FROM employees
        WHERE job_id =(
            SELECT job_id j
            FROM employees
            WHERE employee_id=141
        )
        AND salary >(
            SELECT salary
            FROM employees 
            WHERE employee_id=143
        );
        
#3、在子查询中使用分组函数
    #案例3:返回公司工资最少的员工的last_name,job_id和salary
        #①查询公司的最低工资
        SELECT MIN(salary)
        FROM employees
​
        #②查询last_name,job_id和salary,要求salary=①
        SELECT last_name,job_id,salary
        FROM employees
        WHERE salary =(
            SELECT MIN(salary)
            FROM employees
        );
        
#4、子查询中的having子句
    #案例4:查询最低工资大于50号部门最低工资的部门id和其最低工资
        #①查询50号部门最低工资
        SELECT MIN(salary)
        FROM employees
        WHERE department_id=50
​
        #②查询每个部门的最低工资:因为我们需要把每个部门最低工资拿出来才可以和50号部门最低工资进行对比
        SELECT MIN(salary)
        FROM employees
        GROUP BY department_id
​
        #③在②的基础上筛选,满足min(salary)>①的结果
        SELECT department_id,MIN(salary)
        FROM employees
        GROUP BY department_id #直接能筛选出两列的值
        HAVING MIN(salary) > (
            SELECT MIN(salary)
            FROM employees
            WHERE department_id=50
        )

🏷列(多行)子查询

#1、使用in操作符
    #案例1:返回location_id是1400或1700的部门中的所有员工姓名
        #①查询location_id是1400或1700对应的部门编号
        SELECT DISTINCT department_id
        #采用distinct去重,可以让列表值少一些,因为可能会存在这俩id位置号对应相同部门编号的可能
        FROM departments
        WHERE location_id IN('1400','1700')
​
        #②查询员工姓名,要求部门号是①列表中的某一个
        SELECT last_name
        FROM employees
        WHERE department_id IN(
            SELECT department_id
            FROM departments
            WHERE location_id IN('1400','1700')
        )
        
        #另一种写法:
        SELECT last_name
        FROM employees
        WHERE department_id = ANY(
            SELECT department_id
        FROM departments
        WHERE location_id IN('1400','1700')
        )
        
#2、使用any操作符
    #案例2:返回其他工种中比job_id为'IT_PROG'工种任意工资低的员工的员工号、姓名、job_id以及salary
        #①job_id为'IT_PROG'的工种编号和薪资
        SELECT DISTINCT salary
        FROM employees
        WHERE job_id='IT_PROG'
​
        #②查找员工信息,要求部门员工工资<任意①且工种编号!=①
        SELECT employee_id,last_name,salary
        FROM employees
        WHERE salary < ANY(
            SELECT salary
            FROM employees
            WHERE job_id='IT_PROG'
        ) AND job_id !='IT_PROG'
        
        #另一种写法:
        SELECT employee_id,last_name,salary
        FROM employees
        WHERE salary < (
            SELECT MAX(salary)
            FROM employees
            WHERE job_id='IT_PROG'
        ) AND job_id !='IT_PROG'
        
#3、使用all操作符
    #案例3:返回其他工种中比job_id为'IT_PROG'工种所有工资低的员工的员工号、姓名、job_id以及salary
        SELECT employee_id,last_name,salary
        FROM employees
        WHERE salary < ALL(
            SELECT salary
            FROM employees
            WHERE job_id='IT_PROG'
        ) AND job_id !='IT_PROG'

🏷行子查询

#3、行子查询
    #案例1:查询员工编号最小并且工资最高的员工信息
    SELECT * 
    FROM employees
    WHERE (employee_id,salary)=(
        SELECT MIN(employee_id),MAX(salary)
        FROM employees
    )
    #另一种写法:
    #①查询最小的员工编号
    SELECT MIN(employee_id)
    FROM employees
​
    #②查询最高工资
    SELECT 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:查询每个部门的员工个数
    #意思是把部门信息和员工个数都查询出来,如果没有员工信息则个数为0,对于部门表d中的每个部门,我们都会去查询与其部门号相同的员工表中有多少人并统计出来。
    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 employee_id=102
    ) 部门名

三、from后面【用的较少】

        查询的结果集充当了一张表,要求这张表起别名,然后像普通的表一样使用就行

#案例:查询每个部门的平均工资的工资等级
    #①查询每个部门的平均工资
    SELECT AVG(salary),department_id
    FROM employees
    GROUP BY department_id
​
    SELECT * FROM job_grades
​
    #② 连接①的结果集和job_grades表,筛选条件是平均工资AVG(salary) 在工资等级的最小薪水和最高薪水之间
    SELECT
    FROM (
        SELECT AVG(salary) ag,department_id
        FROM employees
        GROUP BY department_id
    ) ag_dep
    INNER JOIN job_grades g
    ON ag BETWEEN glowest_sal AND highest_sal

        这个案例主要是需要将部门名、部门平均工资以及工资等级筛选出来,但是平均工资是新的表格,因此需要在from后面使用select语句来获取出这个表格(可以取别名为ag_dep),然后再与等级表进行内连接。

四、exist后面(相关子查询)【用的较少】

  • 语法:

                exists(完整的查询语句)

  • 结果:

                1或者0

#案例1:查询有员工的部门名
    SELECT department_name
    FROM departments d
    WHERE EXISTS(
        SELECT *
        FROM employees e
        WHERE e.`department_id`=d.department_id
    )
    #也就是看exists后面有没有结果,也就是先查询出来部门名,然后再去看是否有有员工的部门名再筛选出来,并不是之前的子查询比主查询优先
    
    #采用in
    SELECT department_name,department_id
    FROM departments 
    WHERE department_id IN(
        SELECT department_id
        FROM employees
    )
    #这种写法就是看部门表中的部门id号是否与员工表中的部门号相匹配,如果匹配则证明有对应的员工,则可以筛选出来
    
#案例2:查询没有女朋友的男神信息
    #查询是否存在有女神表的男生id与男神表id相同,如果不同
    SELECT bo.*
    FROM boys bo
    WHERE NOT EXISTS(
        SELECT *
        FROM beauty b
        WHERE b.`boyfriend_id`=bo.id
    )
    

五、课后练习

#案例1、查询和Zlotkey相同部门的员工姓名和工资
    #①查询Zlotkey所在的部门号
    SELECT department_id
    FROM employees
    WHERE last_name='Zlotkey'
​
    #②查询员工姓名和工资,满足部门号=①
    SELECT last_name,salary
    FROM employees
    WHERE department_id = (
        SELECT department_id
        FROM employees
        WHERE last_name='Zlotkey'
    )
    
#案例2.查询工资比公司平均工资高的员工的员工号、姓名和工资
    #①查询公司的平均工资
    SELECT AVG(salary)
    FROM employees
​
    #②,查询工资>①的员工号、姓名和工资
    SELECT employee_id,last_name,salary
    FROM employees
    WHERE salary >(
        SELECT AVG(salary)
        FROM employees
    )
#案例3.查询各部门中工资比本部门平均工资高的员工的员工号,姓名和工资
    #①查询各个部门的平均工资
    SELECT AVG(salary),department_id
    FROM employees
    GROUP BY department_id
​
    #②连接①结果集和employees表,并根据工资>①平均工资进行筛选
    SELECT employee_id,last_name,salary,e.department_id,ag
    FROM (
        SELECT AVG(salary) ag,department_id
        FROM employees
        GROUP BY department_id
    )de_avg
    INNER JOIN employees e
    ON de_avg.department_id=e.department_id
    WHERE e.salary > de_avg.ag

      ①我们首先把每个部门的平均工资取出来形成一个新的部门平均工资表(de_avg), ②员工的工资高于本部门:即需要把部门平均工资表与员工表内联,条件是部门名相同,这样就可以将部门的平均工资和所有该部门的员工信息对应上,可以看到相同部门号下的员工:

        ③获得该表之后然后再去筛选工资高于本部门的,则可获得结果。

#案例4:查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名
     #① 查询姓名中包含字母u 的员工的部门号
     SELECT DISTINCT department_id
     FROM employees
     WHERE last_name LIKE '%u%'
​
     #② 查询员工号和姓名,部门号in ①
     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的部门工作的员工的员工号
     #①查询location_id为1700的部门号
     SELECT DISTINCT department_id
     FROM departments
     WHERE location_id='1700'
​
     #②查询员工号,满足部门号为①
     SELECT employee_id
     FROM employees
     WHERE department_id IN(
         SELECT DISTINCT department_id
         FROM departments
         WHERE location_id='1700'
     )
     
#案例6:查询管理者是king的员工姓名和工资
     #查询king的员工号
     select employee_id
     from employees
     where last_name='K_ing'
​
     #查询员工姓名和工资,满足manager_id=①
     select last_name,salary
     from employees
     where manager_id=any(
          SELECT employee_id
         FROM employees
         WHERE last_name='K_ing'
     )
     #不知道单行还是多行的时候就用in
     
#案例7:查询工资最高的员工的姓名,要求first_name和last_name显示为一列,列名为姓.名
    #①查询最高工资
    select max(salary)
    from employees
​
    #②查询工资=①的姓.名
    select concat(last_name,'.',first_name) "姓.名"
    from employees
    where salary=(
         SELECT MAX(salary)
         FROM employees
)

🏷子查询经典案例

#案例1:查询工资最低的员工信息:last_name,salary
    #①查询最低的工资
    SELECT MIN(salary)
    FROM employees
​
    #②查询员工信息,编号=①
    SELECT last_name,salary
    FROM employees
    WHERE salary=(
        SELECT MIN(salary)
        FROM employees
    )
​
#案例2:查询平均工资最低的部门信息
    #①查询各个部门的平均工资
    SELECT AVG(salary),department_id
    FROM employees
    GROUP BY department_id
​
    #②求出最低平均工资的部门编号
    SELECT department_id
    FROM employees
    GROUP BY department_id
    ORDER BY AVG(salary) ASC
    LIMIT 1
    
    #③查询部门信息
    SELECT *
    FROM departments
    WHERE department_id=(
    SELECT department_id
    FROM employees
    GROUP BY department_id
    ORDER BY AVG(salary) ASC
    LIMIT 1
)
​
#3.查询平均工资最低的部门信息和该部门的平均工资
    #①查询各个部门的平均工资
    SELECT AVG(salary),department_id
    FROM employees
    GROUP BY department_id
​
    #②求出最低平均工资的部门编号
    SELECT avg(salary),department_id
    FROM employees
    GROUP BY department_id
    ORDER BY AVG(salary) ASC
    LIMIT 1
​
    #③查询部门信息
    select ag,d.*
    from (
        SELECT AVG(salary)ag,department_id
        FROM employees
        GROUP BY department_id
        ORDER BY AVG(salary) ASC
        LIMIT 1
    )avg_de
    inner join departments d
    on avg_de.department_id=d.department_id
    
#案例4:查询平均工资最高的job信息
    #①查询平均工资最高的job_id
    SELECT job_id
    FROM employees
    GROUP BY job_id
    ORDER BY MAX(salary) DESC 
    LIMIT 1
​
    #查询job信息
    SELECT *
    FROM jobs
    WHERE job_id=(
        SELECT job_id
        FROM employees
        GROUP BY job_id
        ORDER BY MAX(salary) DESC 
        LIMIT 1
    )
    
#案例5:查询平均工资高于公司平均工资的部门有哪些
    #①查询公司的平均工资
    SELECT AVG(salary)
    FROM employees
​
    #查询部门,满足部门平均工资>①
    SELECT department_id
    FROM employees
    GROUP BY department_id
    HAVING AVG(salary)>(
        SELECT AVG(salary)
        FROM employees
    )
    
#案例6:查询出公司中所有manager的详细信息
    #①查询所有magager_id
    SELECT DISTINCT manager_id
    FROM employees
​
    #查询信息,满足员工号in①
    SELECT *
    FROM employees
    WHERE employee_id IN(
        SELECT DISTINCT manager_id
        FROM employees
    )
    
#案例7:各个部门中,最高工资中最低的那个部门的最低工资是多少
    #①查询最高工资中的最低工资的部门号
    SELECT department_id
    FROM employees
    GROUP BY department_id
    ORDER BY MAX(salary) ASC
    LIMIT 1
​
    #②查询部门号=①的部门最低工资
    SELECT MIN(salary)
    FROM employees
    WHERE department_id=(
        SELECT department_id
        FROM employees
        GROUP BY department_id
        ORDER BY MAX(salary) ASC
        LIMIT 1
    )
    
#案例8:查询平均工资最高的部门的manager的详细信息:last_name,department_id,email,salary
    #①查询平均工资最高的部门编号
    SELECT department_id
    FROM employees
    GROUP BY department_id
    ORDER BY AVG(salary) DESC
    LIMIT 1
​
    #将employees和departments表连接查询,筛选条件是①
    SELECT last_name,e.department_id,email,salary
    FROM employees e
    INNER JOIN departments d
    ON e.employee_id=d.manager_id
    WHERE e.department_id=(
        SELECT department_id
        FROM employees
        GROUP BY department_id
        ORDER BY AVG(salary) DESC
        LIMIT 1
    )
   #这个案例中需要知道,每个部门有对应的manager_id,而这个与员工表中的employee_id是相对应的,因此我们需要先查询到平均工资最高的部门编号,然后用此编号与员工表中连接,通过manager_id对应到员工的基本信息

1.9 分页查询

  • 应用场景:

                当要显示的数据一页显示不全时,需要分页提交sql请求

  • 语法:

    select 查询列表               ⚡️⑦ ​

    from 表                      ⚡️① ​

    【join type join 表2    ⚡️② ​

    on 连接条件               ⚡️③ ​

    where筛选条件          ⚡️④ ​

    group by 分组字段     ⚡️⑤ ​

    having 分组后的筛选  ⚡️⑥ ​

    order by 排序的字段】     ⚡️⑧ ​

    limit offset,size;                ⚡️⑨

    其中offset要显示条目的起始索引,size要显示的条目个数

  • 注意:

                1、limit语句放在查询语句的最后,在执行顺序上也是最后

                2、公式: ​ 要显示的某页的页数 page,每页的条目数size ​ select 查询列表 ​ from 表 ​ limit size*(page-1),size

  • sql实现

#案例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.10 综合查询

        给出三张表,分别是学生表、成绩表、专业表:

        1.🐇 student表

列名类型备注
studentnovarchar(10)学生编号
studentnamevarchar(20)学生姓名
loginpwdvarchar(8)登录密码
sexchar(1)性别
majoridint(11)专业编号
phonevarchar(11)电话
emailvarchar(20)邮箱
borndatedatetime出生日期

        2. 🐇 成绩表

列名类型备注
idint(11)成绩编号
studentnovarchar(10)学生编号
scoredouble登录密码

        3. 🐇 专业表

列名类型备注
majoridint(11)专业编号
majornamevarchar(20)专业名称

        🐰sql实现

#一、查询每个专业的学生人数
    SELECT COUNT(*),majorid
    FROM student
    GROUP BY majorid
​
#二、查询参加考试的学生中,每个学生的平均分、最高分
    SELECT AVG(score),MAX(score)
    FROM result
    GROUP BY studentno;
​
#三、查询姓张的每个学生的最低分大于60的学号、姓名
    SELECT s.studentno,studentname
    FROM student s
    INNER JOIN result r
    ON s.`studentno`=r.`studentno`
    WHERE studentname LIKE '张%'
    GROUP BY s.studentno
    HAVING MIN(score)>60
​
#四、查询专业生日在“1988-1-1”后的学生姓名、专业名称
    SELECT studentname,majorname,borndate
    FROM student s
    INNER JOIN major m
    ON s.`majorid`=m.majorid
    WHERE DATEDIFF(borndate,'1988-1-1')>0
​
#五、查询每个专业的男生人数和女生人数分别是多少
    #majorid 女 男
    #先按照专业分组,然后在每组里面利用子查询查询不同性别的个数
    SELECT majorid,
    (SELECT COUNT(*) FROM student s WHERE s.`majorid`=m.majorid AND sex='女')女,
    (SELECT COUNT(*) FROM student s WHERE s.`majorid`=m.majorid AND sex='男')男
    FROM major m
    GROUP BY majorid
    
#六、查询专业和张翠山一样的学生的最低分
    #①查询张翠山的专业编号
    SELECT majorid
    FROM student
    WHERE studentname='张翠山'
​
    #②查询专业编号=①的学生编号
    SELECT studentno
    FROM student
    WHERE majorid=(
        SELECT majorid
        FROM student
        WHERE studentname='张翠山'
    )
    #查询这些学生中的最低分
    SELECT MIN(score)
    FROM result
    WHERE studentno IN (
        SELECT studentno
        FROM student
        WHERE majorid=(
            SELECT majorid
            FROM student
            WHERE studentname='张翠山'
        )
    
#七、查询大于60分的学生的姓名、密码、专业名
    #查询的是两个表的数据,筛选条件是第三个表的
    SELECT studentname,loginpwd,majorname
    FROM student s
    INNER JOIN major m ON s.`majorid`=m.majorid
    INNER JOIN result r ON s.`studentno`=r.`studentno`
    WHERE r.`score`>60
​
#八、按邮箱位数分组,查询每组的学生个数
    SELECT COUNT(*),LENGTH(email)
    FROM student
    GROUP BY LENGTH(email)
​
#九、查询学生名、专业名、分数
    #查询三个表的数据,学生必定对应一个专业,但是该专业可能没有分数,所以要使用外连接
    SELECT studentname,majorname,score
    FROM student s
    INNER JOIN major m ON s.`majorid`=m.majorid
    LEFT JOIN result r ON s.`studentno`=r.`studentno`
        
#十、查询哪个专业没有学生,分别用左连接和右连接实现
    select m.majorid
    from major m
    left join student s on m.majorid=s.`majorid`
    where s.`studentno` is null    
        
#十一、查询没有成绩的学生人数
    SELECT COUNT(*),s.`studentno`
    FROM student s
    LEFT JOIN result r ON s.`studentno`=r.`studentno`
    WHERE r.`score` IS NULL

1.11 联合查询

  • 应用场景

        1、union 联合 合并:将多条查询语句的结果合并成一个结果 ​

        2、要查询的结果来自于多个表,且多个表没有直接的连接关系,但查询的信息一致,即多个表查询的信息是一样的,例如名字、性别

  • 语法

            查询语句1 union 查询语句2 union...

  • 特点

            1、要求多条查询语句的查询列数是一致的

            2、要求多条查询语句的查询的每一列的类型和顺序最好一致

            3、union关键字默认去重,如果使用union all可以包含重复项

  • sql实现

#引入的案例:查询部门编号>90或邮箱包含a的员工
    SELECT * FROM employees WHERE email LIKE '%a%'
    UNION
    SELECT * FROM employees WHERE department_id>90
    
#案例:查询中国用户中男性的信息以及外国用户中男性的用户信息
    #id对应t_id,cname对应tName
    SELECT id,cname,csex FROM t_ca WHERE csex='男'
    UNION
    seelct t_id,tName FROM t_ua WHERE tGender='male'

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值