学习资料

学习的笔记


8子查询

<h4>认识子查询</h4> <pre><code>-- 子查询可以出现在各个位置 SELECT * FROM emp WHERE sal=(SELECT MIN(sal) FROM emp);--工资最小的员工的信息</code></pre> <h4>在where子句中使用子查询</h4> <pre><code>--子查询单选单列 SELECT * FROM emp WHERE sal &lt;(SELECT sal FROM emp WHERE ename='ALLEN'); SELECT * FROM emp WHERE sal &lt;(SELECT AVG(sal) FROM emp); SELECT * FROM emp WHERE job=(SELECT job FROM emp WHERE ename='ALLEN') AND sal&gt;(SELECT sal FROM emp WHERE empno='7521') --子查询返回单选多列数据,这个知识点是之前没遇到过的,顺序不能乱 SELECT * FROM emp WHERE (job,sal)=(SELECT job,sal FROM emp WHERE ename='SCOTT') AND ename&lt;&gt;'SCOTT'; SELECT * FROM emp WHERE (job,mgr)=(SELECT job,mgr FROM emp WHERE empno='7566') AND empno&lt;&gt;'7566'; SELECT * FROM emp WHERE (job,to_char(hiredate,'yyyy'))=(SELECT job,to_char(hiredate,'yyyy')FROM emp WHERE ename='ALLEN'); --子查询返回单行多列数据:多行子查询操作符(IN、ANY、ALL) SELECT * FROM emp WHERE sal IN (SELECT MIN(sal) FROM emp GROUP BY deptno); SELECT * FROM emp WHERE sal NOT IN (SELECT MIN(sal) from emp GROUP BY deptno); SELECT * FROM emp WHERE empno IN (SELECT mgr FROM emp);--书里说in里有空值将不返回任何值,但是这里返回了。</code></pre> <h4>ANY操作符,旧版本的SQL中有ANY了,后来的版本引入了SOME,功能类似</h4> <ul> <li>=ANY:功能类似in</li> <li>>ANY:比集合中最小的结果还要大</li> <li>&lt;ANY:比集合中最大的结果还要小 <pre><code>--随便测试的 SELECT * FROM emp WHERE sal=ANY(select MIN(sal) FROM emp where job='MANAGER' GROUP BY deptno); SELECT * FROM emp WHERE sal&gt;ANY(select MIN(sal) FROM emp where job='MANAGER' GROUP BY deptno); SELECT * FROM emp WHERE sal&lt;ANY(select MIN(sal) FROM emp where job='MANAGER' GROUP BY deptno);</code></pre> <h4>ALL操作符:类似ANY的用法,意思有点相反</h4></li> <li> <>ALL:等价于NOT IN </li> <li>>ALL:比最大值还要大</li> <li>&lt;ALL:比最小值还要小 <pre><code>SELECT * FROM emp WHERE sal&lt;&gt;ALL(select MIN(sal) FROM emp where job='MANAGER' GROUP BY deptno); SELECT * FROM emp WHERE sal&gt;ALL(select MIN(sal) FROM emp where job='MANAGER' GROUP BY deptno); SELECT * FROM emp WHERE sal&lt;ALL(select MIN(sal) FROM emp where job='MANAGER' GROUP BY deptno);</code></pre></li> </ul> <h3>空数据判断,</h3> <pre><code>- EXISTS 、NOT EXISTS,对子查询中是否有数据返回做出判断,有为true,无为false,NOT EXISTS反之。 SELECT * FROM emp WHERE EXISTS (SELECT * FROM emp WHERE empno='99999'); SELECT * FROM emp WHERE NOT EXISTS (SELECT * FROM emp WHERE empno='99999'); SELECT * FROM emp WHERE EXISTS (SELECT * FROM emp);</code></pre> <h3>HAVING 子句中使用子查询,返回的是单行单列数据</h3> <pre><code>SELECT deptno 部门编号,COUNT(empno) 部门人数,AVG(sal) 平均工资 FROM emp GROUP BY deptno HAVING AVG(sal)&gt;(SELECT AVG(sal) FROM emp ) --查询出每个部门平均工资最高的部门名称及平均工资 SELECT d.dname,ROUND(AVG(e.sal),2) FROM emp e JOIN dept d ON e.deptno=d.deptno GROUP BY d.dname HAVING AVG(sal)=(SELECT MAX(AVG(sal)) FROM emp GROUP BY deptno);</code></pre> <h3>在 FROM 子句中使用子查询。</h3> <ul> <li> <p>子查询返回的是多行多列,多字段分组查询也能实现。子查询性能更强。</p> <pre><code>--查询出每个部门的编号,名称,位置,部门人数,平均工资。 SELECT d.deptno 部门编号,d.dname 部门名称,d.loc 部门位置,e.count 部门人数,e.avg 平均工资 FROM dept d LEFT JOIN (SELECT deptno,COUNT(deptno) COUNT,round(AVG(sal),2) AVG FROM emp GROUP BY deptno) e ON e.deptno=d.deptno; SELECT d.deptno 部门编号,d.dname 部门名称,d.loc 部门位置,count(e.empno) 部门人数,ROUND(AVG(e.sal),2) 平均工资 FROM dept d LEFT JOIN emp e ON e.deptno=d.deptno GROUP BY d.deptno,d.dname,d.loc ;</code></pre> </li> <li> <p>查询出所有在部门SALES(销售部)工作的员工的编号,姓名,基本工资,资金,职位,雇佣日期,部门的最高和最低工资。</p> <pre><code>--确认已经知道的关联字段,如果没有就在子查询中创建关联字段 SELECT e.empno,e.ename,e.sal,e.comm,e.job,e.hiredate,d.max,d.min FROM emp e LEFT JOIN (SELECT deptno,MIN(sal)MIN,MAX(sal) MAX FROM emp GROUP BY deptno) d ON e.deptno=d.deptno WHERE e.deptno=(SELECT deptno FROM dept WHERE dname='SALES');</code></pre> </li> <li> <p>查询出所有薪金高于公司平均薪金的员工编号、姓名、基本工资、职位、雇佣日期,所在部门名称、位置,上级领导姓名,公司的工资等级,部门人数、平均工资、平均服务年限。</p> <pre><code>SELECT E.EMPNO 员工编号, E.ENAME 员工姓名, E.SAL 基本工资, E.JOB 职位, E.HIREDATE 雇佣日期, D.DNAME 部门名称, D.LOC 位置, ME.ENAME 上级领导姓名, S.GRADE 工资等级, AE.COUNT 部门人数, AE.AVG 平均工资, AE.AVGYEARS 平均服务年限 FROM EMP E LEFT JOIN DEPT D ON E.DEPTNO = D.DEPTNO LEFT JOIN EMP ME ON E.MGR = ME.EMPNO LEFT JOIN SALGRADE S ON E.SAL BETWEEN S.LOSAL AND S.HISAL LEFT JOIN (SELECT DEPTNO, COUNT(DEPTNO) COUNT, ROUND(AVG(SAL), 2) AVG, ROUND(AVG(MONTHS_BETWEEN(SYSDATE, HIREDATE) / 12), 2) AVGYEARS FROM EMP GROUP BY DEPTNO) AE ON E.DEPTNO = AE.DEPTNO WHERE E.SAL &gt; (SELECT AVG(SAL) FROM EMP)</code></pre> </li> <li> <p>列出薪金比 ALLEN 或 CLARK 多的所有员工的编号、姓名、基本工资、部门名称、其领导姓名,部门人数。:薪金比 ALLEN 或 CLARK 多的所有员工,并不等价于比两人最低工资高。</p> <pre><code>--对于select后不能再出现统计函数,可以在from中使用,再找到公共字段,联合查询 --错误 SELECT E.EMPNO 员工编号, E.ENAME 姓名, E.SAL 基本工资, D.DNAME 部门名称, ME.ENAME 领导姓名, DC.COUNT 部门人数 FROM EMP E LEFT JOIN DEPT D ON E.DEPTNO = D.DEPTNO LEFT JOIN EMP ME ON E.MGR = ME.EMPNO LEFT JOIN (SELECT DEPTNO, COUNT(DEPTNO) COUNT FROM EMP GROUP BY DEPTNO) DC ON E.DEPTNO = DC.DEPTNO WHERE E.SAL &gt; (SELECT MIN(SAL) FROM EMP WHERE ENAME = 'CLARK' OR ENAME = 'ALLEN')</code></pre> </li> <li> <p>正确,结果也是一样,可能有问题,没有过滤掉自己。</p> <pre><code>SELECT E.EMPNO 员工编号, E.ENAME 姓名, E.SAL 基本工资, D.DNAME 部门名称, ME.ENAME 领导姓名, DC.COUNT 部门人数 FROM EMP E LEFT JOIN DEPT D ON E.DEPTNO = D.DEPTNO LEFT JOIN EMP ME ON E.MGR = ME.EMPNO LEFT JOIN (SELECT DEPTNO, COUNT(DEPTNO) COUNT FROM EMP GROUP BY DEPTNO) DC ON E.DEPTNO = DC.DEPTNO WHERE E.SAL &gt; ANY (SELECT SAL FROM EMP WHERE ENAME IN ('CLARK', 'ALLEN'))</code></pre> </li> <li>列出公司各个部门的经理(假如每个部门只有一个经理,job为MANAGER)的姓名、薪金、部门名称、部门人数、部门平均工资。 <pre><code>SELECT E.ENAME 姓名, E.SAL 薪金, D.DNAME 部门名称, C.COUNT 部门人数, C.AVG 部门平均工资 FROM EMP E LEFT JOIN DEPT D ON E.DEPTNO = D.DEPTNO LEFT JOIN (SELECT DEPTNO, COUNT(DEPTNO) COUNT, AVG(SAL) AVG FROM EMP GROUP BY DEPTNO) C ON E.DEPTNO = C.DEPTNO WHERE JOB = 'MANAGER'</code></pre></li> </ul> <h3>在select子句中使用子查询</h3> <pre><code>--查询出公司每个部门的编号、名称、位置、部门人数、平均工资。(使用之前的from子句也是可以实现的) SELECT DEPTNO 部门编号, DNAME 名称, LOC 位置, (SELECT COUNT(DEPTNO) FROM EMP WHERE DEPTNO = D.DEPTNO) 部门人数, (SELECT ROUND(AVG(SAL), 2) FROM EMP WHERE DEPTNO = D.DEPTNO) 平均工资 FROM DEPT D;</code></pre> <h3>WITH 子句,一种定义临时表的操作方法</h3> <pre><code>WITH e AS (SELECT * from emp) SELECT * FROM e ; --查询出公司每个部门的编号、名称、位置、部门人数、平均工资。 WITH E AS (SELECT DEPTNO DNO, ROUND(AVG(SAL), 2) AVG, COUNT(SAL) COUNT FROM EMP GROUP BY DEPTNO) SELECT D.DEPTNO 部门编号, D.DNAME 部门名称, D.LOC 部门位置, E.AVG 平均工资, E.COUNT 部门人数 FROM DEPT D LEFT JOIN E ON D.DEPTNO = E.DNO;</code></pre> <ul> <li>查询每个部门工资最高的雇员编号、姓名、职位、雇佣日期、工资、部门编号、部门名称,显示的结果按照部门编号进行排序。 <pre><code>WITH E AS (SELECT DEPTNO DNO, MAX(SAL) MAX FROM EMP GROUP BY DEPTNO) SELECT EM.EMPNO 雇员编号, EM.ENAME 姓名, EM.JOB 职位, EM.HIREDATE 雇用日期, EM.SAL 工资, EM.DEPTNO 部门编号, D.DNAME 部门名称 FROM EMP EM JOIN E ON E.DNO = EM.DEPTNO AND EM.SAL = E.MAX LEFT JOIN DEPT D ON EM.DEPTNO = D.DEPTNO ORDER BY EM.DEPTNO ASC</code></pre></li> </ul> <h3>分析函数:这部分知识强大,过于难,下次学。</h3> <p><code>SELECT deptno,ename,sal,SUM(sal) OVER(PARTITION BY deptno) SUM FROM emp ;</code></p> <h3>行列转换,PIVOT():列换行,UNPIVOT()行换列</h3> <ul> <li>子查询:明确了PIVOT()函数操作的数据。</li> <li>统计函数(列):转换过程中设定其他列的显示方法。</li> <li>FOR 转换列名称:将子查询中返回的数据指定转换为列。 <pre><code>-- 查询每个部门中各个职位的总工资 SELECT deptno,job,SUM(sal) FROM emp GROUP BY deptno,job ; SELECT * FROM (SELECT DEPTNO, JOB, SAL FROM EMP) PIVOT(SUM(SAL) FOR JOB IN('PRESIDENT' PRESIDENT_JOB, 'CLERK' CLERK_JOB, 'SALESMAN' SALESMAN_JOB, 'MANAGER' MANAGER_JOB, 'ANALYST' ANALYST_JOB)) ORDER BY DEPTNO;</code></pre></li> <li>在使用PIVOT() 函数中增加XML显示,可利用ANY,PL/SQL好像不能显示 <pre><code>SELECT * FROM (SELECT deptno,job,sal FROM emp) PIVOT XML (SUM(sal) FOR job IN (ANY))ORDER BY deptno ; SELECT * FROM (SELECT JOB, DEPTNO, SAL, SUM(SAL) OVER(PARTITION BY DEPTNO) SUM_SAL, MAX(SAL) OVER(PARTITION BY DEPTNO) AMX_SAL, MIN(SAL) OVER(PARTITION BY DEPTNO) MIN_SAL FROM EMP) PIVOT(SUM(SAL) FOR JOB IN('MANAGER' MANAGER_SAL, 'CLERK' CLERK_SAL, 'SALESMAN' SALESMAN_SAL, 'ANALYST' ANALYST_SAL, 'PRESIDENT' PRESIDENT_SAL)) ORDER BY DEPTNO;</code></pre></li> <li>注意上面的那个和下面这 个的区别,上面的是以部门为单位的聚合函数,下面的是以转列后的聚合函数 <pre><code>SELECT * FROM (SELECT DEPTNO, JOB, SAL FROM EMP) PIVOT(SUM(SAL) AS SUM_SAL, MAX(SAL) AS SUM_MAX FOR JOB IN('MANAGER' AS MANAGER_JOB, 'CLERK' AS CLERK_JOB, 'SALESMAN' AS SALESMAN_JOB, 'ANALYST' AS ANALYST_JOB, 'PRESIDENT' AS PRESIDENT_JOB)) ORDER BY DEPTNO;</code></pre></li> </ul> <p><strong> 穿插DDL语句,为表添加列,插入一条记录,这部分内容在9章与11章</strong></p> <pre><code>ALTER TABLE emp ADD (sex VARCHAR2(10) DEFAULT '男'); SELECT * FROM emp ; UPDATE emp SET sex='女'WHERE to_char(hiredate ,'yyyy')='1981'; COMMIT;</code></pre> <ul> <li> <p>添加一列查询,只转两个职位</p> <pre><code>SELECT * FROM (SELECT DEPTNO, JOB, SAL,SEX FROM EMP) PIVOT(SUM(SAL) AS SUM_SAL, MAX(SAL) AS SUM_MAX FOR(JOB, SEX) IN(('MANAGER', '男') AS MANAGER_MALE_JOB, ('MANAGER', '女') AS MANAGER_FEMALE_JOB, ('CLERK', '男') AS CLERK_MALE_JOB, ('CLERK', '女') AS CLERK_FEMALE_JOB)) ORDER BY DEPTNO;</code></pre> </li> <li>UNPIVOT():将行转换为列</li> <li>INCLUDE NULLS :列变行后保留空值</li> <li>EXCLUDE NULLS:(默认)列变行后不保留空值</li> </ul> <pre><code>WITH TEMP AS (SELECT * FROM (SELECT DEPTNO, JOB, SAL FROM EMP) PIVOT(SUM(SAL) FOR JOB IN('PRESIDENT' AS PRESIDENT_JOB, 'CLERK' AS CLERK_JOB, 'SALESMAN' AS SALESMAN_JOB, 'MANAGER' AS MANAGER_JOB, 'ANALYST' AS ANALYST_JOB)) ORDER BY DEPTNO) SELECT * FROM TEMP UNPIVOT EXCLUDE NULLS (SAL_SUM FOR JOB IN(PRESIDENT_JOB AS 'PRESIDENT', CLERK_JOB AS 'CLERK', SALESMAN_JOB AS 'SALESMAN', MANAGER_JOB AS 'MANAGER', ANALYST_JOB AS 'ANALYST')) ORDER BY DEPTNO;</code></pre> <ul> <li>包含空值 <pre><code>WITH TEMP AS (SELECT * FROM (SELECT DEPTNO, JOB, SAL FROM EMP) PIVOT(SUM(SAL) FOR JOB IN('PRESIDENT' AS PRESIDENT_JOB, 'CLERK' AS CLERK_JOB, 'SALESMAN' AS SALESMAN_JOB, 'MANAGER' AS MANAGER_JOB, 'ANALYST' AS ANALYST_JOB)) ORDER BY DEPTNO) SELECT * FROM TEMP UNPIVOT INCLUDE NULLS (SAL_SUM FOR JOB IN(PRESIDENT_JOB AS 'PRESIDENT', CLERK_JOB AS 'CLERK', SALESMAN_JOB AS 'SALESMAN', MANAGER_JOB AS 'MANAGER', ANALYST_JOB AS 'ANALYST')) ORDER BY DEPTNO;</code></pre></li> </ul> <h3>设置数据层次:确定数据行之间关系结构的一种操作,如管理层、职员层。</h3> <pre><code>/* LEVEL ... CONNECT BY [nocycle] prior 连接条件 [start with 开始条件] level:可以根据数据所处的层次结构实现自动的层次编号,例如,1、2、3 connect by:连接条码 start with :根节点数据的开始条件 */ --观察分层的基本关系,lpad()第三个参数是空格 SELECT empno,LPAD('|-',LEVEL*2,' ')||ename empname,mgr,LEVEL FROM emp CONNECT BY PRIOR empno=mgr START WITH mgr IS NULL ; SELECT empno,ename empname,mgr,LEVEL FROM emp CONNECT BY PRIOR empno=mgr START WITH mgr IS NULL ; -- 加入connect_by_isleaf伪列,返回0是根节点,返回1是叶子节点 SELECT empno,LPAD('|-',LEVEL*2,' ')||ename empname,mgr,LEVEL,DECODE(connect_by_isleaf,0,'根节点',1,' 叶子节点') isleaf FROM emp CONNECT BY PRIOR empno=mgr START WITH mgr IS NULL ; --connect_by_root 列:找出分层中的根节点,可能指定查询根节点的某个字段 SELECT empno,LPAD('|-',LEVEL*2,' ')||ename empname,mgr,LEVEL,connect_by_root empno AS root_name FROM emp CONNECT BY PRIOR empno=mgr START WITH mgr IS NULL ;</code></pre> <p><strong>后面还有一大堆东西,不学了</strong></p> <h3>本章总结</h3> <ul> <li>子查询:在完事的语句中嵌入小查询,可以在任何地方插入。</li> <li>子查询返回的类型有:单行单列、单行多列、多行单列、多行多列。</li> <li>学习了 IN、ANY、ALL 等 。</li> <li>子查询、限定查询、多表查询、统计查询结合有一起可以完成复杂查询操作。</li> <li>分析函数。</li> <li>行列转换可以用复杂的SQL完成,也可以使用 PIVOT()、UNPIVOT()函数实现。</li> <li>分层查询。 </li> </ul>

页面列表

ITEM_HTML