【Oracle学习11】 子查询和集合运算符

【Oracle学习11】 子查询和集合运算符

子查询

11.1 定义子查询

子查询:

  • 子查询是嵌套在另一个SQL语句中的查询.
  • 子查询可以嵌套在SELECT,INSERT,UPDATE,DELETE或其它查询内的查询。
  • 子查询不能在GROUP By及Order By中。
  • 子查询可以返回标量,也可以返回一组记录。
  • 除关联子查询外,子查询都在嵌套查询的外查询之前就先执行。
subquery
subquery
#部门及员工数量
select sysdate Today, (select count(*) from departments ) dept_cnt,
(select count(*) from employees) emp_cnt from dual;
#找出经理员工
select last_name from employees where (employee_id in (select manager_id from employees));
#每个国家最高薪水
SQL>select max(salary),country_id from (select e.salary,department_id,l.country_id from employees e join departments d using (department_id) 
 join locations l using (location_id)) 
 group by country_id;
MAX(SALARY) COUN
----------- ----
      24000 US
      13000 CA
      10000 DE
      14000 UK

##子查询位置

11.2 子查询可以解决的问题

11.2.1 将子查询的结果集用于比较

子查询可以返回标量,也可以返回一组记录。 标量子查询只返回一个值。

#找出低于平均薪水的员工
select avg(salary) from employees; --$6461
select last_name,salary from employees where salary < (select avg(salary) from employees) order by salary desc;
#返回一组记录,找出有员工的部门
select department_name from departments where department_id in (select distinct(department_id ) from employees);
select d.department_id,count(*)  as cnt from departments d join employees on d.department_id = employees.department_id group by d.department_id;

11.2.2 星型转换

oracle可允许START_TRANSFORMATION_ENABLED

#多表关联的sql可改写为星型SQL
select count(quantity_sold) from sales , products, cust_id where sales.xx = products.xx and ... ;
#星型SQL
select count(quantity_sold) from sales where prod_id in 
(select prod_id from products where prod_name='Comic Book Heroes') 
and cust_id in (select cust_id from customers where cust_city='Oxford');

11.2.3 生成对其执行SELECT 语句的表

内联视图:
FROM子句中的子查询,叫称为内联视图(inline views)。

#查询各国家的平均薪水
select avg(salary) ,country_id from 
(select salary,country_id from employees natural join departments natural join locations) group by country_id;
AVG(SALARY) COUN
----------- ----
       5640 US
       6000 CA
       8500 UK

11.2.4 生成投影值

select (select max(salary) from employees) * (select max(commission_pct) from employees) / 100  from dual;

11.2.5 生成传递给DML语句的行

insert into sales_hist select * from sales where date>sysdate-1;

update employees set salary = (select avg(salary) from employees) ; 

delete from departments where department_id not in (select department_id from employees where department_id is not null);

11.3 列举子查询的类型

子查询可以分成a)单行子查询 b)多行子查询 c)关联子查询

subquery
单行子查询
i多行子查询
子查询与Having

11.3.1 单行和多行子查询

  • 在父查询之前就需要执行子查询。
  • 适用于单行子查询的运算符是=,>,>=,<,<=和<>。 多行子查询的比较运算符是IN,NOT IN,ANY和ALL。
错误示例
多行子查询
多行子查询
SQL>SELECT salary FROM   employees WHERE  job_id = 'IT_PROG';
    SALARY
----------
      9000
      6000
      4800
      4800
      4200
#salary < all 没有大于4200元工资
SELECT employee_id, last_name, job_id, salary
FROM   employees
WHERE  salary < all
                    (SELECT salary
                     FROM   employees
                     WHERE  job_id = 'IT_PROG')
AND    job_id <> 'IT_PROG' order by salary;

#salary < ANY ??是指??
SQL>SELECT employee_id, last_name, job_id, salary
FROM   employees
WHERE  salary < ANY
                    (SELECT salary
                     FROM   employees
                     WHERE  job_id = 'IT_PROG')
AND    job_id <> 'IT_PROG';
多列名子查询
Null Values in a Subquery
# 多列名子查询
#找出各部门工资最少的人
SQL> SELECT first_name, min(salary), department_id FROM employees GROUP BY department_id  order by department_id;
ERROR at line 1:
ORA-00979: not a GROUP BY expression
#正确写法如下:
SELECT min(salary), department_id FROM employees
       GROUP BY department_id order by department_id;
#
SQL> SELECT first_name, department_id, salary FROM employees
    WHERE (salary, department_id) IN
          (SELECT min(salary), department_id
           FROM employees
           GROUP BY department_id)
    ORDER BY department_id;

FIRST_NAME                               DEPARTMENT_ID     SALARY
---------------------------------------- ------------- ----------
Jennifer                                            10       4400
Pat                                                 20       6000
Karen                                               30       2500
Susan                                               40       6500
TJ                                                  50       2100
Diana                                               60       4200
Hermann                                             70      10000
Sundita                                             80       6100
Neena                                               90      17000
Lex                                                 90      17000
Luis                                               100       6900
William                                            110       8300

#Null Values in a Subquery。找出不是manager的员工,如下是错误的。
SELECT distinct manager_id FROM   employees; -- rownum=19 有一个null
SQL> SELECT emp.last_name FROM   employees emp
WHERE  emp.employee_id NOT IN
 (SELECT mgr.manager_id FROM   employees mgr);
no rows selected

#可以改写为
SQL>SELECT emp.last_name,manager_id FROM   employees emp
WHERE  emp.employee_id NOT IN
(SELECT nvl(mgr.manager_id,0) FROM   employees mgr) order by manager_id ;    

11.3.2 关联子查询

关联子查询效率较差

#找出薪水少于部门平均薪水的所有员工.
select p.last_name ,p.department_id from employees p 
 where p.salary < (select avg(s.salary) from employees s where s.department_id = p.department_id );
LAST_NAME                                          DEPARTMENT_ID
-------------------------------------------------- -------------
OConnell                                                      50
Grant                                                         50
Fay                                                           20
Gietz                                                        110
...
#找出薪水大于'Taylor'的人
SQL> select last_name from employees where salary > (select salary from employees where last_name = 'Taylor') order by last_name;
ORA-01427: single-row subquery returns more than one row

改正方法1:
select last_name from employees where salary > all (select salary from employees where last_name = 'Taylor') order by last_name;

方法2:
select last_name from employees where salary >  (select max(salary) from employees where last_name = 'Taylor') order by last_name;

11.4 写单行和多行子查询

# 找平均薪水最高的工作
SQL>select job_title from jobs natural join employees group by job_title 
having avg(salary) = (select max(avg(salary)) from employees group by job_id);
JOB_TITLE
----------------------------------------------------------------------
President

11.4.1 使用Exists 条件

#EXISTS 存在一个或多个就返回True。
#有员工的部门.从外表中一条条取记录,在子表中比较.
select department_name from departments d where exists (select * from employees e where d.department_id= e.department_id);
#没有员工的部门
select department_name from departments d where not exists (select * from employees e where d.department_id= e.department_id);

11.4.2 子查询的空结果

若子查询有可能为空,则需要避免使用not in ,因为这相当于<>all。

#结果为空,因为子查询会返回null
SQL> select last_name,employee_id,manager_id from employees where employee_id not in (select manager_id from employees);
no rows selected
改为。找出没有经理的员工
select last_name,employee_id,manager_id from employees where employee_id not in (select manager_id from employees where manager_id is not null); --rownum=50

#有经理的员工
select last_name,employee_id,manager_id from employees where employee_id in (select manager_id from employees where manager_id is not null);

11.5 描述集合运算符

三种集合运算: UNION,INTERSECT ,MINUS。

  • UNION : 返回查询合并,排序并删除重复行。 多个结果集UNION,结果会自动转换为最高的精度。
  • UNION ALL: 返回查询合并,不排序,不去重。
  • INTERSECT: 只返回同时出现在两个查询结果集中的行,排序这些行并删除重复行。 求交集。
  • MINUS: 只返回第一个结果集中的行,并且这些行不能出现在第二个结果集中。 减集。
集合运算符

11.5.1 集合和维恩图

集合运算要求:

  • 查询结果集的列数必须相同。 数据类型大致相同(即相同或者可隐式转换)。
  • 集合运算可以有不同的名称,再输出结果用第一个查询的名称。
  • UNION,MINUS,INTERSECT都会删除重复的行记录。
  • 会默认返回接所有列排序(从左到右)行。 除非使用 NUNION ALL,则不会排序。
  • 集合运算符的优先级相同,以指定它们的顺序应用。
  • 集合中Order by 只能出现在结果集的最忍气吞声,不能出现在一个查询中间。
集合运算原则
UNION
INTERSECT
MINUS
image.png
#UNION
SQL> select region_name from regions UNION select region_name from regions;
REGION_NAME
--------------------------------------------------
Americas
Asia
Europe
Middle East and Africa

#  UNION ALL如下将不排序
select region_name from regions UNION ALL select region_name from regions;

#INTERSECT
SQL> select region_name from regions INTERSECT (select region_name from regions where region_name like 'A%' );
REGION_NAME
--------------------------------------------------
Americas
Asia

#MINUS
SQL> select region_name from regions MINUS (select region_name from regions where region_name like 'A%' );
REGION_NAME
--------------------------------------------------
Europe
Middle East and Africa

SQL> select region_name from regions MINUS (select region_name from regions  );
no rows selected

#两表列数对齐,可以使用TO_CHAR
SQL>SELECT location_id, department_name "Department", 
   TO_CHAR(NULL) "Warehouse location"  
FROM departments
UNION
SELECT location_id, TO_CHAR(NULL) "Department", 
   state_province
FROM locations;

11.6 使用集合运算符将多个查询合并为一个查询

#多个结果集UNION,结果会自动转换为最高的精度。
#不同的结果集可以增加null列来运用
select name,tail_length,null from cats union all select name,null ,wingspan from birds;
select sysdate , null from dual union select null ,'zhang3' from dual;

11.7 返回行的顺序

返回行的顺序:

  • 不能在组成复合查询的单个查询中使用ORDER BY 子句
  • 可以在复合查询的结尾添加ORDER BY 子,并指定列号或别名
  • UNION ALL 返回的行按它们在两个源查询中出现的顺序排列
  • 非UNION ALL,依从左到右列的顺序排序返回的行。
order by

11.8 知识点回顾

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容