【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 知识点回顾

你可能感兴趣的:(【Oracle学习11】 子查询和集合运算符)