11月25日——培训第5天

今天早上刘羽离开了,由于费用和课程设置方面的问题,他觉得还是先就业较好。现在我们是5个人了。屋子里的漏风现象
还是很严重……越来越冷,隔壁的308号房间很快就会空出来,我们5个人可以搬到那里,一个月共880,每个人180左右,
和以前的6人间差不多是一个价,但是相对起来要暖和的多。今天早上等车等了很长时间,大概15分钟吧,而且车还很挤,
天有点下雪,实在是让人不太舒服……

虽然比起昨天晚到了15分钟,但我们还是最早的,看来他们那些住的近的起的相当的晚啊……

今天迟到的人挺多的,现在还有几个人没有到呢……

---------------------------------------------------------

上午课程开始,首先讲解昨天的作业题目:

作业题目:
3、在employees表中查询first_name与last_name组成的完整姓名。显示姓名的总长度为15,如果姓名长度小于15,补'.'
   以表示姓名被完整显示,如果姓名长度大于15,则从第12位开始截取并补'*'以表明姓名被截取。

select salary,
case
     when length(first_name || ' ' || last_name) <= 15 then rpad(first_name || ' ' || last_name,15,'.')
     when length(first_name || ' ' || last_name) > 15 then substr(first_name || ' ' || last_name,1,12) || '***'
end as result
from employees ;


作业题目4:

先执行以下SQL语句,创建一个employees表的备份:

create table emp_copy as
  select *
  from employees
  where manager_id=100 or
        manager_id in (select employee_id from employees where manager_id=100);
新表是什么含义?

我的回答:select employee_id from employees where manager_id=100:从employees表中选取出id号为100的雇主手下的雇员号
所以新表说白了就是要从原始的employees表中选取id号为100的雇主以及100号雇主手下所有雇员的手下全部雇员的全部信息。

老师的答案:创建一个表emp_copy把100号经理的第一层下属信息以及第一层下属的直接下属信息取出,其实取出的是100号经理往下的第一层下属和第二层下属的所有信息,其实是不包括100号经理本身的。

注意:其实后边题目的要求是建立在这个已经建立好的emp_copy表上的,而不是要你使用employees表,是我搞错了。

------------------
现在要求以如下方式显示所有员工:
如果经理为100的员工有101、102、103,而101的直接下属为104、105, 102直接下属为106、107, 103直接下属为108,则显示为
101
--104
--105
102
--106
--107
103
--108
那么这条SQL语句应该怎么写?(注意,不能使用connect by)


我的做法:
select
   case
    when manager_id=100 then to_char(employee_id)
    when manager_id in(select employee_id from employees where manager_id=100)
         then concat('--',to_char(employee_id))
   end as result
from employees;

注意我的这种写法是有问题的,如果我这么写的话,会出现101、102和103挨着显示的情况,和题目要求是不符合的,而且我是用的employees表,这是不应该的,应该使用前边已经创建出来的emp_copy表才对。


老师的答案:

注意创建的备份表emp_copy中只有两类人,一种是雇主,一种是雇员,雇主的经理是100号经理。

所以只有两种情况:一是manager_id是100的情况,这种情况下在emp_copy表中是雇主,另外一种其他的情况就是雇员了。

这可以用case end表达式实现。

最难办的是最后的显示问题:如果按照manager_id排序的话,那么101、102和
103肯定排在最前面,因为他们的manager_id都是100,他们三个会扎堆,这是
和题目要求不符的。

所以呢,要把manager_id和employee_id连接起来,
这里order by 后面要用case语句,也就是说,如果是雇主的话,
那么按照employee_id排序;

如果是雇员的话,按照manager_id||'--'||employee_id排序。

正确的源代码:

当然前提是emp_copy表是已经创建好了的,调用上面的创建表代码即可。

第一步:
select manager_id, case manager_id
       when 100 then to_char(employee_id)
       else '--'||employee_id
       end as result
       from emp_copy;

第二步:
select case manager_id
       when 100 then to_char(employee_id)
       else '--'||employee_id
       end as result
from emp_copy
order by
      case manager_id
         when 100 then to_char(employee_id)
         else manager_id||employee_id
      end;

如果是雇主,则按照自己的号来排序
如果是雇员,则按照自己雇主号加上自己的号来排序

注意:在select和order by的case中,都必须得有to_char函数,因为在else中
有一个字符串连接,得到的是字符串,case分支的类型要保证一致,这是很
重要的。

 

5、对上题进行改进,将员工的manager_id为100的员工都置为空(应该怎么做?),那么上面的语句应该怎么写?

update emp_copy set manager_id=null where manager_id=100;
就是一条简单的更新语句就可以了,不需要什么函数,没有必要想的太复杂……

但是如果必须用nvl、nvl2、nullif、coalesce四个和空值有关的函数来实现
上面的功能的话,又该如何去做呢??

--------------------------
现在是课间休息时间,老师让我们考虑一下上面的问题,刚才方老师还向我询问
了一下课程强度的问题,看来老师这边很关心我们掌握的情况,因为毕竟一期
班前期掌握的情况不是太好,多多少少有些影响了他们现在的学习状态,他们
现在还没有上项目就是因为前面基本的东西有些欠练习,所以希望我们这一次
不至于重蹈覆辙吧……可以感觉到老师是全程监督,全程负责的,这一点让人很
是放心,和别的地方毕竟是不一样啊,当然这样也是正常的,毕竟这里培训的
性质和一些正规的培训机构还是不太一样,因为没有形成规模,所以暂时还是可
以做到全程监督、全程负责,如果以后扩大了招生规模,一旦学生数量过大,
那么教师很有可能管不过来,放羊情况在所难免,俗话说,事物在它的新生阶段
生命力都是很旺盛的,这个培训也是一样,我们现在是二期班,也是处于这个
培训的新兴阶段,老师的劲头丝毫不亚于一期,再加上又有了一期班办学的一些
失败的经验,所以我们的优势相对起来也明显一些,还是物有所值的,至于这个
培训的未来嘛,呵呵,就交给后来人去关注吧,我所能作的也就是详细真实的
记载我在这一期班的真实学习情况,以供后来人参考……
------------------------------

现在思考了一下方老师的问题,是不是可以使用nullif函数呢?毕竟它是这
四个函数里面唯一一个会主动返回null值的函数,只要设置里面的两个表达式
都是一个就可以了,这很容易做到。

结果老师说肯定要使用的函数竟然是nvl,汗啊……自己又错了,看来是理解的问题,
还得再想想……

nvl的使用很简单,就是判断第一个参数是否为空,如果为空的话,那么肯定用后面
括号里提供的表达式来代替显示,否则用前面的表达式,说白了就是这个意思。
-------------------
应该是把下面的代码作一下更改吧,我试一下……
select case manager_id
       when 100 then to_char(employee_id)
       else '--'||employee_id
       end as result
from emp_copy
order by
      case manager_id
         when 100 then to_char(employee_id)
         else manager_id||employee_id
      end;

更改后的代码:

select case manager_id
       when 100 then to_char(nullif(100,100))
       else '--'||employee_id
       end as result
from emp_copy
order by
      case manager_id
         when 100 then to_char(employee_id)
         else manager_id||employee_id
      end;

自己还是搞错了,刚才问了一下老师,原来是必须先执行
update emp_copy set manager_id=null where manager_id=100;
这条语句后再更改这道题目,这样一来emp_copy表中的雇主的
manager_id必然被改为空值了。
那么就无法单纯的使用case了……
----------------------

老师说应该用到的函数是nvl2,这个函数里面三个参数,本质和nvl是一样的。
就这道题目来说,第一个参数肯定是填成manager_id,因为雇主的manager_id
现在是空值,而雇员的manager_id现在仍然有值,正好可以使用nvl2函数来达到
case的效果。

select case
       when nvl2(manager_id,'130','300') = '300' then to_char(employee_id)
       else '--'||employee_id
       end as result
from emp_copy
order by
      case manager_id
         when 100 then to_char(employee_id)
         else manager_id||employee_id
      end;

其实我这种做法可能还是麻烦了,一会儿听听老师怎么说的,因为我基本上还是保留了
case语句段,只不过条件发生了一点变化,条件我用的是nvl2函数,为什么不用nvl函数呢?
是因为nvl2函数不论第一个表达式是否为空,它都有准确的非空表达式在那里,方便我们的
调用,虽然nvl也是有的,但是没有nvl2函数清晰。

所以呢,我用manager_id作为第一个参数,这个参数如果为空的话,返回的肯定是'300'
这时肯定对应的是雇主而不是雇员,因为雇主的manager_id原来是100的,现在被上面的
update emp_copy set manager_id=null where manager_id=100;语句置为空值了,这样
nvl2(manager_id,'130','300')返回'300'时就肯定是雇主的情况,否则就是雇员了

所以按照这种思路更改上面的case语句,就得到结果了,其实也就是一种变通吧,但是没能甩
开case语句……唉,就这么一道破题,整这么长时间,都不知道自己脑子里面咋想的,太面了……
有的人作的很快,我说什么来着,就是要平常心,他们是他们的事情,不要太在意别人准没错,
一会儿听听老师怎么讲这道题目……

------------------------------------------------------------------------
刘栋是最先做完的,看看他是怎么弄的:

select nvl2(manager_id,'--',null)||employee_id as result
from emp_copy order by nvl2(manager_id,manager_id||employee_id,employee_id);
或:
select nvl2(manager_id,'--',null)||employee_id as result
from emp_copy order by nvl(manager_id,employee_id)||employee_id;

唉……非常的巧妙啊,完全甩开了case语句,高明……用两个nvl2分别巧妙的代替了
两个case

作业讲解结束。
---------------------------------------------------


空值处理函数:
nvl函数和nvl2函数,对括号中的第一个参数进行判断,看是否为空,
具体参见手册。
nullif(expression1,expression2):判断两个表达式是否相等,相等就返回空值
                                否则返回第一个表达式。
coalesce(expression1,expression2,……):从众多表达式中返回第一个不为
                                 空的值。

--------------------------------------------------------------------------------------------------------

第三章的讲解结束,开始第四章多表查询讲解:

内连接,外连接,左右连接,全连接,自然连接等等

注意:select * from tab;
这句话返回库里面的表的简明情况
如果是select * from tabs;那么返回的就是详细的很多的信息,可以
自己试验一下。

departments表中的department_id是主键,主键会建立索引,所以查找主键
效率更高,比查别的键快的多。

数据表之间的关系:
一对一、一对多、多对一、多对多这四种关系中以多对多最为复杂

学生和课程的关系,老师和学生的关系,如何体现多对多的关系呢?

多对多只能通过关系表的方式实现,不可能通过外键或其他的方式来实现!

多表查询在数据库处理中是相当的麻烦的,不但要删除表,还要删除关系表中
的关系

oracle的hr用户可以访问的数据库中,员工表和部门表是多对一的关系,
所以员工表中有一个外键指向部门表。

Cartesian:数学家笛卡尔。

两个表没有任何条件的连接就是笛卡儿乘积的情况,一个表是m行,另外一个
表是n行,结果就是m*n了,因为没有任何条件的去连接,每一行都可以去和另
一个表的每一行记录去匹配的。

以scott用户进入,密码是tiger
进入后输入select * from tab;
可以得到四个表,他是hr库的简略版本。

select dept.deptno,empno from emp,dept;这就是无连接条件的笛卡儿连接。

1 忽略了连接条件
2 连接条件不可用,或者连接条件没有起到限制的作用
所以呢,为了避免笛卡儿乘积的出现,必须设置合理的连接条件,保证这个连接
条件一定确实的起作用!!

oracle中连接分两种:一种是oracle的连接oracle proprietary joins
二是sql99的连接sql1999 compliant joins

oracle连接:等值连接,不等值连接等

sql99连接:全连接,左连接,右连接,自由连接等

等值连接例子:
select employees.first_name,departments.department_name from employees,
 departments where employees.department_id = departments.department_id;

只要两个连接的表里面不发生字段重名的情况,则可以省略前面的"表名.",但是
这样以来效率会降低,oracle要浪费时间定位字段隶属于哪个表

select e.first_name,d.department_name from employees e,
departments d where e.department_id = d.department_id;

这种写法给表取了别名,但是注意表名和别名之间是绝对不能加as的!!!!
但是字段名和别名之间是完全可以加as的,也可以省略!

上午课程结束。
--------------------------------
中午回来时第一次破天荒的机房没有开门,我们所有人全被撂在了外面,机房中的
东西很重要,所以中午锁门是必须的,正所谓有了第一回就有第二回,以后中午
也少不了要等……

中午张老师和田老师在前面短暂的聊了聊,大概是关于什么培训的人卷了学费跑了的事情,
具体的听不明白,毕竟是人家的业务嘛,不过真赶上这种骗子还真是可恶呢,多冤啊……
中国骗子就是多……真TMD!

房子好像找到了一处,距离我们住的那个地方有2公里左右,17平米,具体怎么样什么的还
得亲眼看一看。
---------------------------------

下午课程开始:

中午刘栋上网查到了一个isplplus的命令
进入命令行sqlplus后,输入spool off,回车后再输入spool c:/sql2.txt
回车后那么当前会话中以后所输入的所有命令和显示的结果都会保存到那个
指定sql2.txt文本文件中……

有一个小细节值得注意,那就是字段或是表的别名不要用数据库或表中已有
的字段,否则会有错误……当然这和命名规范有关系,要养成好习惯啊。

课程正式开始:

田老师再次申明了班里各人情况都不一样,所以难免有人认为课程进度快了,
有人认为课程进度慢了,田老师希望基础好的人能够多理解一下现在的课程
设置,不要一心的只是想理解框架的那些东西。oracle的这篇578张讲义不是
都讲的,这里只是讲一周时间,下周oracle差不多就完事了,再用一周讲jdbc,
第三周只用两天的时间讲hibernate,因为两周后的课程只能够讲2天的hibernate
,hibernate第三周只是开个头而已,让我们体会一下hibernate的好处。
后面回过头来讲hibernate,然后就是讲web,在这个过程中还是要回头讲
hibernate的内容,希望我们不要着急,沉住气。

继续等值连接:

如果用hr数据库查询每一员工的具体地址,要从departments表中找到
location_id来定位location表,再用desc location观察表中的字段,
可以看到有个country_id,说明可以由此指定到country表,country表中
又有一个region_id,里面有个regions表

这样一来就涉及到了4个表:
select department_name,region_name||' '||country_name||' '||state_province||' '||city||' '||street_address as address_a
 from departments,locations,countries,regions
 where departments.location_id = locations.location_id
      and locations.country_id=countries.country_id
      and countries.region_id = regions.region_id ;
上述代码很清晰,结构也很简单,但是注意,字段名不要写错……

 

--------------------------
下面是非等值连接
使用scott/tiger来进入oracle数据库。里面有一个salgrade表,
使用desc emp来看表结构,里面的sal代表工资,而且salgrade表中
有losal和hisal代表低工资和高工资,grade代表级别。

在这里面每一条记录的losal和hisal都唯一的确定了一个工资的级别。

select ename,sal,grade from emp,salgrade where sal between
     lowsal and hisal ;
这就查出了员工的工资级别,其实本质和等值连接一样,就是判断方面
一个是需要等号,一个是需要不等号。

一般来说,使用了一个用户登陆oracle后,先使用select * from tab;命令
来看看里面都有什么数据表,然后再用desc "数据表名";可以得到具体表的字段
信息,然后再用select * from "数据表名"得到想要的数据表的全部信息。

注意这里面有个情况,就是工资级别里面是从700元开始往上计算的,700元也是
工资级别表里面的底线,如果员工工资小于700元的话,比如执行
insert into emp(empno,sal) values(9999,400);插入语句插入一条工资为400
元的小于700元底线,那么执行
select ename,sal,grade from emp,salgrade where sal between
     lowsal and hisal ;

select ename,sal,grade from emp,salgrade where sal between
     lowsal(+) and hisal(+) ;这就是左连接。

后,400元的员工信息绝对是不会显示的,因为它不满足连接条件

定义:只选取显示满足连接条件的连接叫做内连接inner join。

刚刚那道题目如果既要显示工资级别信息,又要把那个连700元工资底线都
没有到的人的情况显示出来,这时候就肯定要用外连接outer join。

-------------------------
左外连接:把左边表中不满足连接条件的记录显示出来的情况。
右外连接:把右边表中不满足连接条件的记录显示出来的情况。
对于左、右外连接来说,不满足条件的记录对应另一个表中的字段值必然为空值

全连接则是把左、右连接合在一起。

select table1.column,table2.column
from table1,table2
where table1.column(+) = table2.column; 注意这是右外连接,不是左外连接

select first_name,department_name from employees,departments where
      employees.department_id=departments.department_id(+);
这就是左连接了,这样可以显示出暂时没有部门的员工。

select first_name,department_name from employees,departments where
      employees.department_id(+)=departments.department_id;
这就是右连接了,可以显示出没有员工的部门。

注意:如果左、右外连接有多个连接条件的话,那么需要在每个连接条件的
      同样的方向加上'(+)'才行

     比如employees.department_id(+)=departments.department_id and
         employees.employees_id(+) = departments.employees_id

注意oracle里面是没有全连接的,所以让两边都显示出来的话必须用sql99的语法。

注意现在讲的是oracle自己特有的连接语法。oracle特有的就是这个"(+)",这个
东西性能比传统的sql99好,速度快,但是只针对于oracle,其他数据库都没有
这个性能。

所谓左外连接、右外连接就是哪边多显示空行的问题,左边多显示空行就是
右外连接,右边多显示空行就是左外连接了……

-------------------

自连接self_join,比如查询所有员工的经理,employees表中有employee_id
和manager_id,其中的manager_id对应的就是该表中的employee_id,查询的时候
就是从自己的一个字段引到自己的主键身上,这就是自连接查询了。

select worker.last_name||' works for '|| manager.last_name
from employees worker , employees manager
where worker.manager_id = manager.employee_id;

这种做法由于涉及到两张相同的表,所以必须为其命两个不同的别名,这个很
重要,必须注意。

查询谁为谁工作的信息:

其实就是你自己人为的把它想象成为两张表,这样比较好理解一些,当然我们
还有一种做法那就是子查询(后面介绍)。

记得很久以前听过这么一句话,所有的连接都是可以用子查询来取代的,但是并
不是所有的子查询都可以被连接取代……

oracle语法的连接就到此结束了,下面是sql99的连接语法讲解……
-----------------------------------
下面是课间休息时间……

班里有些人确实是急功近利一些,好像有个人是弃学过来的,所以非常期望赶快
学一些有用的东西,所以对现在先讲oracle数据库sql语句的做法提出了质疑,
我们中国人好像都是这样,很多人很浮躁,就是追那些流行的框架技术,其实
那些又算的了什么呢?那些东西学的很好了也就是小公司的一些产品开发,即便
能到大公司发展也很受限制。谁都知道这一行要有很好的发展真正重要的是什么
,但是每个人想法还是都不一样的,有的人会选择这一行干一段时间就转行,认
为从年龄或是体力方面无法长久胜任吧……这也是难怪的。作逻辑层还是有点
意思的,你自己封装接口让别人去开发页面层,对日外包是很郁闷的事情,完全
了解不到底层的东西,作对日外包其实说白了就是作表述层,没有什么值得作的
,也束缚自己的发展,有的大公司不用hibernate,自己开发一套框架使用的情况
也是有的。其实我们中国的软件it技术本来作的就是最底层的急功近利的管理
系统开发,就是想着赚钱嘛,这在csdn上也被人讨论烂了,像真正技术上的操作
系统啊,软件开源拉,有人都感叹:在中国怎么就这么难!有什么办法呢?谁
叫我们现在在转型期……希望我工作以后一段时间情况能够好一些吧,也许是
奢望……

现在讲的课程以前基本接触过,所以现在还有闲心在课间写这种东西,等到一周
后开始讲hibernate的时候估计自己就傻眼了吧……谁知道呢
----------------------------------

休息结束,开始sql99语法的连接方式:

交叉连接
select * from emp cross join dept;
这句话根本无需加连接条件,因为不论加什么条件出来的结果肯定是笛卡儿乘积
,这是交叉连接,出来的结果是笛卡儿乘积,几乎不在应用中使用!

自然连接:
select ename,dname from emp natural join dept;
这是没有连接条件的自然连接,自然连接是基于所有的同名的字段,并且值也是
相同的,才进行连接

其实等价于:
select ename,dname from emp,dept where emp.deptno=dept.deptno;

如果两个表中有两个或是多个列有同名现象,那么他们都会作为连接条件去
连接,所以只要是natural join的话,根本不需要连接条件。

所以自然连接和交叉连接都不要写连接条件!但是如果一定要写连接条件的话,
二者都可以加where,但是绝对都不可以加on这个连接条件!

select ename,dname from emp join dept on emp.deptno=dept.deptno where
sal>=3000 ; (注意这个语句是内连接!当然也是可以在join的左边加上inner
,当然如果不加的话默认也是内连接)

sql99语法中,连接条件要写在on里面不要写在where里面。


外连接:
select ename,dname from emp left outer join dept on
emp.deptno=dept.deptno where sal>=3000 ;

这里就是左外连接,dept会多出来空行。
同理right outer join就是右外连接了!

……全连接:
select first_name,depart_name from employees full outer join
   departments on employees.department_id=departments.department_id;
这就会显示出左外连接和右外连接一块的结果。就是包含了左外连接和右外
连接的所有情况。

using的用法

select e.employee_id,e.last_name,d.location_id
from employees e join departments d
using(department_id)


上句没有指明连接条件,但是因为两个表中department_id是相同的,也就是
暗中指明了这两个同名字段如果对应值相同的话为连接条件……

在自然连接中也可以这么来作,假如自然连接的两个表有两个或多个同名的字段
,如果什么都不加的话默认是按照这些同名列对应记录字段相等的内连接条件
来连接,如果最后加入using的话就是按照后面指定的具体一个字段等值条件来
连接了,而不是按照多个字段来连接了。

oracle是数据库中最难的,如果学好了的话会比学好java的三个框架都要有用,
------------------------------------------
下面是自由练习时间,主要是关于多表联查的题目

下面的练习的前提都是要以hr的身份进入数据库!

1、查询每一部门(departments)的具体地址址,并以如下方式显示:
Human Resources             Europe United Kingdom  London 8204 Arthur St
Shipping                    Americas United States of America California South San Francisco 2011 Interiors Blvd

如果用oracle的连接方式实现:

select department_name,region_name||' '||country_name||' '||state_province||' '||
                       city||' '||street_address as address_a
        from departments,locations,countries,regions
        where departments.location_id = locations.location_id
            and locations.country_id=countries.country_id
            and countries.region_id = regions.region_id ;

如果用sql99语法的连接方式实现:

select department_name,region_name||' '||country_name||' '||state_province||' '||
                       city||' '||street_address as address_a
         from departments join locations on departments.location_id = locations.location_id
                          join countries on locations.country_id=countries.country_id
     join regions on countries.region_id = regions.region_id ;

select department_name
         from departments join locations on departments.location_id = locations.location_id
                          join countries on locations.country_id=countries.country_id
     join regions on countries.region_id = regions.region_id ;


2、使用sql99查询employees表中,员工所在部门位于美国的所有员工。

select * from tab; 结果如下:

TNAME                          TABTYPE  CLUSTERID
------------------------------ ------- ----------
COUNTRIES                      TABLE
DEPARTMENTS                    TABLE
EMPLOYEES                      TABLE
EMP_COPY                       TABLE
EMP_DETAILS_VIEW               VIEW
JOBS                           TABLE
JOB_HISTORY                    TABLE
LOCATIONS                      TABLE
REGIONS                        TABLE


涉及的表顺序如下:employees>>departments>>locations>>countries

我的答案是:
select first_name
from employees join departments on
employees.department_id=departments.department_id
    join locations on departments.location_id=locations.location_id
    join countries on locations.country_id=countries.country_id  
where country_name='United States of America';


老师答案是:

select first_name,region_name from employees
   join departments on employees.department_id=departments.department_id
   join locations using(location_id)
   join countries using(country_id)
   join regions using(region_id)
where country_name='Americas';

注意要谨慎的使用自然连接,保证两个表中的同名字段确实是一个,否则的话要用using语句加以限制!!!
      
----------------------------
连接告一段落,下面是组函数的介绍:
avg、count、max、min、stddev、sum、variance等等
stddev:标准方差
variance:平均方差

下午课程结束……
------------------------------------------------------------------------------------------
晚上了,先看看老师的作业吧:

6、7题都涉及到分组的问题……

6、公司程序员一直在抱怨收入还不如销售人员,做为财务人员,经理要求你提供程序员与财务人员的平均工资,
   这条SQL语句应该怎么写?(从employees表中查询,job_id为IT_PROG的是程序员,SA_REP的是销售员。)

select avg(salary) from employees where job_id='IT_PROG'
union select avg(salary) from employees where job_id='SA_REP';

上面的语句虽然可以实现功能,但是有个问题,我如何在平均工资数字的左边跟着职位名称呢??汗……

select avg(salary), job_id from employees group by(job_id);
上面这句话按照具体职位分组并且显示每个职位人员的平均工资和对应的职位。

7、使用一条SQL语句查询出部门平均工资最高值与部门平均工资最低值之差。

select max(avg(salary)) from employees group by(job_id);  这条语句可以查出具体职位平均工资最高值
select min(avg(salary)) from employees group by(job_id);  这条语句可以查出具体职位平均工资最低值

select ((select max(avg(salary)) from employees group by(job_id))-(select min(avg(salary)) from employees group by(job_id)))
as result from dual;
上面这句话可以查出具体职位平均工资最高的工资值和具体职位平均工资最低的工资值的差值。

select max(avg(salary)) from employees join departments using(department_id) group by(department_name) ;
上面这句话查出各个部门里面员工平均工资最高的那个部门的工资值。

于是根据上面那个工资差值的思路,一样的做法,先把上面那句话整体用括号括起来,然后再将用min计算出来的同样的上面的语句
用括号括起来,然后二者作减法,使用虚表dual显示即可了,如下所示:

select
(select max(avg(salary)) from employees join departments using(department_id) group by(department_name))-
(select min(avg(salary)) from employees join departments using(department_id) group by(department_name))
as result
from dual;

虽然上面的方法能够弄出来,但是你不觉得太别扭了么?对,太别扭了,所以建议你还是按照下面的方法写
比较好:

select max(avg(salary))-min(avg(salary)) as result from employees
join departments using(department_id) group by(department_name);

第7题问题还是比较多的,比如这样显示出来只是单独的一个数字,我要想显示这个数字隶属的部门的话,绝对不可以简单的
在后面添加字段,这是绝对不允许的!那怎么办呢???……


8、分别使用联接和子查询,查询平均工资最多的部门名称。

如果是使用连接的方法,那么自己已经在7题里面实现出来了:
select max(avg(salary)) from employees join departments using(department_id) group by(department_name) ;
首先一定要将employees表和departments表连接起来,这样才能把工资和部门挂上钩,连接的时候为了省事使用了
using语句指明两个表相同的列department_id对应的字段要相等才行,是内联查询,然后以部门名来分组取出
每组的平均工资,然后用max函数,可以返回最大的那个部门的平均工资

可是现在的问题就如同第7题最后提出的问题一样,我肯定想知道这个最大的平均工资隶属于哪个部门,
要不然的话我算出它来还有什么意义呢?但是又无法和max函数写在一起放在select关键字之后,否则
会报错……

看一下下面这条语句:

select avg(salary) as sal,department_name from employees join departments using(department_id) group by(department_name) ;
这条语句其实求出的是一个各部门平均工资的情况表(有两个字段,部门名称和平均工资),
其实就是在这个表中查询满足最大值条件的部门名称。

由于最大值工资已经求出:
select max(avg(salary)) as result from employees
join departments using(department_id) group by(department_name);

那么其实如果用拼凑sql语句的方式就完全可以这么写了

思路:select 部门名称 from 各部门平均工资情况对照表 where 工资=最大值工资;

其中各部门平均工资对照表和最大值工资分别对应上面两条sql语句,所以带入就行了,
但要注意'部门名称'和'工资'的取值要和各部门平均工资情况对照表里面的字段保持一致才行。

加入两段代码后答案如下所示:

select * from
(select avg(salary) as sal,department_name from employees
join departments using(department_id) group by(department_name))
where sal=(select max(avg(salary)) as result from employees
join departments using(department_id) group by(department_name));

倒是可以得出正确的答案,可是这就又成了拼凑sql语句了……有没有别的更好的办法呢?

由7、8题可以得知,拼凑sql语句确实可以作为解决复杂问题的一种途径,它避免了复杂的逻辑思维,
虽然复杂化了语句,但是简化了逻辑,可以在不得已而为止,但是实在是不提倡……


今天就暂时先告一段落,明天等老师讲题了,唉……sql这里还挺麻烦的呢
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值