复杂SQL

SQL语言本质

SQL语言本质是集合的运算:不要认为SQL语言操作的一定是一个表里的数据,实际上SQL操作的是一个集合,整个SQL的处理过程实际上就是集合的运算过程。

标量子查询

scalar subquery

示例:

  1. select
  2. (select dname from dept b where b.deptno=a.deptno), -- 必须为 一对一 对应的字段
  3. ename
  4. from
  5. emp a
  6. order by 1,2;

with重用结果集

with一般用于数据库sql调优

示例:

找出工资大于 所有员工总工资四分之一的员工

普通SQL:

  1. select
  2. ename,sum(sal)
  3. from
  4. emp
  5. group by
  6. ename
  7. having
  8. sum(sal) >= (select sum(sal)/4 from emp);

使用with的sql:

  1. with t as
  2. (select ename,sum(sal) salsum from emp group by ename)
  3. select
  4. ename, salsum
  5. from
  6. t
  7. where
  8. -- 此处重用了t的结果集,避免了再次emp的全表扫描,通常情况下效率会比上面普通SQL要高
  9. -- 但是要分情况使用,因为构造t这个临时结果集也需要一些耗时,所以对于一些数据量小的表,重用结果集比全表扫描可能还要慢
  10. salsum >= (select sum(salsum)/4 from t);

查看SQL执行效率,可以通过查看带有多少个一致性读:

  1. set autotrace trace exp stat;

然后执行sql后,会打印出统计信息:

  1. 统计信息
  2. ----------------------------------------------------------
  3. 1 recursive calls
  4. 0 db block gets
  5. 14 consistent gets
  6. 0 physical reads
  7. 0 redo size
  8. 647 bytes sent via SQL*Net to client
  9. 416 bytes received via SQL*Net from client
  10. 2 SQL*Net roundtrips to/from client
  11. 0 sorts (memory)
  12. 0 sorts (disk)
  13. 5 rows processed

主要关注 consistent gets(一致性读)的数量,数量越多执行越慢。

关闭追踪:

  1. set autotrace off;

使用with as 循环遍历下属机构

  1. WITH Org_Total(orgid,belongorgid) AS
  2. (
  3. SELECT orgid,belongorgid FROM TS_ORG_INFO WHERE orgid='1'
  4. UNION All
  5. SELECT a.orgid,a.belongorgid FROM TS_ORG_INFO a, Org_Total b where a.BELONGORGID=b.orgid AND a.orgid<>'1'
  6. )
  7. SELECT orgid FROM Org_Total;

使用Connect by循环遍历月份

  1. --使用level进行循环查询:
  2. SELECT
  3. (CASE
  4. WHEN length(LEVEL)=1 THEN '20180' || LEVEL
  5. ELSE '2018'|| LEVEL
  6. end) AS countmonth
  7. FROM
  8. dual
  9. CONNECT BY
  10. LEVEL<=12;
  11. --使用rownum进行循环查询:
  12. SELECT
  13. (CASE
  14. WHEN length(rownum)=1 THEN '20180' || rownum
  15. ELSE '2018'||rownum
  16. end) AS countmonth
  17. FROM
  18. dual
  19. CONNECT BY
  20. rownum<=12;

decode处理行转列

decode普通用法示例:

  1. select
  2. decode(deptno,
  3. 10, 'ACCOUNTING',
  4. 20, 'RESERCH',
  5. 30, "SAILES"),
  6. sum(sal)
  7. from
  8. emp
  9. group by
  10. deptno;

decode行转列:

  1. -- 原始sql
  2. select job, ename, sal from emp where job='MANAGER';
  3. -- 行转列
  4. select job,
  5. -- 通过decodeename列中BLAKE的名字换成其工资,其他员工为null
  6. -- 此时对enamesum即可将员工的工资合并到一行,将该sum结果列命名为BLAKE员工名
  7. sum(decode(ename, 'BLAKE', sal)) BLAKE,
  8. sum(decode(ename, 'JONES', sal)) JONES,
  9. sum(decode(ename, 'CLARK', sal)) CLARK,
  10. from emp
  11. where job='MANAGER'
  12. group by job;

使用 merge into 进行对比更新插入

merge into 的用法:

  1. merge into 目标表 a
  2. using 源表 b
  3. on (a.条件字段1=b.条件字段1 and a.条件字段2=b.条件字段2)
  4. when matched then update set a.更新字段=b.字段
  5. when not matched then insert (a.字段1, a.字段2....) values (b.字段1, b.字段2.....)

示例:

ts_test_source 对比插入到 test_source中。

ts_test_source为当日的数据,test_source为历史存量数据。

需要将 ts_test_source 中变化的数据、新插入的数据 插入到 test_source中作为新一天的拉链数据。

创建需要用的表:

  1. create table test_source (
  2. datadate varchar2(32),
  3. customerid varchar2(32),
  4. customername varchar2(32),
  5. age integer,
  6. birthday varchar2(32)
  7. );
  8. create table ts_test_source (
  9. datadate varchar2(32),
  10. customerid varchar2(32),
  11. customername varchar2(32),
  12. age integer,
  13. birthday varchar2(32)
  14. );

插入测试数据:

  1. insert into test_source (datadate,customerid,customername,age,birthday) values('20201207','202012070001','张三',12,'20200101');
  2. insert into test_source (datadate,customerid,customername,age,birthday) values('20201207','202012070002','李四',null,'20200101');
  3. insert into ts_test_source (datadate,customerid,customername,age,birthday) values('20201208','202012070001','张三',12,'20200101'); -- 张三的数据与历史存量完全相同
  4. insert into ts_test_source (datadate,customerid,customername,age,birthday) values('20201208','202012070002','李四',10,'20200101'); -- 李四的age字段做了更新
  5. insert into ts_test_source (datadate,customerid,customername,age,birthday) values('20201208','202012070003','王五',null,'20200101'); -- 新插入了王五的数据

存量表 test_source 内容如下:

DataDate CustomerID CustomerName Age Birthday
20201207 202012070001 张三 12 20200101
20201207 202012070002 李四 [null] 20200101

当日数据 ts_test_source 内容如下:

DataDate CustomerID CustomerName Age Birthday
20201208 202012070001 张三 12 20200101
20201208 202012070002 李四 10 20200101
20201208 202012070003 王五 [null] 20200101

使用 merge into进行对比更新:

  1. merge into test_source a1
  2. using ts_test_source a2
  3. on (a1.customerid=a2.customerid and a1.customername=a2.customername and a1.age=a2.age and a1.birthday=a2.birthday and a1.datadate='20201207')
  4. when not matched
  5. then
  6. insert (a1.datadate, a1.customerid,a1.customername,a1.age, a1.birthday)
  7. values (a2.datadate,a2.customerid,a2.customername,a2.age,a2.birthday )

执行结果:

test_source 表中数据为

DataDate CustomerID CustomerName Age Birthday
20201207 202012070001 张三 12 20200101
20201207 202012070002 李四 [null] 20200101
20201208 202012070002 李四 10 20200101
20201208 202012070003 王五 [null] 20200101

DBA常用SQL

数据库大小

数据表空间文件大小,加上临时表空间文件大小。

不包含redo表空间大小。

  1. select
  2. (
  3. (select sum(bytes) from dba_data_files)
  4. +
  5. (select sum(bytes) from dba_temp_files)
  6. ) total_size
  7. from dual;

某个段对象(表、索引)的大小

  1. select sum(bytes)
  2. from
  3. user_segments
  4. where
  5. segment_name='T';

某个表空间的大小

  1. select sum(bytes)
  2. from
  3. dba_data_files
  4. where
  5. tablespace_name='USERS';

日志文件的状态

  1. select group#, status from v$log;

事务阻塞

  1. select sid, type, lmode, request
  2. from v$lock
  3. where type in ('TM', 'TX')
  4. order by 1,2;

SQL和执行计划

执行计划是SQL获取和处理数据的途径和方法。