sql 预习

-------游标
select o.sid, oSUSEr, machine, count(*) num_curs
  from v$open_cursor o, v$session s
 where user_name = 'SPMSADMIN'
   and o.sid = s.sid
 group by o.sid, osuser, machine
 order by num_curs desc;

select q.sql_text  
  from v$open_cursor o, v$sql q  
 where q.hash_value = o.hash_value
   and o.sid = 699;

------------------坏块------------
alter database datafile 15 offline;

select file_name,tablespace_name from dba_data_files where tablespace_name='SPMSDATA'; 
  
  select * 
    from dba_data_files 
   where tablespace_name = 'SPMSDATA' 
     and file_name in ('/oradata1/utf8/data/pmsdata05.dbf','/oradata1/utf8/data/pmsdata15.dbf'); 

recover datafile 15; 
alter database datafile 15 online;



可能是该表被某一用户锁定,导致其他用户无法继续操作 

SELECT object_name, machine, s.sid, s.serial# 
FROM gv$locked_object l, dba_objects o, gv$session s 
WHERE l.object_id = o.object_id 
AND l.session_id = s.sid;
--释放SESSION SQL: 
--alter system kill session 'sid, serial#'; 
ALTER system kill session '23, 1647'; 

 

------------------坏块------------
alter database datafile 15 offline;

select file_name,tablespace_name from dba_data_files where tablespace_name='SPMSDATA'; 
  
  select * 
    from dba_data_files 
   where tablespace_name = 'SPMSDATA' 
     and file_name in ('/oradata1/utf8/data/pmsdata05.dbf','/oradata1/utf8/data/pmsdata15.dbf'); 


recover datafile 15; 
alter database datafile 15 online;
------------------------

dbms_output.put_line('数据修改成功...');
Connected to Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 
Connected as spmsadmin@UTF8
 
SQL> begin
  2  dbms_job.run(402);
  3  end;
  4  /
 
PL/SQL procedure successfully completed
 
SQL> 
表内存大小  select segment_name, bytes from user_segments order by bytes desc
--------------------
select count(1) from SPMS_ORDER_SELLCUST_BAK t where t.account_type=2 					-- 3.4s       1021955
select count(1) from SPMS_ORDER_SELLCUST_BAK t where t.project_id='38ba55807f00000128efe371d8dd50c2'	-- 2.7s   924

select count(1) from SPMS_ORDER_SELLCUST_BAK t where t.account_type=2 and t.project_id='38ba55807f00000128efe371d8dd50c2'  --3.42s    	231
select count(1) from SPMS_ORDER_SELLCUST_BAK t where t.project_id='38ba55807f00000128efe371d8dd50c2' and t.account_type=2  --2.7s    	231

过滤效果最好的写where 的最边上
---------------------------------------


低效: (索引失效) mstsc
SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE IS NOT NULL; 
高效: (索引有效) 
SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE >=0;

-----------------------

删除重复记录:
最高效的删除重复记录方法 ( 因为使用了ROWID)例子:
DELETE  FROM  EMP E  WHERE  E.ROWID > (SELECT MIN(X.ROWID) 
FROM  EMP X  WHERE  X.EMP_NO = E.EMP_NO);

--------------------分页---------------
select * from (
  select t1.*, rownum rn  from ( select * from z_user where class_id=1) t1 where rownum <= 5
) where rn >= 2



--------------------------------

减少访问数据库的次数。使用关联查询解决
N+1
条
sql
语句问题
举例: 
select * from CUSTOMERS; 
  
select * from ORDERS where CUSTOMER_ID=1; 
select * from ORDERS where CUSTOMER_ID=2; 
select * from ORDERS where CUSTOMER_ID=3; 
select * from ORDERS where CUSTOMER_ID=4; 
可以采用关联查询为:
  
select * from CUSTOMERS left outer join ORDERS 
on CUSTOMERS.ID=ORDERS.CUSTOMER_ID 

-----------------------------

CASE表达式应用
列出雇员的工资等级
  
select 	ename,
	(case when sal<1000 then '低'  
	when sal>=1000 and sal<3000 then '中' 
 	when sal>3000 then '高' 
	else '未知'  
	end) as "工资等级" 
from emp; 

---------------------------------------------------------

行转列

select 
       sum(case when A.ITEM_SEQ BETWEEN 1 AND 5 THEN 1 ELSE 0 END ) AS A,
       SUM(CASE WHEN A.ITEM_SEQ BETWEEN 6 AND 10 THEN 1 ELSE 0 END) AS B,
       SUM(CASE WHEN A.ITEM_SEQ > 10 THEN 1 ELSE 0 END)
from 
       (SELECT T1.ITEM_SEQ FROM TBL_MENU_ITEM T1 WHERE T1.REPOSITORY_ID='SAMPLE-CUSTDIR' and t1.parent_item_id is null ORDER BY T1.ITEM_SEQ) A

--------------------------------

select 
       SUM(DECODE(SIGN(5-A.ITEM_SEQ) ,1, 1,0,1,0)) A,
       SUM(DECODE(SIGN(10-A.ITEM_SEQ) ,1, 1,0,1,0)) A,
       SUM(DECODE(SIGN(15-A.ITEM_SEQ) ,1, 1,0,1,0)) A
from 
       (SELECT T1.ITEM_SEQ FROM TBL_MENU_ITEM T1 WHERE T1.REPOSITORY_ID='SAMPLE-CUSTDIR' and t1.parent_item_id is null ORDER BY T1.ITEM_SEQ) A



------------------------------
第一 避免对列的操作 
	select * from record where  to_char(ActionTime,’yyyymmdd’)=’19991201′(10秒)
	select * from record where ActionTime= to_date (’19991201′ ,’yyyymmdd’)(< 1秒) 

第二 避免不必要的类型转换 
	表tab1中的列col1是字符型(char),则以下语句存在类型转换: 
	select col1,col2 from tab1 where col1>10, 
	应该写为: select col1,col2 from tab1 where col1>’10′。 

第三 增加查询的范围限制 
	增加查询的范围限制,避免全范围的搜索。 
	例3:以下查询表record 中时间ActionTime小于2001年3月1日的数据: 
       	select * from record where ActionTime < to_date (’20010301′ ,’yyyymm’) 
	查询计划表明,上面的查询对表进行全表扫描,如果我们知道表中的最早的数据为2001年1月1日,那么,可以增加一个最小时间,使查询在一个完整的范围之内。修改如下: select * from record where 
	ActionTime < to_date (’20010301′ ,’yyyymm’) 
	and   ActionTime > to_date (’20010101′ ,’yyyymm’) 
	后一种SQL语句将利用上ActionTime字段上的索引,从而提高查询效率。把‘20010301′换成一个变量,根据取值的机率,可以有一半以上的机会提高效率。同理,对于大于某个值的查询,如果知道当前可能的最大值,也可以在Where子句中加上 “AND 列名< MAX(最大值)”。 

第四 尽量去掉“IN”、“OR” 
	含有“IN”、“OR”的Where子句常会使用工作表,使索引失效;如果不产生大量重复值,可以考虑把子句拆开;拆开的子句中应该包含索引。  
	例4:     select count(*) from stuff where id_no in(’0′,’1′)(23秒) 
	可以考虑将or子句分开:  
	select count(*) from stuff where id_no=’0′ 
	select count(*) from stuff where id_no=’1′ 
	然后再做一个简单的加法,与原来的SQL语句相比,查询速度更快。

第五 尽量去掉 “<>” 

	尽量去掉 “<>”,避免全表扫描,如果数据是枚举值,且取值范围固定,则修改为“OR”方式。 
	例5: 
	UPDATE SERVICEINFO SET STATE=0 WHERE STATE<>0; 
	以上语句由于其中包含了“<>”,执行计划中用了全表扫描(TABLE ACCESS FULL),没有用到state字段上的索引。实际应用中,由于业务逻辑的限制,字段state为枚举值,只能等于0,1或2,而且,值等于=1,2的很少,因此可以去掉“<>”,利用索引来提高效率。 
	修改为:UPDATE SERVICEINFO SET STATE=0  WHERE STATE = 1 OR STATE = 2 。进一步的修改可以参考第4种方法。 


第六 去掉Where子句中的IS NULL和IS NOT NULL 

	Where字句中的IS NULL和IS NOT NULL将不会使用索引而是进行全表搜索,因此需要通过改变查询方式,分情况讨论等方法,去掉Where子句中的IS NULL和IS NOT NULL。 

第七 索引提高数据分布不均匀时查询效率 

	索引的选择性低,但数据的值分布差异很大时,仍然可以利用索引提高效率。A、数据分布不均匀的特殊情况下,选择性不高的索引也要创建。 
	表ServiceInfo中数据量很大,假设有一百万行,其中有一个字段DisposalCourseFlag,取值范围为枚举值:[0,1,2,3,4,5,6,7]。按照前面说的索引建立的规则,“选择性不高的字段不应该建立索引,该字段只有8种取值,索引值的重复率很高,索引选择性明显很低,因此不建索引。然而,由于该字段上数据值的分布情况非常特殊,具体如下表: 
	取值范围  1~5   6   7 
	占总数据量的百分比  1%    98%    1% 
	而且,常用的查询中,查询  DisposalCourseFlag<6 的情况既多又频繁,毫无疑问,如果能够建立索引,并且被应用,那么将大大提高这种情况的查询效率。因此,我们需要在该字段上建立索引。 

第八 利用HINT强制指定索引 

	在ORACLE优化器无法用上合理索引的情况下,利用HINT强制指定索引。 
	继续上面7的例子,ORACLE缺省认定,表中列的值是在所有数据行中均匀分布的,也就是说,在一百万数据量下,每种DisposalCourseFlag值各有12.5万数据行与之对应。假设SQL搜索条件DisposalCourseFlag=2,利用DisposalCourseFlag列上的索引进行数据搜索效率,往往不比全表扫描的高,ORACLE因此对索引“视而不见”,从而在查询路径的选择中,用其他字段上的索引甚至全表扫描。根据我们上面的分析,数据值的分布很特殊,严重的不均匀。为了利用索引提高效率,此时,一方面可以单独对该字段或该表用analyze语句进行分析,对该列搜集足够的统计数据,使ORACLE在查询选择性较高的值时能用上索引;另一方面,可以利用HINT提示,在SELECT关键字后面,加上“/*+ INDEX(表名称,索引名称)*/”的方式,强制ORACLE优化器用上该索引。 
	比如: select * from  serviceinfo where DisposalCourseFlag=1 ; 
	上面的语句,实际执行中ORACLE用了全表扫描,加上蓝色提示部分后,用到索引查询。如下: 
	select /*+  INDEX(SERVICEINFO,IX_S_DISPOSALCOURSEFLAG)  */  * 
	from  serviceinfo where DisposalCourseFlag=1; 
	请注意,这种方法会加大代码维护的难度,而且该字段上索引的名称被改变之后,必须要同步所有指定索引的HINT代码,否则HINT提示将被ORACLE忽略掉。 

第九 屏蔽无用索引 

	继续上面8的例子,由于实际查询中,还有涉及到DisposalCourseFlag=6的查询,而此时如果用上该字段上的索引,将是非常不明智的,效率也极低。因此这种情况下,我们需要用特殊的方法屏蔽该索引,以便ORACLE选择其他字段上的索引。比如,如果字段为数值型的就在表达式的字段名后,添加“+ 0”,为字符型的就并上空串:“||”"” 
	如: select * from  serviceinfo where DisposalCourseFlag+ 0 = 6 and workNo =  ‘36′ 。 
	不过,不要把该用的索引屏蔽掉了,否则同样会产生低效率的全表扫描。 

第十 分解复杂查询,用常量代替变量 

	对于复杂的Where条件组合,Where中含有多个带索引的字段,考虑用IF语句分情况进行讨论;同时,去掉不必要的外来参数条件,减低复杂度,以便在不同情况下用不同字段上的索引。 
	继续上面9的例子,对于包含 
	Where (DisposalCourseFlag < v_DisPosalCourseFlag) or (v_DisPosalCourseFlag is null) and ….的查询,(这里v_DisPosalCourseFlag为一个输入变量,取值范围可能为[NULL,0,1,2,3,4,5,6,7]),可以考虑分情况用IF语句进行讨论,类似: 
	IF v_DisPosalCourseFlag =1 THEN 
	Where DisposalCourseFlag = 1 and …. 
	ELSIF v_DisPosalCourseFlag =2 THEN 
	Where DisposalCourseFlag = 2 and …. 

第十一 like子句尽量前端匹配 

	因为like参数使用的非常频繁,因此如果能够对like子句使用索引,将很高的提高查询的效率。 
	例6:select * from city where name like ‘%S%’ 
	以上查询的执行计划用了全表扫描(TABLE ACCESS FULL),如果能够修改为: 
	select * from city where name like ‘S%’ 
	那么查询的执行计划将会变成(INDEX RANGE SCAN),成功的利用了name字段的索引。这意味着Oracle SQL优化器会识别出用于索引的like子句,只要该查询的匹配端是具体值。因此我们在做like查询时,应该尽量使查询的匹配端是具体值,即使用like ‘S%’。 

第十二 用Case语句合并多重扫描 

	我们常常必须基于多组数据表计算不同的聚集。例如下例通过三个独立查询: 
	例8:1)select count(*) from emp where sal<1000; 
    	 2)select count(*) from emp where sal between 1000 and 5000; 
    	 3)select count(*) from emp where sal>5000; 
	这样我们需要进行三次全表查询,但是如果我们使用case语句: 
	select 
	count (case when sal <1000 
	then 1 else null end)              count_poor, 
	count (case when between 1000 and 5000 
	then 1 else null end)              count_blue_collar, 
	count (case when sal >5000 
	then 1 else null end)              count_poor 
	from emp; 
	这样查询的结果一样,但是执行计划只进行了一次全表查询。 

第十三掌 使用nls_date_format 

	例9: 
	select * from record where  to_char(ActionTime,’mm’)=’12′ 
	这个查询的执行计划将是全表查询,如果我们改变nls_date_format, 
	SQL>alert session set nls_date_formate=’MM’; 
	现在重新修改上面的查询: 
	select * from record where  ActionTime=’12′ 
	这样就能使用actiontime上的索引了,它的执行计划将是(INDEX RANGE SCAN)。 

第十四 使用基于函数的索引 

	前面谈到任何对列的操作都可能导致全表扫描,例如: 
	select * from emp where substr(ename,1,2)=’SM’; 
	但是这种查询在客服系统又经常使用,我们可以创建一个带有substr函数的基于函数的索引, 
	create index emp_ename_substr on eemp ( substr(ename,1,2) ); 
	这样在执行上面的查询语句时,这个基于函数的索引将排上用场,执行计划将是(INDEX RANGE SCAN)。 

第十五 基于函数的索引要求等式匹配 

	上面的例子中,我们创建了基于函数的索引,但是如果执行下面的查询: 
	select * from emp where substr(ename,1,1)=’S’ 
	得到的执行计划将还是(TABLE ACCESS FULL),因为只有当数据列能够等式匹配时,基于函数的索引才能生效,这样对于这种索引的计划和维护的要求都很高。请注意,向表中添加索引是非常危险的操作,因为这将导致许多查询执行计划的变更。然而,如果我们使用基于函数的索引就不会产生这样的问题,因为Oracle只有在查询使用了匹配的内置函数时才会使用这种类型的索引。 

第十六 使用分区索引 

	在用分析命令对分区索引进行分析时,每一个分区的数据值的范围信息会放入Oracle的数据字典中。Oracle可以利用这个信息来提取出那些只与SQL查询相关的数据分区。 
	例如,假设你已经定义了一个分区索引,并且某个SQL语句需要在一个索引分区中进行一次索引扫描。Oracle会仅仅访问这个索引分区,而且会在这个分区上调用一个此索引范围的快速全扫描。因为不需要访问整个索引,所以提高了查询的速度。 

第十七 使用位图索引 

	位图索引可以从本质上提高使用了小于1000个唯一数据值的数据列的查询速度,因为在位图索引中进行的检索是在RAM中完成的,而且也总是比传统的B树索引的速度要快。对于那些少于1000个唯一数据值的数据列建立位图索引,可以使执行效率更快。 


第十八 决定使用全表扫描还是使用索引 


	和所有的秘笈一样,最后一招都会又回到起点,最后我们来讨论一下是否需要建立索引,也许进行全表扫描更快。在大多数情况下,全表扫描可能会导致更多的物理磁盘输入输出,但是全表扫描有时又可能会因为高度并行化的存在而执行的更快。如果查询的表完全没有顺序,那么一个要返回记录数小于10%的查询可能会读取表中大部分的数据块,这样使用索引会使查询效率提高很多。但是如果表非常有顺序,那么如果查询的记录数大于40%时,可能使用全表扫描更快。因此,有一个索引范围扫描的总体原则是: 
	1)对于原始排序的表  仅读取少于表记录数40%的查询应该使用索引范围扫描。反之,读取记录数目多于表记录数的40%的查询应该使用全表扫描。 
	2)对于未排序的表    仅读取少于表记录数7%的查询应该使用索引范围扫描。反之,读取记录数目多于表记录数的7%的查询应该使用全表扫描。 

---------------------------
2010-05-07 13:04 oracle根据存储过程中内容查找存储过程名 在oracle中,对象的源代码是可以从user_source数据字典中

1. 有时候知道对象的某一部分内容,但不知道对象名是什么,可以通过 
select * from user_source s WHERE s.text LIKE '%退出%' 来查找。



2. 
自己写完了存储过程忘了这个过程的内容是什么了,就可以使用oracle的数据词典来查看。具体如下:select text from user_source where name='TEST';注意你的sql语句name的值必须大写,不然oracle不能识别。


-----------------表外连接---------------------------
select * from z_user a left outer join z_class b on a.class_id = b.id
select * from z_user a,z_class b where a.class_id = b.id(+)   

ID	NAME	CLASS_ID	ID	CLASS_NAME
1	3	小C	1	1	one
2	2	小B	1	1	one
3	1	小A	1	1	one
4	6	小F	2	2	two
5	5	小E	2	2	two
6	4	小D	2	2	two
7	9	小I	3	3	three
8	8	小H	3	3	three
9	7	小G	3	3	three
10	10	小J			

select * from z_user a right outer join z_class b on a.class_id = b.id
select * from z_user a,z_class b where a.class_id(+) = b.id


ID	NAME	CLASS_ID	ID	CLASS_NAME
1	1	小A	1	1	one
2	2	小B	1	1	one
3	3	小C	1	1	one
4	4	小D	2	2	two
5	5	小E	2	2	two
6	6	小F	2	2	two
7	7	小G	3	3	three
8	8	小H	3	3	three
9	9	小I	3	3	three
10				4	four 

select * from z_user a full outer join z_class b on a.class_id = b.id

ID	NAME	CLASS_ID	ID	CLASS_NAME
1	3	小C	1	1	one
2	2	小B	1	1	one
3	1	小A	1	1	one
4	6	小F	2	2	two
5	5	小E	2	2	two
6	4	小D	2	2	two
7	9	小I	3	3	three
8	8	小H	3	3	three
9	7	小G	3	3	three
10	10	小J			
11				4	four 
------------------------------------------
set serveroutput on 
SQL> var n varchar2(111);
SQL> exec z_test.findUserById(1,:n)
 
PL/SQL procedure successfully completed
n
---------
小A
 
SQL> 

-----------------START WITH-CONNECT BY-PRIOR--------------------------
SELECT * FROM spms_misc_region r START WITH r.region_id ='4'  CONNECT BY   r.parent_id = PRIOR r.region_id;
SELECT * FROM spms_misc_region r START WITH r.region_id='4'  CONNECT BY  PRIOR r.parent_id =  r.region_id;


看这两个SQL语句

(1)select name from student order by id;

(2)select distinct(name) from student order by id;

执行结果你可能会说:

第1句返回以id排序的所有name字段

第2句返回以id排序的所有不重复的name字段.

但是执行结果不是这样的,第2句会报ORA-01791: not a SELECTed expression

 

原因:

一般来说,order by 的排序字段是可以不出现在select查询字段列表中的,但当查询中使用了DISTINCT或者GROUP BY子句时,order by中的排序字段就必须出现在select列表中,否则就会报上述错误.


第2句应改成  select distinct(name),id from student order by id

或者         select distinct(name) from student order by name



------------------------------------------
SELECT columnName1[,columnName2,…]

FROM tableName1,tableName2,..

[START WITH columnName3] 

CONNECT BY {PRIOR col 1 = col 2 | col 1 = PRIOR col 2}

WHERE columnName4;
上述语法主要用查询表中的树型结构关系

columnName3是根结点的限定语句,当然可以放宽限定条件,以取得多个根结点,实际就是多棵树。

{PRIOR col 1 = col 2 | col 1 = PRIOR col 2}是连接条件

PRIOR表示强制从根节点到叶节点的顺序检索或从强制从叶节点到根节点的顺序检索

columnName4表示过滤条件,是对结果树进行过滤,注意是先有结果树再过滤,不是先过滤再得到结果树

例1:

SELECT * FROM EMP

START WITH EMPNO=7369  

CONNECT BY  PRIOR MGR = EMPNO;

上述查询语句,从根节点empno = 7369开始查询雇员编号和对应的领导编号的关系,PRIOR放在等号的前面表示从根节点到叶节点查询。如果不写明PRIOR表示默认从叶节点到根节点查询。
SELECT *
  FROM spms_misc_industry
where spms_misc_industry.lvl=1
 START WITH spms_misc_industry.industry_id = '55'

CONNECT BY PRIOR
            spms_misc_industry.parent_id = spms_misc_industry.industry_id
            
order by spms_misc_industry.lvl








UPDATE b tt SET (tt.a,tt.b) =(SELECT tti.a,tti.b FROM a tti    WHERE tti.a =tt.a)   全表

Oracle没有update from语法,可以通过两种写法实现同样的功能:
1:子查询UPDATE A SET A.NAME=(SELECT B.NAME FROM B WHERE B.ID=A.ID),本查询要根据具体情况看看是否变通成如下
(1)单列
UPDATE A 

SET A.NAME=(SELECT B.NAME FROM B WHERE B.ID=A.ID) 

WHERE A.ID IN (SELECT ID FROM B);

(2)多列
UPDATE order_rollup 

SET(qty,price)=(SELECT SUM(qty),SUM(price) FROM order_lines WHERE customer_id='KOHL' )

WHERE cust_id='KOHL' AND order_period=TO_DATE('01-Oct-2000')


2:利用视图来做
UPDATE (SELECT A.NAME ANAME,B.NAME BNAME FROM A,B WHERE A.ID=B.ID)
SET ANAME=BNAME;

例如:

UPDATE tablea a
SET a.fieldforupdate = (SELECT b.fieldsource FROM tableb b WHERE a.keyfield = b.keyfield)
WHERE EXISTS (SELECT b.fieldsource FROM tableb b WHERE a.keyfield = b.keyfield)








可能是该表被某一用户锁定,导致其他用户无法继续操作 

SELECT object_name, machine, s.sid, s.serial# 
FROM gv$locked_object l, dba_objects o, gv$session s 
WHERE l.object_id = o.object_id 
AND l.session_id = s.sid;
--释放SESSION SQL: 
--alter system kill session 'sid, serial#'; 
ALTER system kill session '23, 1647'; 


 

你可能感兴趣的:(LearningNotes)