Oraclesql

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oraclesql相关的知识,希望对你有一定的参考价值。

参考技术A

  前言

  sql_trace 是我在工作中经常要用到的调优工具 相比较statspack 我更愿意用这个工具

  因为数据库慢原因的 %以上是由于sql问题造成的 statspack没有sql的执行计划 显示没有它直观 方便 对想要针对性不强

   介绍数据库调优需要经常会用到的工具 可以很精确地跟抓取相关session正在运行的sql 再通过tkprof分析出来sql的执行计划等相关信息 从而判断那些sql语句存在问题

  统计如下信息(摘字官方文档)

  Parse execute and fetch counts

  CPU and elapsed times

  Physical reads and logical reads

  Number of rows processed

  Misses on the library cache

  Username under which each parse occurred

  Each mit and rollback

   使用

  使用前需要注意的地方

   初始化参数timed_statistics=true   允许sql trace 和其他的一些动态性能视图收集与时间(cpu elapsed)有关的参数 一定要打开 不然相关信息不会被收集 这是一个动态的参数 也可以在session级别设置

  SQL>alter session set titimed_statistics=true

   MAX_DUMP_FILE_SIZE跟踪文件的大小的限制 如果跟踪信息较多可以设置成unlimited 可以是KB MB单位 I开始默认为unlimited这是一个动态的参数 也可以在session级别设置

  SQL>alter system set max_dump_file_size=

  SQL>alter system set max_dump_file_size=unlimited

   USER_DUMP_DEST指定跟踪文件的路径 默认路径实在$ORACLE_BASE/admin/ORA_SID/udump这是一个动态的参数 也可以在session级别设置

  SQL>alter system set user_dump_dest=/oracle/trace

  数据库级别

  设置slq_trace参数为true会对整个实例进行跟踪 包括所有进程 用户进程和后台进程 会造成比较严重的性能问题 生产环境一定要慎用

  SQL>alter system set sql_trace=true;

  Session级别

  当前会话

  SQL>alter session set sql_trace=true;

  SQL>alter session set sql_trace=false;

  其他会话

  通过oracle提供的系统包 DBMS_SYSTEM SET_SQL_TRACE_IN_SESSION来实现

  SQL>execute dbms_system set_sql_trace_in_session(sid serial# true);

  SQL>execute dbms_system set_sql_trace_in_session(sid serial# false);

  注

   sid serial#从v$session视图中获得

   DBMS_SYSTEM包里还可以对其他用户的参数(如 timed_statistics max_dump_file)进行设置 在这不做介绍了 很少用到 想了解dbms_system里的程序包可以desc dbms_system看一下

  得到trace文件后我们要用tkprof他进行格式化 通过sql语句快速定位到相应的trace文件

  Tkprof

  tkprof的目的是将sql trace生成的跟踪文件转换成用户可以理解的格式

  格式

  tkprof tracefile outputfile [optional | parameters ]

  参数和选项(这里只介绍最常用的 也是最实用的)

  explain=user/password执行explain命令将结果放在SQL trace的输出文件中

  sys=[yes/no]确定系统是否列出由sys用户产生或重调的sql语句

  sort=sort_option按照指定的方法对sql trace的输出文件进行降序排序

  sort_option选项

  prscnt按解析次数排序

  prscpu按解析所花cpu时间排序

  prsela按解析所经历的时间排序

  prsdsk按解析时物理的读操作的次数排序

  prsqry按解析时以一致模式读取数据块的次数排序

  prscu按解析时以当前读取数据块的次数进行排序

  execnt按执行次数排序

  execpu按执行时花的cpu时间排序

  exeela按执行所经历的时间排序

  exedsk按执行时物理读操作的次数排序

  exeqry按执行时以一致模式读取数据块的次数排序

  execu按执行时以当前模式读取数据块的次数排序

  exerow按执行时处理的记录的次数进行排序

  exemis按执行时库缓冲区的错误排序

  fchcnt按返回数据的次数进行排序

  fchcpu按返回数据cpu所花时间排序

  fchela按返回数据所经历的时间排序

  fchdsk按返回数据时的物理读操作的次数排序

  fchqry按返回数据时一致模式读取数据块的次数排序

  fchcu按返回数据时当前模式读取数据块的次数排序

  fchrow按返回数据时处理的数据数量排序

  注

  这些排序中我经常用到的是fchdsk fckchela fchqry 因为有问题的sql一般都是大的查询造成的 当然更新 插入 删除时也会存在全表扫描 这就需要:exedsk exeqry exeela等选项 根据具体情况具体分析

  Cpu时间和Elapsed时间都是以秒为单位 而且两个值基本上一样 但我比较常用elapsed 他是反映的用户相应时间 从运行sql到用户得到结果的时间 会更实际些

  tkprof输出文件各列的含义 (理解下面的含义对我们快速定位问题很有帮助)

  parse:

  将sql语句转换成执行计划 包括检查是否有正确的授权 需要到得表 列及其他引用到得对象是否存在 这些信息分别存在v$librarycache v$rowcache

  execute

  oracle实际执行的语句 如 insert update delete 这些会修改数据 对于select操作 这部只是确定选择的行数

  fetch

  返回查询获得的行数 只有执行select会被收集

  Count

  这个语句被parse execute fetch的次数的统计

  Cpu

  这个语句所有的parse execute fetch所用的cpu总的时间 以秒为单位 如果TIMED_STATISTICS 关闭的话 值为

  Elapsed

  这个语句所有的parse execute fetch所消耗的总的时间 以秒为单位 如果TIMED_STATISTICS 关闭的话 值为

  Disk

  这个语句所有的parse execute fetch从磁盘上的数据文件中读取的数据块的数量

  Query

  在一致性读的模式下 这个语句所有的parse execute fetch所获取的buffer数量(这部分是从内存读取的也就是逻辑读取的 相当于执行计划里的consistent gets)

  Current

  在current模式下 这个语句所有的parse execute fetch所获取的buffer数量 一般是current模式下发生的delect insert update的操作都会获取buffer

  Rows

  语句返回的行数 不包括子查询中返回的记录数目 对于select语句 返回在fetch这步 对于insert delete update操作 返回记录是在execute这步

   分析

  我一般的思路步骤是

   先找磁盘多的sq l(sort= fchdsk ) 意味着全表扫描 找运行时间长的(sort= fchela) 意味着sql可能写的不好或磁盘 逻辑读较多 找出一致性读较多的(sort= fchqry) 当表不是很大的时候(可能全部缓存住了) 没有发生磁盘读 但不意味着不需要建立索引 或者sql需要优化 找出当前模式从缓冲区获得数据的数量(sort=exedsk exeela exeqry) 这些主要集中在dml语句里的操作 看是否有必要优化sql或建立索引之所以排序是为了在sql很多的时候快速定位sql 如果sql比较少的话就没必要排序了 但我们要有分析问题的思路

   举例

  我自己建立了一个表

  create table t (id int);

  begin

  for v in loop

  insert into t values(v );

  end loop

  mit;

  end;

  下面是sql_trace所抓到得sql

  不正常状态

  *******************************************************************************

  select *

  from t

  where id=

  call count cpu elapsed disk query current rows

        

  Parse                                                                   Execute                                                                 Fetch                                                        

        

  total                                                        

  Misses in library cache during parse:

  Optimizer goal: CHOOSE

  Parsing user id: (WH)

  Rows Row Source Operation

     

   TABLE ACCESS FULL T

  Rows Execution Plan

     

   SELECT STATEMENT GOAL: CHOOSE

   TABLE ACCESS (FULL) OF T

  首先这是一个select语句 它走了全部扫描

  磁盘读( )和逻辑读( )都很多

  运行了 次(Execute) 分析了 次(Parse) 一共用了将近 秒(elapsed)

  我只是选择表的一行的数据的结果 就发生这么大的成本 很显然是全表扫描的结果造成的

  正常状态

  在做跟踪前我为这个表建立了一个索引

  Create index t on t (id);

  *******************************************************************************

  select *

  from t

  where id=

  call count cpu elapsed disk query current rows

        

  Parse                                                                 Execute                                                                 Fetch                                                                

        

  total                                                            

  Misses in library cache during parse:

  Optimizer goal: CHOOSE

  Parsing user id: (WH)

  Rows Row Source Operation

     

   INDEX RANGE SCAN T (object id )

  Rows Execution Plan

     

   SELECT STATEMENT GOAL: CHOOSE

   INDEX (RANGE SCAN) OF T (NON UNIQUE)

  *******************************************************************************

  同样的语句

  它走了索引 物理读 这个 其实是开始读索引时需要第一次读入的 以后运行就没有了

  逻辑读 (平均这个sql一次 个逻辑读)

  同样运行了 次(Execute)

  分析了 次(Parse) 运行次数越多 分析次数越少越好一共只用了 秒(elapsed)

lishixinzhi/Article/program/Oracle/201311/17866

OracleSql语句学习

SELECT e.ename,m.ename,d.loc
FROM emp_weiyiji e JOIN emp_weiyiji m ON e.mgr=m.empno
JOIN dept_weiyiji d ON e.deptno=d.deptno
WHERE e.ename=‘SMITH‘

SELECT e.ename,m.ename,d.loc
FROM emp_weiyiji e,emp_weiyiji m,dept_weiyiji d
WHERE e.mgr=m.empno AND e.deptno=d.deptno AND e.ename=‘SMITH‘

--子查询
--子查询是一条查询语句,它是嵌套在其他SQL语句当中的,作用是为外层的SQL提供数据的。子查询常用在
--查询语句中,当然也可以用作DML,DDL中.
查看与FORD同部门的员工?
SELECT ename,sal,deptno FROM emp_weiyiji
WHERE deptno=(SELECT deptno FROM emp_weiyiji WHERE ename=‘FORD‘)--先执行括号里面的
查看和SMITH同职位的都有谁?
SELECT ename,sal,job FROM emp_weiyiji
WHERE job=(SELECT job FROM emp_weiyiji WHERE ename=‘SMITH‘)
查看比公司平均工资高的员工信息?
SELECT ename,sal,job,deptno FROM emp_weiyiji
WHERE sal>(SELECT AVG(sal) FROM emp_weiyiji)

--在DDL中使用子查询,可以基于一个子查询的结果集当做表快速的创建出来。
--创建employee_weiyiji表
--字段:empno,ename,mgr,sal,job,deptno,dname,dloc
CREATE TABLE employee_weiyiji
AS
SELECT e.empno,e.ename,e.mgr,e.sal,e.job,d.deptno,d.dname,d.loc
FROM emp_weiyiji e LEFT OUTER JOIN dept_weiyiji d ON e.deptno=d.deptno
--子查询根据查询结果不同分为:
--单行单列,单行多列:常用在WHERE作为过滤条件
--多行多列:常当做表看待
--当判断的是一个多行单列子查询结果时,通常要配合IN,ANY,ALL使用。
查看与职位是SALESMAN同部门的其他职位员工?
SELECT ename,job,deptno FROM emp_weiyiji
WHERE deptno IN(SELECT deptno FROM emp_weiyiji
WHERE job=‘SALESMAN‘) AND job<>‘SALESMAN‘
查看比SALESMAN和CLERK职位工资都高的员工?
SELECT ename,sal deptno FROM emp_weiyiji
WHERE sal>ALL(SELECT sal FROM emp_weiyiji
WHERE job IN(‘SALESMAN‘,‘CLERK‘))

--EXISTS关键字
--EXISTS在WHERE中使用,后面跟一个子查询,子查询的结果不关注具体指,只关注是否能查询出数据,只要
--可以则条件成立。
查看有员工的部门信息?--查看没有员工部门信息在EXISTS前面加NOT就行了
SELECT deptno,dname FROM dept_weiyiji d
WHERE EXISTS(SELECT * FROM emp_weiyiji e
WHERE d.deptno = e.deptno
)
查询部门的最低薪水,前提是要高于30号部门的最低薪水?
SELECT MIN(sal),deptno FROM emp_weiyiji
GROUP BY deptno
HAVING MIN(sal)>(SELECT MIN(sal) FROM emp_weiyiji
WHERE deptno=30)
--FROM子句中使用子查询,该子查询是当做一张表使用的。
查看比自己所在部门平均工资高的员工?
SELECT e.ename,e.sal,e.deptno
FROM emp_weiyiji e,(SELECT AVG(sal) avg_sal,deptno
FROM emp_weiyiji GROUP BY deptno) t
WHERE e.deptno=t.deptno AND e.sal>t.avg_sal


SELECT e.ename,e.sal,(SELECT d.dname FROM dept_weiyiji d
WHERE d.deptno = e.deptno) dname
FROM emp_weiyiji e

--分页查询
--分页查询其实就是将数据分段查询出来,当数据量大的时候经常会使用分页查询技术,这样可以减少
--资源开销,提高系统响应速度。
--标准SQL中没有定义分页语法,所以不同的数据库的分页语法不一样。ORACLE中使用伪列ROWNUM实现
--ROWNUM是一个伪列,并不存在于任何表中,但是所有的表都可以查询该字段,而该字段的值就是查询
--出来的结果集中的每条记录的行号,从1开始自动递增。ROWNUM字段的值生成是伴随查询进行的过程中的。
--每当可以从表中查询出一条记录,那么ROWNUM的值就是该条查询出来记录的行号。
SELECT ROWNUM ,ename,sal,deptno FROM emp_weiyiji
WHERE ROWNUM >1
由于ROWNNUM的值是查询过程中动态生成的,并从1开始所以在使用ROWNUM对结果集编号时不要使用ROWNUM
在WHERE中做>1以上数字的判断,否则查不到任何数据
SELECT * FROM(SELECT ROWNUM rn,ename,sal,deptno FROM emp_weiyiji)
WHERE rn BETWEEN 6 AND 10
先排序,然后在标号,最后在取需要的部分
SELECT *FROM(SELECT ROWNUM rn,t.*
FROM(SELECT ename,sal,deptno FROM emp_weiyiji
ORDER BY sal DESC) t
WHERE ROWNUM<=10)
WHERE rn>=6
pageSize(每页显示的条目数):5
page(页数):2
start:(page-1)*pageSize+1
end:PageSize*page

--DECODE函数,可以实现简单的分支结构
SELECT ename, job ,sal,
DECODE(job,
‘MANAGER‘,sal * 1.2,
‘ANALYST‘,sal * 1.1,
‘SALESMAN‘,sal* 1.05
)bonus
FROM emp_weiyiji
--和DECODE函数功能相似的有CASE语句,实现类似于if
SELECT ename,job,sal,
CASE job WHEN ‘MANAGER‘THEN sal*1.2
WHEN ‘ANALYST‘THEN sal*1.1
WHEN ‘SALESMAN‘THEN sal*1.05
ELSE sal END
bouns FROM emp_weiyiji;

SELECT COUNT(1),DECODE(job,
‘MANAGER‘,‘VIP‘,
‘ANALYST‘,‘VIP‘,
‘OTHER‘)
FROM emp_weiyiji
GROUP BY DECODE(job,
‘MANAGER‘,‘VIP‘,
‘ANALYST‘,‘VIP‘,
‘OTHER‘)

SELECT deptno,dname,loc FROM dept_weiyiji
ORDER BY DECODE(
dname,‘OPERATIONS‘,1,
‘ACCOUNTING‘,2,
‘SALES‘,3);

--排序函数
--排序函数允许对结果集按照指定字段分组,然后在组内按照指定字段排序,之后为每组的记录编行号
--ROW_NUMBER():生成组内连续且唯一的数字
查看每个部门的工资排名
SELECT ename ,sal,deptno,
ROW_NUMBER() OVER(
PARTITION BY deptno
ORDER BY sal DESC
) rank
FROM emp_weiyiji
--RANK函数:组内不连续也不唯一的数字(排名4,4过了直接就是6)
查看每个部门的工资排名,若工资一致,排名一致
SELECT ename,sal,deptno,
RANK() OVER(
PARTITION BY deptno
ORDER BY sal DESC
) rank
FROM emp_weiyiji;

--DENSE_RANK:生成组内连续但不唯一的数字(排名4,4过了直接就是5)
SELECT ename,sal,deptno,
DENSE_RANK() OVER(
PARTITION BY deptno
ORDER BY sal DESC
) rank
FROM emp_weiyiji


CREATE TABLE sales_weiyiji(
year_id NUMBER NOT NULL,
month_id NUMBER NOT NULL,
day_id NUMBER NOT NULL,
sales_value NUMBER(10,2) NOT NULL
);
INSERT INTO sales_weiyiji
SELECT TRUNC(DBMS_RANDOM.value(2010,2012)) AS year_id,
TRUNC(DBMS_RANDOM.value(1,13)) AS month_id,
TRUNC(DBMS_RANDOM.value(1,32)) AS day_id,
ROUND(DBMS_RANDOM.value(1,100),2) AS sales_value
FROM dual
CONNECT BY level<=1000;
--查看并集,既是‘MANAGER‘,工资又高于2500
SELECT ename,job,sal FROM emp_weiyiji
WHERE job=‘MANAGER‘
UNION
SELECT ename,job,sal FROM emp_weiyiji
WHERE sal>2500;
--全并集
SELECT ename,job,sal FROM emp_weiyiji
WHERE job=‘MANAGER‘
UNION ALL
SELECT ename,job,sal FROM emp_weiyiji
WHERE sal>2500;
--交集,保留即满足‘MANAGER‘,工资又高于2500
SELECT ename,job,sal FROM emp_weiyiji
WHERE job=‘MANAGER‘
INTERSECT
SELECT ename,job,sal FROM emp_weiyiji
WHERE sal>2500
--差集
SELECT ename,job,sal FROM emp_weiyiji
WHERE job=‘MANAGER‘
MINUS
SELECT ename,job,sal FROM emp_weiyiji
WHERE sal>=2500
--ROLLUP(高级分组函数)
--1.ROLLUP
-- GROUP BY ROLLUP(a,b,c)
-- 等价于
-- GROUP BY a,b,c
-- UNION ALL
-- GROUP BY a,b
-- UNION ALL
-- 全表
SELECT
year_id,month_id,day_id,SUM(sales_value)
FROM sales_weiyiji
GROUP BY
ROLLUP(year_id,month_id,day_id)
ORDER BY
year_id,month_id,day_id

--CUBE函数
--CUBE的分组次数是2的参数个数次方,没一种组合都会进行一次分组
SELECT
year_id,month_id,day_id,SUM(sales_value)
FROM sales_weiyiji
GROUP BY
CUBE(year_id,month_id,day_id)
ORDER BY
year_id,month_id,day_id
--GROUPING SETS函数
--该函数允许自定义分组模式,然后将这些分组结果并在一个结果集显示。其中每个参数为一种分组模式。
查看每天与每月的营业额?
SELECT year_id,month_id,day_id,SUM(sales_value)
FROM sales_weiyiji
GROUP BY GROUPING SETS(
(year_id,month_id,day_id),(year_id,month_id))

 

以上是关于Oraclesql的主要内容,如果未能解决你的问题,请参考以下文章