SQL / 数据查询语言DQL(Data Query Language )

Posted on 2021-07-26 10:33  金色的省略号  阅读(353)  评论(0编辑  收藏  举报

  数据查询语言DQL,基本结构是由SELECT子句,FROM子句,WHERE子句组成的查询块:

  SELECT <字段名表>
  FROM <表或视图名>
  WHERE <查询条件>

  数据查询有以下几种:

  1、基础查询

#进阶1:基础查询
/*
语法:
select 查询列表 from 表名;
1、查询列表可以是:表中的字段、常量值、表达式、函数
2、查询的结果是一个虚拟的表格 ( 不保存 )
*/

#打开数据库
USE myemployees;

#1、查询表中的单个字段
SELECT last_name FROM employees;

#2、查询表中的多个字段
SELECT last_name,salary,email FROM employees;

#3、查询表中的所有字段
 
#方式一( 栏位,双击字段可以快速添加,选中代码,按F12格式化或按F9执行 ):
SELECT 
  `first_name`, # 字段两边的着重号`是区分是关键字还是字段的,可以去掉,
  `last_name`,
  `email`,
  `phone_number`,
  `job_id`,
  `salary`,
  `commission_pct`,
  `manager_id`,
  `department_id`,
  `hiredate` 
FROM
  employees ;
  
# 方式二,* 代表所有字段,字段与原始表一样:
SELECT * FROM employees; 

#4、查询常量值
SELECT 100;
SELECT 'john'; #查询字符常量

#5、查询表达式
SELECT 100*98;

#6、查询函数
SELECT VERSION(); #查询函数VERSION()的返回值

#7、起别名
/*
便于理解(提高可读性)
如果要查询的字段有重名的使用别名可以区分开来
*/
#方式一:使用as
SELECT 100%98 AS 结果;
SELECT last_name AS 姓, first_name ASFROM employees;

#方式二:使用空格
SELECT last_name 姓, first_name 名 FROM employees;

#别名含有特殊符号的要加双引号或单引号
SELECT salary AS "out put" FROM employees;

#8、去重
#查询员工表中涉及的所有的部门编号 ( 去掉重复的编号用distinct )
SELECT DISTINCT department_id FROM employees;

#9+号的作用
/*
java中的+号:
运算符,两个操作数都为数值型
连接符,只要一个操作数为字符串

mysql中的+号:
运算符,仅仅只有一个功能
select 100+90;   #两个操作数都为数值型,则做加法运算
select '123'+90; #只要其中一个操作数为字符型,试图把字符型转为数值型,
            #转换成功则继续做加法运算
select 'john'+90;   #转换失败则字符型数值为0,再做加法运算
select null+90;  #只要其中一个操作数为null,结果为null
*/

#查询员工名和姓连接成一个字段并显示为姓名,使用字符连接函数concat(),
#只要其中一个操作数为null,结果为null
SELECT 
  CONCAT(last_name, ' ', first_name) AS 姓名 
FROM
  employees ;

#使用ifnull()函数,避免结果为null

SELECT 
  IFNULL(`commission_pct`, 0) AS 奖金率,
  `commission_pct` 
FROM
  employees ;

SELECT 
  CONCAT(
    `first_name`,
    ',',
    `last_name`,
    ',',
    `email`,
    ',',
    IFNULL(`commission_pct`, 0)
  ) AS out_put 
FROM
  employees ;
View Code

  2、条件查询

#进阶2:条件查询
/*
语法:( 执行顺序 from/where/select )
    select
        查询列表
    from
        表名
    where
        筛选条件(结果是true或false);
分类:
    一、按条件表达式筛选
        条件运算符:
            > < = != <> >= <= <=>
        其中 <> 与 != 相同,<=> 安全等于
    二、按逻辑表达式筛选 ( 作用:是连接条件表达式的 )
        逻辑运算符:
            && || !
            and or not
    三、模糊查询
        like
        between and
        in
        is null|is not null
*/

USE myemployees;
#一、按条件表达式筛选

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

#查询部门编号不等于90号的员工名和部门编号
SELECT 
  first_name,
  department_id 
FROM
  employees 
WHERE department_id <> 90 ;

#二、按逻辑表达式筛选

#查询工资在10000到20000之间的员工名、工资及奖金
SELECT 
  first_name,
  salary,
  commission_pct 
FROM
  employees 
WHERE salary >= 10000 
  AND salary <= 20000 ;

#查询部门编号不是在90到110之间 或 工资高于15000的员工信息
/*
select 
  * 
from employees
where department_id <90 
    or department_id > 110
  or salary > 15000 ;
*/
SELECT 
  * 
FROM
  employees 
WHERE NOT (
    department_id >= 90 
    AND department_id <= 110
  ) 
  OR salary > 15000 ;

#模糊查询
/*
like
一般和通配符搭配使用
    通配符:
    %任意多个字符包含0个字符
    _任意单个字符
between and
in
is null|is not null
*/

#查询员工中包含字符a的员工信息
SELECT 
  * 
FROM
  employees 
WHERE first_name LIKE '%a%' ;

#查询员工中第三个字符为e,第五个字符为a的员工和工资
SELECT 
  first_name,
  salary 
FROM
  employees 
WHERE first_name LIKE '__e_a%' ;

#查询员工姓名中第二个字符为_的员工姓名
/*select 
  last_name 
from
  employees 
where last_name like '_\_%' ; #使用转义字符\
*/

SELECT 
  last_name 
FROM
  employees 
WHERE last_name LIKE '_$_%' ESCAPE '$' ; # ESCAPE 定义转义字符
#查询员工编号在100到120之间的员工信息
SELECT 
  * 
FROM
  employees 
WHERE employee_id >= 100 
  AND employee_id <= 120 ;
  
#查询员工部门号第一个数字为1的员工信息,like 可以查询数值型的字段
SELECT 
  * 
FROM
  employees 
WHERE department_id LIKE '1__' ;
 
#between and 提高语句简洁度,包含临界值如下的100 120,两个临界值不调换顺序
SELECT 
  * 
FROM
  employees 
WHERE employee_id BETWEEN 100 
  AND 120 ;

#查询员工的工种编号是 IT_PROG、AD_VP、AD_PRES中的一个员工名和工种编号
SELECT 
  first_name,
  job_id 
FROM
  employees 
WHERE job_id = 'IT_PROT' 
  OR job_id = 'AD_VP' 
  OR job_id = 'AD_PRES' ;

#IN 判断某个字段的值是否属于in列表中的某一项
#比使用or提高语句简洁度,in类别的值类型必须一致或兼容,不支持通配符
SELECT 
  first_name,
  job_id 
FROM
  employees 
WHERE job_id IN ('IT_PROT', 'AD_VP', 'AD_PRES') ;

#查询没有奖金的员工名和奖金率
/*
= 或 <> 不能判断 null
is null或is not null 可以判断null值
*/
SELECT 
  first_name,
  commission_pct 
FROM
  employees 
WHERE commission_pct IS NULL ; # 没有奖金 WHERE commission_pct IS NOT NULL ;

#安全等于 <=> ,可读性不好
SELECT 
  first_name,
  commission_pct 
FROM
  employees 
WHERE commission_pct <=> NULL ; #is null

#奖金等于12000的员工名
SELECT 
  first_name,
  salary
FROM
  employees 
WHERE salary <=> 12000 ;

# is null 仅仅可以判断null值,可读性高,建议使用
# <=> 即可以判断null值,又可以判断普通的数值,可读性较低

#查询员工号为176的员工的姓名和部门和年薪
SELECT 
  first_name,
  department_id,
  salary * 12 *(1+ IFNULL(commission_pct, 0)) AS 年薪 
FROM
  employees 
WHERE employee_id = 176 ;

#查询没有奖金,且工资小于18000的salary,last_name
SELECT 
  last_name,
  salary 
FROM
  employees 
WHERE commission_pct IS NULL 
  AND salary < 18000 ;

#查询 employee 表中,job_id 不为 'IT' 或者 工资为12000的员工的信息
SELECT 
  * 
FROM
  employees 
WHERE job_id <> 'IT' 
  OR salary = 12000 ;

#查看部门 departments 表的结构
DESC departments;

#查看部门departments表中涉及到了哪些位置编号
 
SELECT DISTINCT  #distinct 去重
  location_id 
FROM
  departments ;

#------------------------
SELECT 
  * 
FROM
  employees ;

#如果判断的字段有null值下面语句与上面语句不一样
SELECT 
  * 
FROM
  employees 
WHERE commission_pct LIKE '%%' 
  AND last_name LIKE '%%' ;
View Code

  3、排序查询

#进阶3:排序查询
/*
语法:
    select 查询列表
    from 表
    【where 筛选条件】
    order by 排序列表 【asc|desc】
特点:
    1、asc是升序,desc是降序
        如果不写,默认是升序
    2、order by 子句中可以支持单个字段、多个字段、表达式、函数、别名
    3、order by 子句一般放在查询语句的最后面,limit子句除外
*/
#查询员工信息,要求工资从高到低排序
SELECT 
  * 
FROM
  employees 
ORDER BY salary DESC ;

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

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

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

#按姓名的长度显示员工的姓名和工资【按函数排序】
SELECT 
  LENGTH(last_name) AS 字节长度,
  last_name,
  salary 
FROM
  employees 
ORDER BY LENGTH(last_name) DESC ;

#查询员工信息,要求先按工资升序排序,再按员工编号降序排序【按多个字段排序】
SELECT 
  * 
FROM
  employees 
ORDER BY salary ASC,
  employee_id DESC ;

#查询员工的姓名和部门号和年薪,按年薪降序 按姓名升序
SELECT 
  last_name,
  department_id,
  salary * 12 * (1 + IFNULL(commission_pct, 0)) AS 年薪 
FROM
  employees 
ORDER BY 年薪 DESC,
  last_name ASC ;

#选择工资不在8000到17000的员工的姓名和工资,按工资降序
SELECT 
  last_name,
  salary 
FROM
  employees 
WHERE salary NOT BETWEEN 8000 
  AND 17000 
ORDER BY salary ASC ;

#查询邮箱中包含e的员工信息,并先按邮箱的字节数降序,再按部门号升序
SELECT 
  * 
FROM
  employees 
WHERE email LIKE '%e%' 
ORDER BY LENGTH(email) DESC,
  department_id ASC ;
View Code

  4、函数查询

#进阶4:常见函数
/*

概念:类似于Java的方法,将一组逻辑语句封装在方法体中,对外暴露方法名
好处:1、隐藏了实现细节 2、提高代码的重用性
调用:select 函数名(实参列表) 【from 表】;
特点:叫什么(函数)、干什么(函数功能)
分类:  1、单行函数,如concat、length、ifnull等
    字符函数、数学函数、日期函数、其他函数、流程控制函数  
    2、分组函数
    功能:做统计使用,又称为统计函数、聚合函数、组函数
常见函数:
    一、单行函数
    字符函数:
    length concat substr instr trim upper lower lpad rpad replace
    数学函数:
    round ceil floor truncate mod
    日期函数:
    now curdate curtime year month monthname day hour minute sencond str_to_date date_format
    其他函数:
    version database user
    控制函数:
    if case
    二、分组函数
    
    
*/
#单行函数
#一、字符函数
#length 获取参数值的字节个数
SELECT LENGTH('john');
SELECT LENGTH('张三丰hahaha'); #一个汉字3个字节,结果为15

SHOW VARIABLES LIKE '%char%'; #utf8

#concat 拼接字符串
SELECT CONCAT(last_name,'_',first_name) 姓名 FROM employees;

#upperlower
SELECT UPPER('john');
SELECT LOWER('JOHN');

#将姓变大写,名变小写,然后拼接
SELECT CONCAT(LOWER(first_name),'_',UPPER(last_name)) 姓名 FROM employees;

#substr substring 截取字符 索引从1开始
SELECT SUBSTR('李莫愁爱上了陆展元',7) out_put;
SELECT SUBSTR('李莫愁爱上了陆展元',1,3) out_put; #1是开始索引,3是字符长度
SELECT SUBSTR('limochouaishangleluzhanyuan',7);

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

#instr 返回子串第一次出现的索引,如果找不到,返回0
SELECT INSTR('杨不悔','不悔'); #返回值,是首次适配的索引值

#trim 去除首尾字符
SELECT LENGTH(TRIM('     张翠山    ')) AS out_put;
SELECT TRIM('a' FROM 'aaaaa张aa翠山aaaa') AS out_put;

#lpad 左填充 不够长度从左边填充字符 长度低于字符个数会截断字符串
SELECT LPAD('殷素素',10,'*') AS out_put;

#rpad 右填充
SELECT RPAD('殷素素',10,'*') AS out_put;

#replace 替换
SELECT REPLACE('Hello World Hello','Hello','tt') AS out_put;

#二、数学函数

#round 四舍五入
SELECT ROUND(1.657);
SELECT ROUND(1.657,2); #保留小数点后2位

#ceil 向上取整,返回 >= 该参数的最小整数
SELECT CEIL(1.003);

#floor 向下取整,返回 <= 该参数的最小整数
SELECT FLOOR(9.99);  # 9
SELECT FLOOR(-9.99); # -10

#truncate 截断
SELECT TRUNCATE(1.6999,1); # 1.6

#mod 取余 a-a/b*b
SELECT MOD(10,3); # 1
SELECT -10%3;

#三、日期函数

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

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

#curtime 返回当前系统时间,不包含日期
SELECT CURTIME();

#------------------------
SELECT CURDATE(),CURTIME();

#可以获取指定的部分,年,月,日,小时,分钟,秒
SELECT YEAR(NOW());
SELECT YEAR('1998-1-1');
SELECT YEAR('1998/1/1');
SELECT YEAR('1998.1.1');
SELECT YEAR(hiredate) ASFROM employees;

SELECT MONTH(NOW());
SELECT MONTH('1998.1.1');
SELECT MONTHNAME(NOW());  #显示英文月名 monthname

SELECT DAY(NOW()); #day

SELECT MINUTE(NOW()); # minute

SELECT HOUR(NOW());

SELECT SECOND(NOW());

#str_to_date 将日期格式的字符转换成指定格式的日期,解析失败返回 null
#%Y四位年份 %y二位年份 %m补0的月份 %c月份 %d日 %H 24小时制 %h 12小时制 %i分钟 %s秒
SELECT STR_TO_DATE('7.22.2021','%m.%d.%Y'); #前后格式一致、正确

#date_format: 将日期转换成字符 转换失败返回 null
SELECT DATE_FORMAT('2021.7.22','%Y年%m月%d日');

#查询入职日期为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');

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

#四、其他函数

SELECT VERSION();
SELECT    DATABASE(); #查看当前库
SELECT USER();

#五、流程控制函数

#if函数: if else 的效果

SELECT IF(10 > 5,'','');

SELECT 
  last_name,
  commission_pct,
  IF(
    commission_pct IS NULL,
    '没奖金,哈哈',
    '有奖金,嘻嘻'
  ) AS 备注 
FROM
  employees ;

#case函数 方式一:switch case的效果
/*
Java中:
switch(变量或表达式){
    case 常量1:语句1;break;
    ...
    default: 语句n; break;
}

mysql中
case 要判断的字段或表达式    #case相当于Java的switch
when 常量1 then 要显示的值1或语句1   #when相当于Java的case
...
else 要显示的值n或语句n      #相当于Java的default
end               #结束标志

*/

/*查询员工的工资,要求
部门号=30,显示的工资为1.1倍
部门号=40,显示的工资为1.2倍
部门号=50,显示的工资为1.3倍
其他部门,显示的工资为原工资

*/

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

#case函数 方式二:类似于 多重if
/*
Java中:
if(条件1){
    语句1:
}else if(条件2){
    语句2:
}
...
else{
    语句n:
    
}

sql中:
case
when 条件1 then 要显示的值1或语句1
...
else 要显示的值n或语句n
end
*/

/*查询员工的工资情况
如果工资 > 20000,显示A级别
如果工资 >15000,显示B级别
如果工资 >10000,显示C级别
否则,显示D级别
*/

SELECT salary,  # 显示salay,后面还有显示加逗号
CASE
WHEN salary > 20000 THEN 'A'  # 值1 'A'
WHEN salary > 15000 THEN 'B'
WHEN salary > 10000 THEN 'C'
ELSE 'D'
END AS 工资级别  # 显示 工资级别
FROM employees;

#显示系统时间(注:日期+时间)
SELECT NOW();

#查询员工号,姓名,工资,以及工资提高百分之20后的结果(new salary)
SELECT employee_id, last_name, salary,salary * 1.20 AS 'new salary'
FROM employees;

#将员工的姓名按首字母排序,并写出姓名的长度(length)
SELECT LENGTH(last_name) AS 'LENGTH', SUBSTR(last_name,1,1) AS 首字符, last_name FROM employees ORDER BY 首字符 ASC;

#做一个查询,产生下面结果 Dream Salary King earns 24000 monthly but wants 72000   #用到拼接 concat
SELECT 
  CONCAT(
    last_name,
    ' earns ',
    salary,
    ' monthly but wants ',
    salary * 3
  ) AS 'Dream Salary' 
FROM
  employees 
WHERE salary = 24000 ;

#使用case-when   方式一:
SELECT last_name, job_id AS job,
CASE job_id 
WHEN 'AD_PRES' THEN 'A'
WHEN 'ST_MAN' THEN 'B'
WHEN 'IT_PROG' THEN 'C'
WHEN 'SA_PRE' THEN 'D'
WHEN 'ST_CLEAK' THEN 'E'
END AS grade
FROM employees
WHERE job_id = 'AD_PRES';

#分组函数
/*
功能:用作统计使用,又称为聚合函数或统计函数或组函数

分类:
sum 求和、 avg 平均值、max 最大值、min 最小值、count 计算个数(非空值)

特点:
1、sum、avg一般用于处理数值型
   max、min、count可以处理任何类型
2、以上分组函数都忽略null值
3、可以和distinct搭配实现去重的运算
4、count 详细介绍
   一般使用 COUNT(*) 用于统计行数
5、和分组函数一同查询的字段要求是group by 后的字段
*/

#简单的使用
SELECT SUM(salary) FROM employees;
SELECT AVG(salary) FROM employees;
SELECT MIN(salary) FROM employees;
SELECT MAX(salary) FROM employees;
SELECT COUNT(salary) FROM employees;

SELECT 
  SUM(salary) AS 和,
  AVG(salary) AS 平均,
  MAX(salary) AS 最高,
  MIN(salary) AS 最低,
  COUNT(salary) 个数 
FROM
  employees ;

SELECT 
  SUM(salary) AS 和,
  ROUND(AVG(salary),2) AS 平均,
  MAX(salary) AS 最高,
  MIN(salary) AS 最低,
  COUNT(salary) 个数 
FROM
  employees ;

#和 distinct 搭配
SELECT COUNT(DISTINCT salary) FROM employees; # distinct与参数之间是空格

# count函数的详细介绍,支持4种重载
SELECT COUNT(salary) FROM employees;
SELECT COUNT(*) FROM employees;  #统计总行数 用的最多
SELECT COUNT(1) FROM employees; #在表中加了 一列 的常量值( 1,2'崔侠'等等常量 ) 并统计行总行数
#效率:
# myisam 存储引擎下 count(*)的效率最高
# innodb 存储引擎下 COUNT(*) 和 count(1)的效率差不多, 比 count(字段)要高一些

#和分组函数一同查询的字段有限制
#和分组函数一同查询的字段:要求是group by 后的字段

#查询公司员工工资的最大值,最小值,平均值,总和
SELECT MAX(salary),MIN(salary),AVG(salary),SUM(salary) FROM employees;

#查询员工表中的最大入职时间和最小入职时间的长差天数  ( diffrence )
SELECT 
  DATEDIFF(    # datediff函数 计算2个日期之间的天数
    MAX(DISTINCT hiredate),
    MIN(DISTINCT hiredate)
  ) AS 'diffrence' 
FROM
  employees ;

#-------------------------------  
SELECT DATEDIFF(NOW(),'1995.1.1');
  
#查询部门编号为90的员工个数
SELECT COUNT(*) FROM employees WHERE department_id = 90;
View Code

  5、分组查询

#进阶5:分组查询

/*
语法:
    select 分组函数,列(要求出现在group by 的后面)
    from 表
    【where 筛选条件】
    group by 分组的列表
    【order by 子句】
注意:
    查询列表必须特殊,要求是分组函数 和 group by后出现的字段
特点:
    1、分组查询中的筛选条件分为两类
             数据源           位置                   关键字
    分组前筛选   原始表           group by 子句的前面    where   
    分组后筛选   分组后的结果表   group by 子句的后面    having
    分组函数做条件肯定是放在having子句中
    能用分组前筛选的,就优先考虑使用分组前筛选
    2、group by子句支持单个字段分组,多个字段分组(多个字读之间用逗号分开没有顺序要求),表达式或函数(用的较少)
    3、也可以添加排序(排序放在整个分组查询的最后)
*/

#引入:查询每个部门的平均工资
SELECT AVG(salary) FROM employees; #整个公司的平均工资

#可以使用group by 子句将表中的数据分成若干组  
#查询列表的特殊性:
#查询列表:department_id 是 group by 后出现的字段, AVG(salary) 是分组函数

SELECT department_id, AVG(salary) FROM employees GROUP BY department_id;

/* #查询语句
select column,group_function(column)   # 列 column
from table
[ where condition ]   # where 一定在 from 之后
[ group by group_by_expression ]
[ order by column ] ;
*/

#简单的分组查询
#查询每个工种的最高工资
SELECT job_id, MAX(salary) FROM employees GROUP BY job_id;

#查询每个位置撒谎那个的部门个数
SELECT COUNT(*),location_id FROM departments GROUP BY location_id;

#添加分组前的筛选条件

#查询邮箱中包含a字符的,每个部门的平均工资

SELECT AVG(salary),department_id
FROM employees
WHERE email LIKE '%a%'
GROUP BY department_id;

#查询有奖金的每个领导手下员工的最高工资

SELECT MAX(salary),manager_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY manager_id;

#添加分组后的筛选条件

#查询哪个部门的员工个数 > 2

#查询每个部门的员工个数
SELECT COUNT(*), department_id
FROM employees
GROUP BY department_id;

#根据上面查询结果进行筛选,查询哪个部门的员工个数 > 2
SELECT COUNT(*), department_id
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 2 ;  # having

#查询每个工种有奖金的员工的最高工资 > 12000 的工种编号和最高工资
#查询每个工种有奖金的员工的最高工资
SELECT MAX(salary), job_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id;

#根据上述条件继续筛选,最高工资 > 12000
SELECT MAX(salary), job_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING MAX(salary) > 12000;

#查询领导编号 > 102 的每个领导手下的最低工资>5000的领导编号是哪个,以及最低工资
#查询每个领导手下的员工最低工资
SELECT MIN(salary),manager_id
FROM employees
GROUP BY manager_id;

#添加筛选条件 : 编号>102

SELECT MIN(salary),manager_id
FROM employees
WHERE manager_id > 102
GROUP BY manager_id;

#添加筛选条件;最低工资 > 5000

SELECT MIN(salary),manager_id
FROM employees
WHERE manager_id > 102
GROUP BY manager_id
HAVING MIN(salary) > 5000;

#按表达式或函数分组

#按员工姓名的长度分组,查询每一组员工个数>5的有哪些

#查询每个长度的员工的个数
SELECT COUNT(*), LENGTH(last_name)
FROM employees
GROUP BY LENGTH(last_name); #按长度分组 

#添加筛选条件
SELECT COUNT(*), LENGTH(last_name)
FROM employees
GROUP BY LENGTH(last_name)
HAVING COUNT(*)>5;  #分组后的筛选条件

#group byhaving 后支持别名
SELECT COUNT(*) AS C, LENGTH(last_name) AS LEN
FROM employees
GROUP BY LEN   #别名 LEN
HAVING C > 5;  #别名 C

#按多个字段分组

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

#添加排序
#查询每个部门每个工种的员工的平均工资,并且按平均工资的高低显示
SELECT AVG(salary),department_id,job_id
FROM employees
GROUP BY job_id,department_id
ORDER BY AVG(salary) DESC;    #分组后排序

#部门编号不为null 平均工资 > 10000
SELECT AVG(salary),department_id,job_id
FROM employees
WHERE department_id IS NOT NULL  #分组前用 where
GROUP BY job_id,department_id
HAVING AVG(salary) > 10000    #分组后用 having
ORDER BY AVG(salary) DESC;    #分组后排序

#查询各job_id的员工工资的最大值,最小值,平均值,总和,并按job_id升序
SELECT MAX(salary),MIN(salary),AVG(salary),SUM(salary),job_id
FROM employees
GROUP BY job_id;
ORDER BY job_id ASC;

#查询员工最高工资和最低工资的差距(diffrence)
SELECT MAX(salary) - MIN(salary) AS DIFFRENCE  #表达式  MAX(salary) - MIN(salary)
FROM employees;

#查询各管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内
SELECT MIN(salary), manager_id
FROM employees
WHERE manager_id IS NOT NULL
GROUP BY manager_id
HAVING MIN(salary) >= 6000;

#查询所有部门的编号,员工数量和工资平均值,并按平均值工资降序
SELECT COUNT(*),AVG(salary), department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary) DESC;  #排序 :按平均工资降序

#选择具有各个job_id的员工人数
SELECT COUNT(*),job_id
FROM employees
GROUP BY job_id;
View Code

  6、多表查询(连接查询)

#进阶6:连接查询
/*
含义:又称多表查询,当查询的字段来自多个表时,就会用到连接查询
    select name,boyName from beauty,boys;
    笛卡尔乘积现象: 表1 有m行,表2有n行,结果=m*n行
    发生原因: 没有有效的连接条件
    如何避免: 添加有效的连接条件
分类:
    按年代分类
    sql92标准:  仅仅支持内连接
    sql99标准【推荐】:支持内连接 + 外连接(左外+右外) + 交叉连接
    按功能分类:
        内连接:
            等值连接
            非等值连接
            自连接
        外连接:
            左外连接
            右外连接
            全外连接
        交叉连接    

*/
SELECT * FROM beauty;
SELECT * FROM boys;
SELECT NAME,boyName FROM beauty,boys; #有问题的查询,一个表的每个记录与下个表的每个记录匹配,笛卡尔乘积现象

#一、sql92标准
#1、等值连接
/*
    多表等值连接的结果为多表的交集部分
    N表连接,至少需呀N-1个连接条件
    多表的顺序没有要求
    一般需要为表起别名
    可以搭配前面介绍的所有子句使用,比如排序、分组、筛选
*/

#查询女神名和对应的男神名
SELECT NAME,boyName FROM beauty,boys WHERE beauty.boyfriend_id = boys.id; #连接条件

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

#查询员工号、工种号、工种名 (限定字段需要,考虑表名比较长,为表起别名)
SELECT last_name, employees.job_id, job_title # 限定job_id 为 employees 表的字段,因与jobs表 字段名一样 不限定会产生歧义错误
FROM employees, jobs
WHERE employees.`job_id` = jobs.`job_id`;

#起别名: 
/*提高语句的简洁度 区分多个重名的字段
注意:如果为表起了别名,则查询的字段就不能使用原来的表名去限定
*/
SELECT last_name, e.job_id, job_title
FROM employees AS e, jobs AS j
WHERE e.`job_id` = j.`job_id`;

#两个表的顺序是否可以调换,答案: 可以
SELECT e.last_name, e.job_id, j.job_title
FROM jobs AS j, employees AS e
WHERE e.`job_id` = j.`job_id`;

#可以加筛选

#查询有奖金的员工名、部门名
SELECT last_name, department_name,commission_pct
FROM employees, departments
WHERE employees.`department_id` = departments.`department_id` AND commission_pct IS NOT NULL;

#查询城市名中第二个字符为o部门名和城市名
SELECT department_name,city
FROM departments,locations
WHERE departments.`location_id` = locations.`location_id` AND city LIKE '_o%'; #先去加连接条件

#可以加分组

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

#查询出有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资
SELECT MIN(salary),department_name,departments.manager_id  #部门表的部门名领导编号
FROM employees, departments
WHERE commission_pct IS NOT NULL AND employees.`department_id` = departments.`department_id`
GROUP BY department_name, departments.manager_id  #不是一一对应


#可以加排序
#查询每个工种的工种名和员工的个数,并按员工个数降序

SELECT jobs.job_title, COUNT(*)
FROM employees,jobs
WHERE employees.`job_id` = jobs.`job_id`
GROUP BY jobs.`job_title`      # group by
ORDER BY COUNT(*) DESC;

#可以实现三表连接
#查询员工名、部门名和所在的城市
SELECT last_name,department_name, city
FROM employees,departments,locations
WHERE employees.`department_id` = departments.`department_id` AND departments.`location_id` = locations.`location_id`
AND city LIKE 'S%'              # and 城市以S开头
ORDER BY department_name DESC;  #排序 order by

#别名
SELECT last_name,department_name, city
FROM employees AS e, departments AS d,locations AS l
WHERE e.`department_id` = d.`department_id` AND d.`location_id` = l.`location_id`;

#2、非等值连接

#查询员工的工资和工资级别    #between and  表1的每行与表2的每行比较,看哪个符合条件

SELECT salary, grade_level
FROM employees,job_grades
WHERE employees.`salary` BETWEEN job_grades.`lowest_sal` AND job_grades.`highest_sal`;
#and grade_level = 'A';  #工资级别 = 'A'


/*CREATE TABLE job_grades  #工资级别表 脚本
(grade_level VARCHAR(3),
 lowest_sal  int,
 highest_sal int);

INSERT INTO job_grades
VALUES ('A', 1000, 2999);

INSERT INTO job_grades
VALUES ('B', 3000, 5999);

INSERT INTO job_grades
VALUES('C', 6000, 9999);

INSERT INTO job_grades
VALUES('D', 10000, 14999);

INSERT INTO job_grades
VALUES('E', 15000, 24999);

INSERT INTO job_grades
VALUES('F', 25000, 40000);*/

#3、自连接,相当于等值连接  #一个表当多个表使用
# 查询员工名和上级的名
SELECT e.`employee_id`, e.`last_name` AS employee_name, m.`employee_id`, m.`last_name` AS manager_name
FROM employees AS e, employees AS m       #m 别名就是领导, e 别名就是员工
WHERE e.`manager_id` = m.`employee_id`;   #员工e的领导m的id 与 领导m的员工id比较,相等,就是领导的员工id

#显示员工表的最大工资,工资平均值
SELECT MAX(salary),AVG(salary)
FROM employees;

#查询员工表的employee_id, job_id,last_name,按department_id降序,salary升序
SELECT employee_id,job_id,last_name
FROM employees
ORDER BY department_id DESC, salary ASC;

#查询员工表的job_id中包含 a 和 e 的,并且 a 在 e 的前面
SELECT job_id
FROM employees
WHERE job_id LIKE '%a%e%';

#已知表 student 里面有 id(学号),name,gradeId(年级编号)
#已知表 grade 里面有 id (年级编号),name(年级名)
#已知表 result 里面有 id, score, studentNO (学号)
#要求查询姓名、年级名、成绩
SELECT student.name,grade.name,result.score
FROM student,grade,result
WHERE student.gardeId = grade.id AND student.id = result.studentNO;

#显示当前日期,以及去前后空格,截取子字符串的函数
SELECT NOW();
SELECT TRIM(' ' FROM ' f da ');
SELECT SUBSTR(' sdfds df ',4,3); #索引从1开始

#向上取整,随机100以内(不含100) 
SELECT CEIL( RAND()*100 );  # rand 函数随机产生 0-1 之间的小数,无限接近于1,但不到1

#显示所有员工的姓名,部门号和部门名称
SELECT last_name, employees.`department_id`,department_name
FROM employees,departments
WHERE employees.`department_id` = departments.`department_id`;

#查询90号部门员工的job_id和90号部门的location_id
SELECT job_id,location_id,employees.`department_id`
FROM employees,departments
WHERE employees.`department_id` = 90 AND employees.`department_id` = departments.`department_id`;

#选择所有有奖金的员工的last_name, department_name,location_id,city
SELECT last_name, department_name,d.`location_id`,city
FROM employees AS e, departments AS d, locations AS l
WHERE e.`commission_pct` IS NOT NULL AND e.`department_id` = d.`department_id` AND d.`location_id` = l.`location_id`;

#选择city在Toronto工作的员工的 last_name,job_id,department_id,department_name
SELECT last_name, job_id,d.department_id,department_name,city
FROM employees AS e, departments AS d, locations AS l
WHERE city = 'Toronto' AND e.`department_id` = d.`department_id` AND d.`location_id` = l.`location_id`;

#查询每个工种、每个部门的部门名、工种名和最低工资
SELECT department_name, e.job_id,job_title, MIN(salary)
FROM employees AS e, departments AS d, jobs AS j
WHERE e.`department_id` = d.`department_id` AND e.`job_id` = j.`job_id`  #连接
GROUP BY department_name, job_title; #查询每个工种、每个部门的(分组)部门名、工种名和最低工资

#查询每个国家下的部门个数大于2的国家编号
SELECT country_id, COUNT(*) AS 部门个数
FROM departments AS d, locations AS l
WHERE d.`location_id` = l.`location_id` #连接
GROUP BY country_id  #按国家分组  每个国家
HAVING COUNT(*) > 2;

#选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号,结果类似于下面的格式
/* 
   employees  Emp#     manager  Mgr #
   kochhar    101      King     100
*/

SELECT e.`last_name` AS employees, e.`employee_id` AS 'Emp#', m.`last_name` AS manage, m.`employee_id` AS 'Mgr#'
FROM employees AS e, employees AS m
WHERE e.`last_name` = 'kochhar' AND e.`manager_id` = m.`employee_id`;

#二、sql99语法
/*
语法:
    select 查询列表
    from 表1 别名 【连接类型】
    join 表2 别名 
    on 连接条件
    【where 筛选条件】
    【group by 分组】
    【having 筛选条件】
    【order by 排序列表】

内连接 : inner
外连接 :
    左外: left  【outer】
    右外: right  【outer】
    全外: full  【outer】
交叉连接: cross join

*/

#内连接
/*
语法:
    select 查询列表
    from 表1 别名
    inner join 表2 别名
    on 连接条件

分类:
    等值
    非等值
    自连接
特点:
1、添加排序、分组、筛选
2、inner 可以省略
3、筛选条件放在where后面,连接条件放在on后面,提高分离性,便于阅读
4、inner join 连接和 sql92语法中的等值连接效果是一样的,都是查询多表的交集

*/

#1、等值连接

#查询员工名、部门名
SELECT last_name, department_name
FROM employees
INNER JOIN departments                                      #92的逗号
ON employees.`department_id` = departments.`department_id`; #92的where

#查询名字中包含e的员工名和工种名(筛选)
SELECT last_name,job_title
FROM employees
INNER JOIN jobs
ON employees.`job_id` = jobs.`job_id`  #连接条件
WHERE last_name LIKE '%e%';            #筛选条件

#查询部门个数>3的城市名和部门个数 (分组+筛选)
#查询每个城市的部门个数
#在上述结果上筛选满足条件的
SELECT city, COUNT(*)
FROM departments
INNER JOIN locations
ON departments.`location_id` = locations.`location_id`
GROUP BY city         #分组  以城市名 比对 departments 表
HAVING COUNT(*) > 3;  #筛选  分组的基础上

#查询哪个部门员工个数 > 3的部门名员工个数,并按个数降序(排序)
#查询每个部门的员工个数
SELECT COUNT(*), department_name  #每个部门名 department_name
FROM employees
INNER JOIN departments
ON employees.`department_id` = departments.`department_id`
GROUP BY department_name
HAVING COUNT(*) > 3           #筛选员工个数>3的记录,并排序
ORDER BY COUNT(*) DESC;

#查询员工名、部门名、工种名,并按部门名降序

SELECT last_name, department_name, job_title
FROM employees
INNER JOIN departments ON employees.`department_id` = departments.`department_id`
INNER JOIN jobs ON employees.`job_id` = jobs.`job_id`

#按部门名排序
ORDER BY department_name DESC;

#非等值连接

#查询员工的工资级别
SELECT salary, grade_level # grade_level 字段对应 lowest_sal 字段 highest_sal 字段
FROM employees
INNER JOIN job_grades 
ON employees.`salary` BETWEEN job_grades.`lowest_sal` AND job_grades.`highest_sal`; # salary 对应符合条件的

#查询每个工资级别的个数大于20的个数,并且按工资级别降序
SELECT COUNT(*), grade_level # grade_level 字段对应 lowest_sal 字段 highest_sal 字段
FROM employees
INNER JOIN job_grades 
ON employees.`salary` BETWEEN job_grades.`lowest_sal` AND job_grades.`highest_sal` #非等值连接条件 工资--工资级别
GROUP BY grade_level   #工资级别  分组
HAVING COUNT(*) > 20   #分组基础上 筛选
ORDER BY grade_level DESC; #工资级别 排序

#自连接

#查询员工的名字、上级的名字
SELECT e.`last_name`, m.`last_name`
FROM employees e
INNER JOIN employees m ON e.`manager_id` = m.`employee_id`;

#查询姓名中包含字符k的员工的名字、上级的名字
SELECT e.`last_name`, m.`last_name`
FROM employees e
INNER JOIN employees m ON e.`manager_id` = m.`employee_id`
WHERE e.`last_name` LIKE '%k%';

#外连接: 分主、从表,有两部分,相当于内连接的交集的一部分,还有一部分是从表没有的部分(NULL/*
应用场景: 用于查询一个表中有,另一个表中没有的记录

特点:
1、外连接的查询结果为主表中的所有记录,
    如果从表中有和它匹配的,则显示匹配的值
    如果表中没有和它匹配的,则显示null
    外连接查询结果=内连接结果+主表有而从表没有的记录
2、左外连接,left join 左边的是主表,
   右外连接,right join 右边的是主表
3、左外和右外交换连个表的顺序,可以实现同样的效果
4、全外连接 = 内连接的结果+表1中有但表2没有的+表2中有但表1没有的
    
*/

#引入: 查询没有男朋友的女神名
SELECT * FROM beauty;
SELECT * FROM boys;

SELECT beauty.`name`, boyName
FROM beauty
INNER JOIN boys ON beauty.`boyfriend_id` = boys.`id`; #男朋友在男神表中的女神 交集


#查询男朋友不在男神表的女神名  #男神表中没有的  男朋友(不在男神表)
#查询的信息主要来自哪个表,哪个表就是主表

#左外连接
SELECT b.name, bo.*
FROM beauty AS b
LEFT OUTER JOIN boys bo
ON b.`boyfriend_id` = bo.`id`
WHERE bo.`id` IS NULL;  #b.`boyfriend_id` = bo.`id`    bo.id不可能为空,从表中的主键列(字段)

/*
#左外连接 从表中没有的

update beauty set boyfriend_id = 9 where id = 10; #把boyfriend_id值 等于 4的修改为9

SELECT b.name, bo.*
FROM boys AS bo  #主表 boys
left OUTER JOIN beauty b  #从表beauty
ON b.`boyfriend_id` = bo.`id`
WHERE b.`id` IS NULL;  #beauty表中的boyfriend_id 没有值为4的, boys表中的id为4 可以查出来
*/

#右外连接 与左外连接结果一样的
SELECT b.name, bo.*
FROM boys AS bo  #主表 boys
RIGHT OUTER JOIN beauty b  #从表beauty
ON b.`boyfriend_id` = bo.`id`
WHERE bo.`id` IS NULL;  #boys 的 id 为 null

#修改字段值 
UPDATE boys SET usercp = NULL WHERE id = 3; #修改boys表 字段id为3的 字段usercp的值为null
SELECT * FROM boys;

#查询哪个部门没有员工
#查询的信息主要来自哪个表,哪个表就是主表
#左外  部门是主表
SELECT d.*, e.employee_id
FROM departments AS d
LEFT OUTER JOIN employees AS e
ON d.`department_id` = e.`department_id`
WHERE e.`employee_id` IS NULL;  # 从表employees的主键列 employee_id

#右外 右边的是主表
#右外连接,将返回右表的所有行,如果右表的某行在左表中没有匹配行,则将为左表返回空值
SELECT d.*, e.employee_id
FROM employees AS e
RIGHT OUTER JOIN departments AS d  # 右外连接 右表是  departments
ON d.`department_id` = e.`department_id`
WHERE e.`employee_id` IS NULL; #左表 employees 的空值

#左连接,字段来自左表,右连接,字段来自右表

#全外  ( 三部分组成 ) mysql不支持

#use girls;
SELECT b.*, bo.*
FROM beauty AS b
FULL OUTER JOIN boys AS bo
ON b.`boyfriend_id` = bo.id;

#交叉连接
SELECT b.*, bo.*
FROM beauty AS b
CROSS JOIN boys AS bo; #笛卡尔乘积 表1一行对表2所有行


/*
sql92和sql99
功能: sql99支持的较多
可读性: sql99实现连接条件和筛选条件分离,可读性较高

#内连接,两个表的交集部分
select 查询列表
from A
inner join B
on A.key = B.key

#左外连接,查询表A中所有记录
select 查询列表
from A
left join B
on A.key = B.key
#where B.key is null # 从表B 没有的去掉

#右连接,查询表B中所有记录
select 查询列表
from B
right join A
on A.key = B.key
#where A.key is null # 从表A 没有的去掉.

#全外连接,实现两个表的全部
select 查询列表
from A
full join B
on A.key = B.key

#全外连接,去掉两个表的交集
select 查询列表
from A
full join B
on A.key = B.key
where A.key is null or B.key is null;

*/

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

#查询那个城市没有部门
SELECT city,departments.`department_id`
FROM locations
LEFT OUTER JOIN departments
ON locations.`location_id` = departments.`location_id`
WHERE departments.`department_id` IS NULL;

#查询部门名为SAL或IT的员工信息 #有可能departments表中部门名为 SAL 或 IT的没有员工,所以用外连接
SELECT e.*, d.`department_name`, d.`department_id` #部门名一样,部门编号不一定一样
FROM departments AS d  #主表是 departments表
LEFT OUTER JOIN  employees AS e 
ON e.`department_id` = d.`department_id`
WHERE d.`department_name` IN( 'SAL','IT' );
View Code

  7、子查询

#进阶7: 子查询
/*
含义:
出现在其他语句中的select语句,称为子查询或内查询
外部的查询语句,称为主查询或外查询
子查询,一般放在小括号内

分类:
按自查询出现的位置:
    select后面: 
        标量子查询
    from之后:
        表子查询
    where或having后面:
        标量子查询(单行)
        列子查询 (多行)
        
        行子查询
    exists 后面: #存在 相关子查询
        表子查询
    
按结果集的行列数不同:
    标量子查询(结果集只有一行一列)
    列子查询(结果集只有一列多行)
    行子查询(结果集有一行多列)
    表子查询(结果集一般为多行多列)

*/

SELECT first_name FROM employees 
WHERE department_id IN(SELECT department_id FROM departments WHERE location_id = 1700);#多行

#一、where或having后面
/*

1、标量子查询(单行子查询)
2、列子子查询(多行子查询)
3、行子查询(多列多行)

特点:
1、子查询放在小括号内
2、子查询一般放在条件的右侧
3、标量子查询,一般搭配着单行操作符使用 > < >= <= = <>
   列子查询,一般搭配着多行操作符使用 in 、any/some、 all
4、子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果

*/

#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'
);

#子查询,返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资

#查询141号员工的job_id
SELECT job_id FROM employees WHERE employee_id = '141';
#查询143号员工的salary
SELECT salary FROM employees WHERE employee_id = '143'

#查询员工的姓名,job_id和工资,要求 job_id =  且salary > 
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'
)  ;

#子查询,公司工资最少的员工的last_name,job_id和salary

#公司最少工资
SELECT MIN(salary) FROM employees;

#公司最少工资的员工,姓名,job_id,salary
SELECT last_name, job_id, salary
FROM employees
WHERE salary = (         #单行操作符 =
    SELECT MIN(salary) 
    FROM employees 
);

#子查询,查询 最低工资  大于  50号部门最低工资 的部门id和其最低工资

#50号部门最低工资
SELECT MIN(salary)
FROM employees
WHERE department_id = 50;

#每个部门的最低工资

SELECT department_id, MIN(salary) AS min_sal
FROM employees
WHERE department_id IS NOT NULL
GROUP BY department_id;

#最低工资大于 50号部门最低工资 的id和其最低工资

SELECT department_id, MIN(salary) AS min_sal
FROM employees
WHERE department_id IS NOT NULL
GROUP BY department_id
HAVING min_sal > (                 #单行操作符 >
    SELECT MIN(salary)
    FROM employees
    WHERE department_id = 50
);

#非法使用标量子查询

#2、列子查询(多行子查询,一列多行)
# 多行比较操作符 in/not in 等于列表中(子查询语句)的任意一个、 any/some 和某一个值比较、 all 和所有值比较

#返回location_id 是 1400 或 1700的部门中的所有员工姓名

#查询location_id 是1400 或 1700的部门的编号
SELECT DISTINCT department_id #去重
FROM departments
WHERE location_id IN(1400,1700);

#查询员工姓名,要求部门号是上述列表中的某一个

SELECT last_name
FROM employees
WHERE department_id IN( # in 任意一个,可以用 = any替换, not in 可以用<>all 替换
    SELECT DISTINCT department_id #去重
    FROM departments
    WHERE location_id IN(1400,1700)
);

#---------------------
SELECT last_name, location_id
FROM employees
INNER JOIN departments
ON employees.`department_id` = departments.`department_id`
AND location_id IN (1400,1700);

#返回其他工种中比job_id为'IT_PROG'工种任一工资低的员工的:工号、姓名、job_id以及salary

#查找job_id为'IT_PROG'部门任一工资
SELECT DISTINCT salary FROM employees WHERE job_id = 'IT_PROG'); #去重

#查询工号、姓名、job_id以及salary  salary < any()  
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'; #其他工种

#或者 比最大的低
SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary < (                    #比最大的低,即比任一个低
    SELECT DISTINCT MAX(salary) #最大的
    FROM employees 
    WHERE job_id = 'IT_PROG'
) AND job_id <> 'IT_PROG'; 

#返回其他工种中比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'; 
#或 比最小的低
SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary < (        #比最小的低,即比所有
    SELECT DISTINCT MIN(salary) #最小的
    FROM employees 
    WHERE job_id = 'IT_PROG'
) AND job_id <> 'IT_PROG'; #其他工种

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

#查询员工编号最小且工资最高的员工信息
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 *
FROM employees
WHERE (employee_id,salary) = (
    SELECT MIN(employee_id), MAX(salary)
    FROM employees
);


#二、select 后面
/*
仅仅支持标量子查询,即只能一行一列
*/

#查询每个部门的员工个数

SELECT d.*, (
    SELECT COUNT(*) 
    FROM employees e
    WHERE e.department_id = d.department_id  #两个表连接条件 每个部门
)个数
FROM departments d;

#查询员工号=102的部门名
SELECT (
    SELECT department_name
    FROM departments AS d
    INNER JOIN employees AS e
    ON d.department_id = e.department_id
    WHERE e.employee_id = 102
)部门名;

#三、from 后面
/*
将子查询结果充当一张表,要求必须起别名
*/

#查询每个部门的平均工资的工资等级

#查询每个部门的平均工资
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id;

SELECT * FROM job_grades;

#连接结果和job_grades表,筛选条件平均工资 between ... and ...
SELECT ag_dep.*, g.`grade_level`
FROM (
    SELECT AVG(salary) AS ag,department_id
    FROM employees
    GROUP BY department_id
) AS ag_dep    #作为一个表,起别名
INNER JOIN job_grades g
ON ag_dep.ag BETWEEN lowest_sal AND highest_sal;

#四、exists后面(相关子查询) #先执行外查询,再内查询
/*
语法:
exists(完整的查询语句)
结果:
1或0

*/

# EXISTS 布尔类型 
SELECT EXISTS(SELECT employee_id FROM employees WHERE salary = 300000);

#查询有员工的部门名

#in
SELECT department_name
FROM departments
WHERE department_id IN(
    SELECT department_id
    FROM employees
);

#exists
SELECT department_name
FROM departments d
WHERE EXISTS(   #满足有员工  的条件
    SELECT *
    FROM employees e
    WHERE d.department_id = e.department_id # 部门有员工
);

#查询没有女朋友的男神信息

#in

SELECT *
FROM boys
WHERE boys.`id` NOT IN (
    SELECT boyfriend_id
    FROM beauty
);

#exists

SELECT *
FROM boys
WHERE NOT EXISTS(
    SELECT boyfriend_id
    FROM beauty
    WHERE beauty.`boyfriend_id` = boys.`id`
);


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

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

#查询各部门中工资比本部门平均工资高的员工的员工号、姓名和工资

#查询各部门的平均工资
SELECT AVG(salary), department_id
FROM employees
GROUP BY department_id;  #分组,每组(部门)的平均工资

#连接上述结果集表 和 employees表,进行筛选
SELECT employee_id, last_name, salary,e.`department_id`
FROM employees e
INNER JOIN (
    SELECT AVG(salary) AS avg_sal, department_id
    FROM employees
    GROUP BY department_id    
) AS avg_dep   #结果集表,的department_id = employees表的 department_id
ON e.`department_id` = avg_dep.department_id  #本部门,连接条件
WHERE e.salary > avg_dep.avg_sal; #工资大于结果集表的平均工资

#查询 和姓名中包含字母u的员工 在相同部门的员工 的员工号和姓名

#查询姓名中包含字母u的员工 所在的部门
SELECT DISTINCT department_id #去重
FROM employees
WHERE last_name LIKE '%u%';

#查询部门号 = 上述结果集中的任意一个的员工号和姓名
SELECT last_name, employee_id
FROM employees
WHERE department_id IN (  #与上述结果集,相同部门,即在 该结果集(部门) 中的员工
    SELECT DISTINCT department_id #去重
    FROM employees
    WHERE last_name LIKE '%u%'
);

#查询在部门的location_id为1700的部门工作的员工的员工号

#查询 部门的location_id为1700的部门

SELECT DISTINCT department_id, location_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
);

#查询管理者是K_ing的员工姓名和工资

#内连接
SELECT e.last_name AS e_name, e.salary AS e_sal, m.`last_name` AS m_name
FROM employees e
INNER JOIN employees m
ON e.`manager_id` = m.`employee_id`
WHERE m.`last_name` = 'K_ing';

# last_name = 'K_ing' 的 employee_id
SELECT employee_id
FROM employees
WHERE last_name = 'K_ing';

# manager_id 在上述结果集中
SELECT last_name, salary
FROM employees 
WHERE manager_id IN (
    SELECT employee_id
    FROM employees
    WHERE last_name = 'K_ing'
);

#查询工资最高的员工的姓名,要求first_name 和last_name 显示为一列,列名为姓.名

#查询最高工资
SELECT MAX(salary)
FROM employees;

#最高工资的姓.名
SELECT CONCAT(first_name,'.',last_name) AS '姓.名'
FROM employees
WHERE salary =(
    SELECT MAX(salary)
    FROM employees
);

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

#2、查询平均工资最低的部门信息

/*
select min(avg_sal)
from (  select avg(salary) as avg_sal, department_id
    from employees
    group by department_id
) as avg_sal_dep #各部门平均工资工资表
*/

#方式一:

#各部门的平均工资
SELECT AVG(salary) AS avg_sal, department_id
FROM employees
GROUP BY department_id;

#查询上述结果集中的最低平均工资
SELECT MIN(avg_sal)  #最低平均工资
FROM (  SELECT AVG(salary) AS avg_sal, department_id
    FROM employees
    GROUP BY department_id
) AS avg_sal_dep; 

#查询哪个部门的平均工资=上述结果

SELECT department_id   #平均工资、部门编号   AVG(salary), 
FROM employees
GROUP BY department_id #部门编号分组
HAVING AVG(salary) = (            #平均工资 = 最低平均工资的
    SELECT MIN(avg_sal)       #各部门平均工资 的最低平均工资
    FROM (  SELECT AVG(salary) AS avg_sal, department_id  #各部门平均工资
        FROM employees
        GROUP BY department_id
    ) AS avg_sal_dep
);

#查询部门信息
SELECT *
FROM departments       ##### 表departments
WHERE department_id = (       
    SELECT department_id   #平均工资最低的部门
    FROM employees ##### 表employees
    GROUP BY department_id #部门编号分组
    HAVING AVG(salary) = (
        SELECT MIN(avg_sal)  #最低评价工资
        FROM (  SELECT AVG(salary) AS avg_sal, department_id
            FROM employees
            GROUP BY department_id
        ) AS avg_sal_dep
    )
);

#方式二:

#各部门的平均工资
SELECT AVG(salary), department_id
FROM employees
GROUP BY department_id;

#求出最低平均工资的部门编号
SELECT department_id #求部门编号
FROM employees
GROUP BY department_id #按部门编号分组
ORDER BY AVG(salary) #平均工资排序
LIMIT 1;         #排第一的部门即使最低平均工资的部门编号

#查询部门信息
SELECT *
FROM departments
WHERE department_id = (
    SELECT department_id
    FROM employees
    GROUP BY department_id
    ORDER BY AVG(salary)
    LIMIT 1
);

#3、查询各部门中工资比本部门平均工资高的员工的员工号,姓名和工资



#各部门平均工资
SELECT AVG(salary), department_id
FROM employees
GROUP BY department_id;

#连接上述结果集和 employees 表
SELECT employee_id, last_name, salary, e.department_id
FROM employees AS e
INNER JOIN (
    SELECT AVG(salary) AS avg_sal, department_id
    FROM employees
    GROUP BY department_id #分组字段
)AS avg_sal_dep
ON e.department_id = avg_sal_dep.department_id  #连接条件
WHERE salary > avg_sal_dep.avg_sal; #筛选

#查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名

#姓名中包含字母u的员工所在的部门
SELECT DISTINCT department_id
FROM employees
WHERE last_name LIKE '%u%';

#部门相同的 方式一
SELECT employee_id, last_name, e.`department_id`
FROM employees AS e
INNER JOIN (
    SELECT DISTINCT department_id
    FROM employees
    WHERE last_name LIKE '%u%'
) AS u_dep
ON e.`department_id` = u_dep.department_id;

#方式二
SELECT employee_id, last_name, e.`department_id`
FROM employees AS e
WHERE e.`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
);

#查询管理者是K_ing的员工的姓名和工资

#自连接方式
SELECT e.`last_name`, e.`salary`
FROM employees AS e
INNER JOIN employees AS m
ON e.`manager_id` = m.`employee_id`
WHERE m.`last_name` = 'K_ing';

#查询姓名为K_ing的员工编号
SELECT employee_id
FROM employees
WHERE last_name = 'K_ing';

# 哪个员工的管理者编号 = 在上述结果集
SELECT last_name, salary
FROM employees
WHERE employees.`manager_id` IN (
    SELECT employee_id
    FROM employees
    WHERE last_name = 'K_ing'
);

#7、查询工资最高的员工的姓名,要求first_name 和 last_name显示为一列,列名为 姓.名

SELECT CONCAT(first_name,'.',last_name) AS '姓.名'
FROM employees
WHERE salary = (
    SELECT MAX(salary)
    FROM employees
);

#1、查询每个专业的学生人数
SELECT majorid,COUNT(*)
FROM student
GROUP BY majorid; # 按专业字段分组 字段属性即具体每个专业分组

#2、查询参加考试的学生中,每个学生的平均分、最高分
SELECT AVG(score),MAX(score),studentno
FROM result 
GROUP BY studentno; #每个学生

#3、查询姓张的每个学生的最低分大于60的学号、姓名

SELECT s.studentno, s.studentname, MIN(score)#最低分
FROM student AS s
INNER JOIN result AS r
ON s.studentno = r.studentno
WHERE s.studentname LIKE '%张%' #姓张的
GROUP BY s.studentno            #每个学生 分组
HAVING MIN(score) > 60;         #分组后筛选 > 60


#4、查询生日在'1988-1-1'后的学生姓名、专业名称
SELECT studentname,majorname
FROM student AS s
INNER JOIN major AS m
ON s.majorid = m.majorid
WHERE DATEDIFF( borndate, '1988-1-1' ) > 0 ;

#5、查询每个专业的男生人数和女生人数分别是多少
#方式一 :按专业、性别 分组
SELECT COUNT(*) AS 个数, sex, majorid
FROM student
GROUP BY sex, majorid;

#方式二:
SELECT majorid, 
(SELECT COUNT(*) FROM student WHERE sex = '' AND majorid = s.majorid),
(SELECT COUNT(*) FROM student WHERE sex = '' AND majorid = s.majorid)
FROM student s
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 = '张翠山'
    )
);

#7、查询大于60分的学生的姓名、密码、专业名
SELECT studentname, loginwd, majorname
FROM student                       #三表连接
INNER JOIN major
ON student.majorid = major.majorid
INNER JOIN result
ON student.studentno = result.studentno
WHERE result.score > 60;

#8、按邮箱位数分组,查询每组的学生个数

SELECT COUNT(*), LENGTH(email)
FROM student
GROUP BY LENGTH(email);

#9、查询学生名、专业名、分数
SELECT studentname, majorname,score
FROM student
INNER JOIN major
ON student.majorid = major.majorid
LEFT OUTER JOIN result  #没有分数的情况
ON student.studentno = result.studentno

#10、查询哪个专业没有学生,分别用左连接和右连接实现
#左连接
SELECT m.majorid,m.majorname,s.studentno
FROM major m
LEFT OUTER JOIN student s
ON m.majorid = s.majorid
WHERE s.studentno IS NULL;

#右连接
SELECT m.majorid,m.majorname,s.studentno
FROM student s
RIGHT OUTER JOIN major m
ON m.majorid = s.majorid
WHERE s.studentno IS NULL;

#11、查询没有成绩的学生人数
#外连
SELECT COUNT(*) # 下面结果的数,结果就是没有成绩的
FROM student s
LEFT OUTER JOIN result r 
ON s.sutdentno = r.studentno
WHERE r.id IS NULL;
View Code

  8、分页查询

#进阶8:分页查询
/*
应用场景: 当要显示的数据,一页显示不全,需要分页提交sql请求
语法:
    select 查询列表
    from 表
    【join type】 join 表2
    on 连接条件
    where 筛选条件
    group by 分组字段
    having 分组后的筛选
    order by 排序的字段
    limit 【offset,】 size;
    offset 要显示条目的起始索引(起始索引从0开始)
    size 要显示的条目个数
特点:
    limit语句在查询语句的最后,执行上也是最后
    公式:
        要显示页数page,每页的条目数size
        select 查询列表
        from 表
        limit (page-1)*size, size;
*/

#查询前5条员工信息
SELECT * FROM employees LIMIT 0, 5;
SELECT * FROM employees LIMIT 5;  #从第一条开始, 索引 0可以省略

#查询第11条到第25条
SELECT * FROM employees LIMIT 11, 15;

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

/*
已知表 stuinfo
id 学号
name 姓名
email 邮箱 john@126.com
gradeId 年级编号 #
sex 性别 男 女
age 年龄

已知表 grade
id 年级编号  #
gradeName 年级名称
*/

#查询 所有学员的邮箱的用户名 (注:邮箱中 @ 前面的字符)
#INSTR 函数,从一个字符串中查找指定子串(一个字符也是串)的位置(下标)
SELECT SUBSTRING(email,1,INSTR(email,'@')-1) AS 用户名 # INSTR(email,'@')-1 是用户名的字符串长度
FROM stuinfo;

SELECT INSTR('john@126.com','@'); # 下标从1开始,子串'@'的位置(下标为5)

SELECT 
  SUBSTRING(
    'john@126.com',
    1,
    INSTR('john@126.com', '@') - 1
  ) AS 用户名 ;  #第3个参数是用户名子串的长度 

#查询男生和女生的个数
SELECT COUNT(*), sex # group by 中字段,是 select 指定字段 来限制
FROM stuinfo
GROUP BY sex ;  #字段 sex 里面进行分组,男一组,女一组,分组计算

/*
select count(*), job_id #查询字段 job_id
from employees
group by job_id;  #每个 job_id 进行分组,统计个数
*/

#查询年龄 > 18岁的所有学生的姓名和年级名称
/*
select name,gradeName
from stuinfo as s
inner join grade as g
on s.gradeId = g.id
where s.age > 18;  
*/

#查询哪个年级的学生最小年龄 > 20SELECT MIN(age), gradeId 
FROM stuinfo 
GROUP BY gradeId
HAVING MIN(age) > 20; #分组基础上 筛选  having 

#查询语句中涉及的所有关键字及执行先后顺序

/*
select 查询列表              7
from 表                      1
连接类型 join 表2            2
on 连接条件                  3
where 筛选条件               4
group by 分组列表            5
having 分组后的筛选          6
order by 排序列表            8
limit 偏移,条目数           9

*/

#每一条语句都会生成一个虚拟的表格
View Code

  9、联合查询

#进阶9:联合查询 
/*
union 联合 合并:将多条查询语句的结果合并成一个结果
语法:
查询语句1
union
查询语句2
union
...

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

特点:
1、要求多条查询语句的查询列数是一致的
2、要求多条查询语句查询的每一列的类型和顺序最好是一致的
3、union关键字默认去重,如果使用 union all 可以包含重复项


*/

#查询部门编号 > 90 或者 邮箱中包含a的员工信息

#方式一:
SELECT * FROM employees WHERE email LIKE '%a%' OR department_id > 90;

#方式二: union
SELECT * FROM employees WHERE email LIKE '%a%'
UNION
SELECT * FROM employees WHERE department_id > 90;

#查询中国用户中男性的信息以及外国用户中男性的用户信息
SELECT id, cnname,csex, FROM t_ca WHERE csex = ''
UNION
SELECT t_id,tName,tGender FROM t_ua WHERE tGender = 'male';
View Code