Oracle单值函数以及多表查询

第三章:单值函数
    函数分为:
      1.单值函数
          1.字符函数
          2.日期函数
          3.转换函数
          4.数字函数
      2.分组函数(后面的章节再做学习)
    

    哑表dual
      dual是一个虚拟表,用来构成select的语法规则,oracle保证dual里面永远只有一条记录。
      例如:
      显示1+1的结果,可以看出,dual很多时候是为了构成select的标准语法
      select 1+1 from dual;

    
    字符函数
      LOWER        Converts to lowercase
      UPPER        Converts to uppercase
      INITCAP                    Converts to initial capitalization
      CONCAT    Concatenates values
      SUBSTR    Returns substring
      LENGTH    Returns number of characters
      NVL        Converts a null value    

      lower 把字符转为小写
          例如:把'HELLO'转换为小写
          select lower('HELLO')
          from dual;
          
          例如:把s_emp表中的last_name列的值转换为小写
          select lower(last_name)
          from s_emp;
        
      upper 把字符转换为大写
          例如:把'world'转换为大写
          select upper('world')
          from dual;

          例如:把s_emp表中的last_name列的值转换为大写
          select upper(last_name)
          from s_emp;
          
          
          例如:查询s_emp表中名字为Ngao的人信息
          这样是查不到:
          select last_name,salary,dept_id
          from s_emp
          where last_name='NGAO';
          这样就可以查询到了:
          select last_name,salary,dept_id
          from s_emp
          where upper(last_name)='NGAO';

      initcap 把字符串首字母转换为大写
          例如:把'hELLO'转换为首字母大写,其余字母小写
          select initcap('hELLO')
          from dual;

      
      concat 把俩个字符串连接在一起(类似之前的||的作用)
          例如:把'hello'和'world'俩个字符串连接到一起,并且起个别名为msg
          select concat('hello','world') msg
          from dual;
          
          例如:把first_name和last_name俩个列的值连接到一起
          select concat(first_name,last_name) as name
          from s_emp;

      
      substr 截取字符串
          例如:截取'hello'字符串,从第2个字符开始(包含第二个字符),截取后面连续的3个字符
          select substr('hello',2,3)
          from dual;


      length 获得字符串长度
          例如:获得'world'字符串的长度
          select length('world')
          from dual;

          例如:获得s_emp表中last_name列的每个值的字符长度
          select length(last_name)
          from s_emp;

      nvl 替换列中为null的值
          在前面的章节已经使用过了
          select last_name,nvl(commission_pct,0)
          from s_emp;



    数字函数
      ROUND        Rounds value to specified decimal
      TRUNC        Truncates value to specified decimal
      MOD        Returns remainder of division   


      round 四舍五入
          round(arg1,arg2)
          第一个参数表示要进行四舍五入操作的数字
          第二个参数表示保留到哪一位

          例如:
          保留到小数点后面2位
          select round(45.923,2)
          from dual;

          保留到个位 (个十百千万...)
          select round(45.923,0)
          from dual;//46

          保留到十位 (个十百千万...)
          select round(45.923,-1)
          from dual;//50

                                保留到十位 (个十百千万...)
          select round(45.923,-2)
          from dual; //0



      trunc 截取到某一位
          trunc(arg1,arg2)
          和round的用法一样,但是trunc只舍去不进位

          例如:
          截取到小数点后面2位
          select trunc(45.929,2)
          from dual;

          截取到个位 (个十百千万...)
          select trunc(45.923,0)
          from dual;

          截取到十位 (个十百千万...)
          select trunc(45.923,-1)
          from dual;//40            


      mod 取余
          mod(arg1,arg2)    //被除数 除数
          第一个参数表示要进行取余操作的数字
          第二个参数表示参数1和谁取余
          
          例如:
          把10和3进行取余 (10除以3然后获取余数)
          select mod(10,3)
          from dual;



    日期函数
      MONTHS_BETWEEN   Number of months between two dates
      ADD_MONTHS       Add calendar months to date
      NEXT_DAY           Next day of the date specified
      LAST_DAY           Last day of the month
      ROUND               Round to date at midnight
      TRUNC               Remove time portion  from date


         sysdate关键字
        表示系统的当前时间    
        例如:
        显示时间:当前时间
        select sysdate from dual;

        注意:sysdate进行加减操作的时候,单位是天
        例如:
        显示时间:明天的这个时候
        select sysdate+1 from dual;

        例如:
        显示时间:昨天的这个时候
        select sysdate-1 from dual;
        
        例如:
        显示时间:1小时之后的这个日期
        select sysdate+1/24 from dual;

        
      months_between 俩个日期之间相差多少个月(单位是月)
        例如:
        30天之后和现在相差多少个月
        select months_between(sysdate+30,sysdate)
        from dual;//前面的减后面的


      add_months  返回一个日期数据:表示一个时间点,往后推x月的日期
        例如:
        '01-2月-2019'往后推2个月
        select add_months('01-2月-2019',2)
        from dual;    
        
        例如:
        当前时间往后推4个月
        select add_months(sysdate,4)
        from dual;

        注意:这个数字也可以是负数,表示往前推x月

      next_day 返回一个日期数据:表示一个时间点后的下一个星期几在哪一天
        例如:
        离当前时间最近的下一个星期5是哪一个天
        select next_day(sysdate,'星期五')
        from dual;
        注意:
        如果要使用'FRIDAY',那么需要把当前会话的语言环境修改为英文
        alter session set nls_language=english;
        alter session set nls_language='simplified chinese';


      last_day 返回一个日期数据:表示一个日期所在月份的最后一天
        例如:
        当前日期所在月份的最后一天(月底)
        select last_day(sysdate)
        from dual;


      round  对日期进四舍五入,返回操作后的日期数据
        例如:
        把当前日期四舍五入到月
        select round(sysdate,'MONTH')
        from dual;
        测试:15号16号分别是舍弃还是进位
        
        把当前日期四舍五入到年
        select round(sysdate,'YEAR')
        from dual;
        
        //这个写法是错误的
        //数字函数也有一个round
        //俩个ronnd函数有冲突
        //所以这里不能使用默认的日期格式
        select round('01-2月-2019','MONTH')
        from dual;

      trunc  对日期进行截取 和round类似,但是只舍弃不进位




    类型转换函数
      TO_CHAR converts a number or date string to a character string.
      TO_NUMBER converts a character string containing digits to a number.
    TO_DATE converts a character string of a date to a date value.
      

      to_char 把日期转换为字符

        例如:
        把当前日期按照指定格式转换为字符串
        select to_char(sysdate,'yyyy')
        from dual;
        
          日期格式:
      yyyy:四位数的年份
          rrrr:四位数的年份
         yy:两位数的年份
          rr:两位数的年份
      mm:两位数的月份(数字)
          D:一周的星期几//'从周六开始算'
      DD:一月的第几天
          DDD :一年的第几天
          YEAR:英文的年份
          MONTH:英文全称的月份
          mon:英文简写的月份
          ddsp:英文的第几天(一个月的)
          ddspth:英文序列数的第几天(一个月的)
   DAY:全英文的星期
          DY:简写的英文星期
          hh:小时
          mi:分钟
          ss:秒

        例如:
        测试常见的一些日期数据转换为字符串的格式
        select to_char(sysdate,'yyyy MM D DD DDD YEAR MONTH ddsp ddspth DAY DY') from dual;

        select to_char(sysdate,'dd-mm-yy')
        from dual;

        select to_char(sysdate,'yy-mm-dd')
        from dual;

        select to_char(sysdate,'dd-mm-yy HH24:MI:SS AM')
        from dual;


      千年虫:
        在早期的计算机的程序中规定了的年份仅用两位数来表示。也就是说,假如是1971年,在计算机里就会被表示为71,但是到了2000年的时候这个情况就出现了问题,计算机就会将其年份表示为00。这样的话计算机内部对年份的计算就会出现问题。这个事情当时被称为千年虫
    
        数据库中表示日期中年份的有俩种: yy和rr
        之前一直使用的时候yy格式,后来才有的rr格式
        yy表示使用一个俩位数表示当前年份:
        1990 ---yy数据库格式---> 90
        1968 ---yy数据库格式---> 68
        1979 ---yy数据库格式---> 79
        
        rr格式表示:
        如果日期中的年份采用的格式为rr,并且只提供了最后2位年份,那么年份中的前两位数字就由两部分共同确定:提供年份的两位数字(指定年),数据库服务器上当前日期中年份的后2位数字(当年)。确定指定年所在世纪的规则如下:
        规则1 如果指定年在00~49之间,并且当前年份在00~49之间,那么指定年的世纪就与当前年份的世纪相同。因此,指定年的前两位数字就等于当前年份的前两位数字。例如,如果指定年为15,而当前年份为2007,那么指定年就是2015。

        规则2 如果指定年在50~99之间,并且当前年份在00~49之间,那么指定年的世纪就等于当前年份的世纪减去1。因此,指定年的前两位数字等于当前年份的前两位数字减去1。例如,如果指定年为75,而当前年份为2007,那么指定年就是1975。

        规则3 如果指定年在00~49之间,并且当前年份在50~99之间,那么指定年的世纪就等于当前年份的世纪加上1。因此,指定年的前两位数字等于当前年份的前两位数字加上1。例如,如果指定年为15,而当前年份为2075,那么指定年就是2115。

        规则4 如果指定年在50~99之间,并且当前年份在50~99之间,那么指定年的世纪就与当前年份的世纪相同。因此,指定年的前两位数字就等于当前年份的前两位数字。例如,如果指定年为55,而当前年份为2075,那么指定年就是2055。

        注意:rr格式并没有完全的解决俩位数年份保存的问题,思考里面还有哪些问题存在。

        另外参照图片:rr日期格式规则.png



      to_char 把数字转换为字符
        例如:
        select to_char(salary,'$999,999.00')
        from s_emp;

        fm表示去除结果显示中的开始的空格
        select to_char(salary,'fm$999,999.00')
        from s_emp;

        L表示系统本地的货币符号
        select to_char(salary,'fmL999,999.00')
        from s_emp;
        


      to_number 把字符转换为数字
        例如:
        select to_number('1000')
        from dual;
        
        //这个写法是错的 abc不能转换为数字
        select to_number('abc')
        from dual;

        

      to_date 把字符转换为日期
        例如:
        select to_date('10-12-2019','dd-mm-yyyy')
        from dual;

        select to_date('25-5月-95','dd-month-yy')
        from dual;

        select to_date('95/5月/25','yy/month/dd')
        from dual;

        //session语言环境设置为英文下面可以运行
        select to_date('25-MAY-95','dd-MONTH-yy')
        from dual;

        alter session set nls_language=english;
        alter session set nls_language='simplified chinese';


        

    oracle数据库中表示一个日期数据的几种方式
      1.使用sysdate
      2.使用oracle默认的日期格式 例如:'25-MAY-95'
          注意要看当前的语言环境
      3.使用日期函数ADD_MONTHS/NEXT_DAY/LAST_DAY/ROUND/TRUNC
      4.使用转换函数to_date


    函数之间的嵌套
      格式:F3(F2(F1(arg0,arg1),arg2),arg3)
      例如:
      先把'hello'和'world'连接起来,再转换为字母大写然后再从第4个字符开始,连着截取4个字符
      select substr(upper(concat('hello','world')),4,4)
      from dual;



第四章:多表查询

    多表查询,又称表联合查询,即一条sql语句涉及到的表有多张,数据通过特定的连接进行联合显示.


    笛卡尔积
        在数学中,两个集合X和Y的笛卡尓积(Cartesian product),又称直积,表示为X × Y.
        假设集合A={a, b},集合B={0, 1, 2},则两个集合的笛卡尔积为{(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}。
        在数据库中,如果直接查询俩张表,那么其查询结果就会产生笛卡尔积
        例如:
        select *
        from s_emp,s_dept;
        
    
    连接查询
        为了在多表查询中避免笛卡尔积的产生,我们可以使用连接查询来解决这个问题.
        连接查询分为:
            1.等值连接
            2.不等值连接
            3.外连接
                左外连接
                右外连接
                全连接
            4.自连接
    
    等值连接
        利用一张表中某列的值和另一张表中某列的值相等的关系,把俩张表连接起来。
        例如:查询员工的名字、部门编号、部门名字
                                select last_name,dept_id,name
        from s_emp,s_dept
        where s_emp.dept_id=s_dept.id;

        为了表述的更加清楚,可以给每张表起别名
        select se.last_name,se.dept_id,sd.id,sd.name
        from s_emp se,s_dept sd
        where se.dept_id=sd.id;


    不等值连接(不完全等值)
        假设数据库中还有一张工资等级表:salgrade
        工资等级表salgrade:
            gradeName列表示等级名称
            losal     列表示这个级别的最低工资数
            hisal    列表示这个级别的最高工资数

        表中的数据类似于下面内容:
        id  salgrade    losal hisal
        1  初级程序员   2000  4000
        2  中级程序员   4000  6000
        
        例如:
        查询出员工的名字、职位、工资、工资等级名称
        SELECT  e.last_name, e.title, e.salray, s.gradeName
        FROM    s_emp e, salgrade s
        WHERE   e.salray BETWEEN s.losal AND s.hisal


    外连接
        外连接分为:左外连接 右外连接 全连接
        先分别在俩s_emp和s_dept表中插入新的数据
        特点:新员工tom不在任何部门,新增部门st下面没有任何员工
        insert into s_emp(id,last_name) values(26,'tom');
        insert into s_dept(id,name) values(60,'st');
        commit;

        下面条sql语句可以把上面插入的数据给删除掉
        delete from s_emp where id=26;
        delete from s_dept where id=60;
        commit;
        
        这个时候再使用等值连接的话,查询出来的数据就会少,因为新增的员工tom和部门表中的数据连接不上,当然新增的部门st也和员工表中的数据连接不上.那么这俩条数据都是在等值连接中查询不出来.


    左外连接
        例如:
        查询所有员工 以及对应的部门的名字,没有部门的员工也要显示出来
        select last_name,dept_id,name
        from s_emp,s_dept
        where s_emp.dept_id=s_dept.id(+);
        
        或者 俩者是等价的

        select last_name,dept_id,name
        from s_emp left outer join s_dept
        on s_emp.dept_id=s_dept.id;
        
        注意:outer可以省去不写


    
    右外连接
        例如:
        查询所有员工 以及对应的部门的名字,没有任何员工的部门也要显示出来
        select last_name,dept_id,name
        from s_emp,s_dept
        where s_emp.dept_id(+)=s_dept.id;

        select last_name,dept_id,name
        from s_emp right outer join s_dept
        on s_emp.dept_id=s_dept.id;

        注意:outer可以省去不写

    
    全连接
        例如:
        查询所有员工 以及对应的部门的名字,没有任何员工的部门也要显示出来,没有部门的员工也要显示出来
        select last_name,dept_id,name
        from s_emp full outer join s_dept
        on s_emp.dept_id=s_dept.id;

        注意:outer可以省去不写

    
    自连接
        一张表,自己和自己连接
        例如:
        查询每个员工的名字以及员工对应的管理者的名字

select s1.last_name,s2.last_name manager_name
from s_emp s1,s_emp s2
where  s1.manager_id=s2.id;



        select s1.last_name,s2.last_name manager_name
        from s_emp s1,s_emp s2
        where s1.manager_id = s2.id;



    对查询结果集的操作
        如果有俩条sql语句,每一条sql都可以查询出一个结果,这个被称之为结果集。那么我们可以使用下面的关键字对俩个结果集进行操作
        union        取俩个结果集的并集
        union all    把俩个结果集合在一起显示出来    
        minus        第一个结果集除去第二个结果集和它相同的部分
        intersect    求俩个结果集的交集

        注意:前提条件 俩个结果集中查询的列要完全一致

    union  取俩个结果集的并集 (去重)
        例如:
        select last_name,dept_id,name
        from s_emp,s_dept
        where s_emp.dept_id=s_dept.id(+)
        union
        select last_name,dept_id,name
        from s_emp,s_dept
        where s_emp.dept_id(+)=s_dept.id;

    union all  把俩个结果集合在一起显示出来(两个集合的结果全部放进来)    
        例如:
        select last_name,dept_id,name
        from s_emp,s_dept
        where s_emp.dept_id=s_dept.id(+)
        union all
        select last_name,dept_id,name
        from s_emp,s_dept
        where s_emp.dept_id(+)=s_dept.id;

    minus  第一个结果集除去第二个结果集和它相同的部分
        例如:
        select last_name,dept_id,name
        from s_emp,s_dept
        where s_emp.dept_id=s_dept.id(+)
        minus
        select last_name,dept_id,name
        from s_emp,s_dept
        where s_emp.dept_id(+)=s_dept.id;
        只有tom
        对比俩种情况的结果

        select last_name,dept_id,name
        from s_emp,s_dept
        where s_emp.dept_id(+)=s_dept.id
        minus
        select last_name,dept_id,name
        from s_emp,s_dept
        where s_emp.dept_id=s_dept.id(+);
                                只有st



    intersect  求俩个结果集的交集
        select last_name,dept_id,name
        from s_emp,s_dept
        where s_emp.dept_id=s_dept.id(+)
        intersect
        select last_name,dept_id,name
        from s_emp,s_dept
        where s_emp.dept_id(+)=s_dept.id;

    

    oracle中的伪列 rownum
        伪列rownum,就像表中的列一样,但是在表中并不存储。伪列只能查询,不能进行增删改操作。它会根据返回的结果为每一条数据生成一个序列化的数字.rownum是oracle才有的伪列

        rownum 所能作的操作:
        
        rownum 只能等于1  如果让其等于其他数 则查不到数据
        例如:
        select last_name
        from s_emp
        where rownum=1

        rownum 大于0      如果让其大于其他数 则查不到数据
        例如:
        select last_name
        from s_emp
        where rownum>0
        
        rownum 可以小于任何数
        例如:
        select last_name
        from s_emp
        where rownum<7

第五章:组函数
    
    group by 在查询表中数据的时候进行分组的关键字
        思考:为什么要对数据进行分组
    having   分组之后的进行进一步数据筛选的关键字
        having和where的功能类似

    组函数(分组函数),是指将数据按照某列的值进行分组后,然后使用组函数分别对每个分好的小组中的数据进行处理。所以组函数一般要结合着分组关键字group来使用

    组函数:
        avg       求平均值
count     计算有多少条数据    count(*)
        max       最大值
        min       最小值
sum       求和

        stddev    标准差
        variance  方差
    
    
    sql语句的各部分构成
        select   ....
        from     ....
        where     ....
        group by ...
        having     ...
        order by ....
        
        注:除了select和from之外其他的都不是必须的。

        假如select..from..后面的语句都出现了,那么他们的执行顺序为:
        where-->group by分组-->执行组函数-->having筛选->order by


        组函数出现的位置:
            1.select后面
            2.having后面
            3.order by后面
   4.where后面一定【不能】出现组函数

            注意:如果select/having语句后面出现了组函数,那么select/having后面没有被组函数修饰的列,就必须出现在group by 后面


        where和having对比:
           1.where和having都是做条件筛选的
           2.where执行的时间比having要早
   3.where后面【不能出现组函数】
           4.having后面可以出现组函数
           5.where语句要紧跟from后面
           6.having语句要紧跟group by后面

        group by和having的关系:
           1.group by可以单独存在,后面可以不出现having语句
           2.having不能单独存在,有需要的话,必须出现在group by后面

        order by语句
           1.如果sql语句中需要排序,那么就一定要写在sql语句的最后面
           2.order by后也可以出现组函数


    使用组函数:不结合group分组使用
        注:如果不使用group分组的话,那么默认当前查询到的所有数据是一组
        例如:
        查询s_emp表中所有员工的平均工资
        select avg(salary)
        from s_emp;

        查询s_emp表中共有多少条数据
        select count(*)
        from s_emp;

        查询s_emp表中所有员工中的最大工资
        select max(salary)
        from s_emp;

        查询s_emp表中所有员工中的最小工资
        select min(salary)
        from s_emp;

        查询s_emp表中所有员工的工资总和
        select sum(salary)
        from s_emp;

        查询s_emp表中所有员工的工资的标准差
        select stddev(salary)
        from s_emp;

        查询s_emp表中所有员工的工资的方差
        select variance(salary)
        from s_emp;


    使用组函数:结合group分组使用
        例如:
        查询s_emp表中每个部门的平均工资
        select dept_id,avg(salary)
        from s_emp
        group by dept_id;
    
        查询s_emp表中每个部门员工的最高工资
        select dept_id,max(salary)
        from s_emp
        group by dept_id;

        查询s_emp表中每个部门员工的工资总和
        select dept_id,sum(salary)
        from s_emp
        group by dept_id;

        查询s_emp表中每个部门员工的人数
        select dept_id,count(*)
        from s_emp
        group by dept_id;

        
        //分组之后使用各种组函数
        select dept_id,count(*),min(salary),max(salary),sum(salary),avg(salary)
        from s_emp
        group by dept_id;


    使用组函数:结合group分组以及having筛选使用
        例如:
        查询s_emp表中部门的平均工资大于等于1400的部门
        select dept_id,avg(salary)
        from s_emp
        group by dept_id
        having avg(salary)>=1400;
        
        思考:上面的sql语句修改为如下,是否可以?

        select dept_id,avg(salary)
        from s_emp
        where avg(salary)>=1400
        group by dept_id;
  不可以       where后面不能出现组函数
        
        查询s_emp表中部门的总工资大于等于4000的部门
        select dept_id,sum(salary)
        from s_emp
        group by dept_id
        having sum(salary)>=4000;



    使用组函数:其他实例
        例如:
        查询s_emp表中部门的平均工资大于等于1400的部门,并且显示出这些部门的名字,同时按照部门编号进行排序
        第一步:查询出基本需求
        select dept_id,avg(salary)
        from s_emp
        group by dept_id
        having avg(salary)>=1400
        order by dept_id;

        第二步:加入多表查询,并且分别给表起别名
        select se.dept_id,avg(se.salary)
        from s_emp se,s_dept sd
        group by se.dept_id
        having avg(se.salary)>=1400
        order by se.dept_id;

        第三步:查询出s_dept表中的部门名称,并且进行等值连接
        select se.dept_id,avg(se.salary),sd.name
        from s_emp se,s_dept sd
        where se.dept_id = sd.id
        group by se.dept_id
        having avg(se.salary)>=1400;
        order by se.dept_id;

        第四步:select语句后出现了组函数,那么没有被组函数修饰的列放到group by分组后面
    ?    select se.dept_id,avg(se.salary),sd.name
        from s_emp se,s_dept sd
        where se.dept_id = sd.id
        group by se.dept_id,sd.name
        having avg(se.salary)>=1400
        order by se.dept_id;
        
        思考:是否可以把where后的条件筛选 转移 到having语句后面?
       不可以 等值连接不是group by 表达式                          


方法二:
select  id,name
from s_dept
where id in ( select dept_id
from s_emp
group by dept_id
having avg(salary)>=1400)
order by id;


        查询s_emp表中最大的工资数,并且显示出这个最大工资的员工的名字
        第一步:查出最大工资数
        select max(salary)
        from s_emp;
        
        第二步:加上last_name的显示
        select last_name,max(salary)
        from s_emp;

        第三步:select后面出现组函数,没有被组函数修饰的列放到group by后面,但是发现查询结果并不是想要结果
        select last_name,max(salary)
        from s_emp
        group by last_name;
        
        第四步:修改为多表查询(起别名),从s1表中查询出最大的工资数是多少,然后再和s2表连接起来,选出s2表中这个最大工资数的员工名字
        select s2.last_name,max(s1.salary)
        from s_emp s1,s_emp s2
        where s2.salary = max(s1.salary)
        group by s2.last_name;
        
        第五步:where后面不能出现组函数,所以改为having
        select s2.last_name,max(s1.salary)
        from s_emp s1,s_emp s2
    ?    group by s2.last_name,s2.salary
        having s2.salary = max(s1.salary);

        
        练习:
        查询s_emp表每个部门的最大工资数,并且显示出这个最大工资的员工名字以及该部门的名字和该部门所属区域,并且使用部门编号进行排序
        
        


第六章:子查询(嵌套查询)
    子查询,即一个select语句中嵌套了另外的一个或者多个select语句

    例如:
    查询工资比Simth工资高的员工信息
        第一步:查询Smith的工资数
        select salary
        from s_emp
        where last_name='Smith';    
        结果:
            SALARY
        ----------
               940

        第二步:查询工资比940高的员工信息
        select last_name,salary
        from s_emp
        where salary>940;

        第三步:把第二步中的数字940替换成第一步中的sql语句即可(注意格式)
        select last_name,salary
        from s_emp
        where salary>(
            select salary
            from s_emp
            where last_name='Smith'
        );

    
    例如:
    查询所有部门的平均工资
    select dept_id,avg(salary)
    from s_emp
    group by dept_id;
    
    结果:
       DEPT_ID AVG(SALARY)
    ---------- -----------
            42  1081.66667
            43         900
            34        1160
            44        1050
            31        1400
            32        1490
            35        1450
            50        2025
            41      1247.5
            45        1089
            33        1515

       DEPT_ID AVG(SALARY)
    ---------- -----------
            10        1450


    查询平均工资比1247.5高的部门编号
    select dept_id
    from s_emp
    group by dept_id
    having avg(salary)>1247.5;
    
    结果:
       DEPT_ID
    ----------
            31
            32
            35
            50
            33
            10


    查询平均工资比1247.5高的部门中员工信息
    select last_name,salary,dept_id
    from s_emp
    where dept_id in(10,31,32,33,35,50);

    等价于

    select last_name,salary,dept_id
    from s_emp
    where dept_id in(
        select dept_id
        from s_emp
        group by dept_id
        having avg(salary)>1247.5
    );



    查询平均工资比 41号部门的平均工资 高 的部门中员工的信息
    select avg(salary)
    from s_emp
    where dept_id=41;
    
    结果:
    AVG(SALARY)
    -----------
         1247.5

    所以这个sql语句和1247.5是等价的,那么就可以将上面sql语句中的1247.5给替换了
    select last_name,salary,dept_id
    from s_emp
    where dept_id in(
        select dept_id
        from s_emp
        group by dept_id
        having avg(salary)>1247.5
    );
    替换后为:
    select last_name,salary,dept_id
    from s_emp
    where dept_id in(
        select dept_id
        from s_emp
        group by dept_id
        having avg(salary)>(
            select avg(salary)
            from s_emp
            where dept_id=41
        )
    );

    
    练习:
    注意:(可以把子查询的结果作为一张表来使用)
    查询平均工资比 41号部门的平均工资 高的部门中员工的信息,并且显示出当前部门的平均工资
    
    查询平均工资比 41号部门的平均工资 高的部门中员工的信息,并且显示出当前部门的平均工资,同时显示出部门的名字
    


    查询员工信息,这些员工的工资要比自己所在部门的平均工资高

    查询员工信息,这些员工的工资要比自己所在部门的平均工资高,同时显示部门的名称以及所在地区



    查询工资比 Ngao所在部门平均工资 要高的员工信息,同时这个员工所在部门的平均工资 也要 比Ngao所在部门的平均工资要高

    查询工资比 Ngao所在部门平均工资 要高的员工信息,同时这个员工所在部门的平均工资 也要 比Ngao所在部门的平均工资要高,显示当前部门的平均工资

    查询工资比 Ngao所在部门平均工资 要高的员工信息,同时这个员工所在部门的平均工资 也要 比Ngao所在部门的平均工资要高,显示当前部门的平均工资以及部门的名字和所在地区




O

原文地址:https://www.cnblogs.com/yxj808/p/12022532.html