Sunday, August 28, 2022

一, 常用到的设置环境参数的语句 设置SCOTT/TIGER的DEMO 运行@ORACLE_HOME/sqlplus/demo/demobld.sql (响应的demodrop.sql.是DROP SCOTT的脚本) 做一个登陆用的login.sql define _editor=vi set serveroutput on size 1000000 使DBMS_OUTPUT有效. set trimspool on SPOOL不会以定长来控制,而是以空格来控制 set long 5000 LONG或CLOG 显示的长度 set linesize 100 set pagesize 9999 每9999行后打印HEAD column plan_plus_exp format a80 autotrace后explain plan output的格式 column global_name new_value gname set termout off define gname=idle column global_name new_value gname select lower(user) || '@' || substr( global_name, 1, decode( dot, 0, length(global_name), dot-1) ) global_name from (select global_name, instr(global_name,'.') dot from global_name ); set sqlprompt '&gname> ' set termout on set trimspool on; 去除重定向(spool)输出每行的拖尾空格,缺省为off 得到username@dbname的提示符. scott@WWMDB> 二, 常用到的DBA脚本 Runstats 比较两个作相同事情的方法的优劣点。本包统计三个值,一个是时间(不重要,由于手工运行,而且偏差较大),二,使用内存方面,三,LATCH。 开发人员经常在自己的PC上装DB,然后进行开发,这些脚本的目的就是让开发人员可以观察自己的SQL是如何运行的以及效率如何. create or replace view stats as select 'STAT...' || a.name name, b.value from v$statname a, v$mystat b where a.statistic# = b.statistic# union all select 'LATCH.' || name, gets from v$latch; create global temporary table run_stats ( runid varchar2(15), name varchar2(80), value int ) on commit preserve rows; create or replace package runstats_pkg as procedure rs_start; --运行操作之前 procedure rs_middle; --运行第一个操作之后 procedure rs_stop( p_difference_threshold in number default 0 ); --结束 --p_difference_threshold 控制多大差别才予以输出。 end; create or replace package body runstats_pkg as g_start number; --全局变量。 g_run1 number; g_run2 number; procedure rs_start --清空以前的统计信息 is begin delete from run_stats; ---清空 insert into run_stats select 'before', stats.* from stats; g_start := dbms_utility.get_time; end; procedure rs_middle --记录第一个SQL运行时间,STATISTIC,LATCH is begin g_run1 := (dbms_utility.get_time-g_start); insert into run_stats select 'after 1', stats.* from stats; g_start := dbms_utility.get_time; end; procedure rs_stop(p_difference_threshold in number default 0) is begin g_run2 := (dbms_utility.get_time-g_start); dbms_output.put_line ( 'Run1 ran in ' || g_run1 || ' hsecs' ); dbms_output.put_line ( 'Run2 ran in ' || g_run2 || ' hsecs' ); dbms_output.put_line ( 'run 1 ran in ' || round(g_run1/g_run2*100,2) || '% of the time' ); dbms_output.put_line( chr(9) ); insert into run_stats select 'after 2', stats.* from stats; dbms_output.put_line ( rpad( 'Name', 30 ) || lpad( 'Run1', 10 ) || lpad( 'Run2', 10 ) || lpad( 'Diff', 10 ) ); for x in ( select rpad( a.name, 30 ) || to_char( b.value-a.value, '9,999,999' ) || to_char( c.value-b.value, '9,999,999' ) || to_char( ( (c.value-b.value)-(b.value-a.value)), '9,999,999' ) data from run_stats a, run_stats b, run_stats c where a.name = b.name and b.name = c.name and a.runid = 'before' and b.runid = 'after 1' and c.runid = 'after 2' and (c.value-a.value) > 0 and abs( (c.value-b.value) - (b.value-a.value) ) > p_difference_threshold order by abs( (c.value-b.value)-(b.value-a.value)) ) loop dbms_output.put_line( x.data ); end loop; dbms_output.put_line( chr(9) ); dbms_output.put_line ( 'Run1 latches total versus runs -- difference and pct' ); dbms_output.put_line ( lpad( 'Run1', 10 ) || lpad( 'Run2', 10 ) || lpad( 'Diff', 10 ) || lpad( 'Pct', 8 ) ); for x in ( select to_char( run1, '9,999,999' ) || to_char( run2, '9,999,999' ) || to_char( diff, '9,999,999' ) || to_char( round( run1/run2*100,2 ), '999.99' ) || '%' data from ( select sum(b.value-a.value) run1, sum(c.value-b.value) run2, sum( (c.value-b.value)-(b.value-a.value)) diff from run_stats a, run_stats b, run_stats c where a.name = b.name and b.name = c.name and a.runid = 'before' and b.runid = 'after 1' and c.runid = 'after 2' and a.name like 'LATCH%' ) ) loop dbms_output.put_line( x.data ); end loop; end; end; / 测试 SQL> create table wwm as select * from all_objects where 1=2; 表已创建。 SQL> create table wwm2 as select * from all_objects where 1=2; 表已创建。 SQL> exec runstats_pkg.rs_start; PL/SQL 过程已成功完成。 SQL> insert into wwm select * from all_objects; 已创建34750行。 SQL> exec runstats_pkg.rs_middle; PL/SQL 过程已成功完成。 然后用第二种方法,一行一行地插入。 SQL> begin 2 for x in (select * from all_objects) 3 loop 4 insert into wwm2 values x; 5 end loop; 6 commit; 7 end; 8 / ---------------( create procedure p( ENAME in varchar2 ) as begin for x in ( select * from emp where ename = ENAME ) loop Dbms_output.put_line( x.empno ); end loop; end; ----------------- SQL> exec runstats_pkg.rs_stop(1000000) Run1 ran in 3558 hsecs Run2 ran in 14263 hsecs run 1 ran in 24.95% of the time Name Run1 Run2 Diff STAT...session pga memory 0 4,040,516 4,040,516 STAT...session pga memory max 0 4,064,632 4,064,632 STAT...session pga memory max 0 4,064,632 4,064,632 STAT...session pga memory 0 4,106,052 4,106,052 STAT...redo size 3,920,896########## 8,291,688 STAT...redo size 3,920,896########## 8,352,408 Run1 latches total versus runs -- difference and pct Run1 Run2 Diff Pct 305,006 1,462,079 1,157,073 20.86% 三, Mystat 统计某操作之前和之后的变化。 Mystat.sql脚本捕捉初始值 set echo off set verify off column value new_val V define S="&1" 输入 1 的值: redo size --可以输入不同的值来判断不同的指标 set autotrace off select a.name, b.value from v$statname a, v$mystat b where a.statistic# = b.statistic# and lower(a.name) like '%' || lower('&S')||'%' / NAME VALUE ----------------------------- redo size 16408460 set echo on 中间做需要统计的操作。 1* update wwm set object_name=lower(object_name) SQL> / 已更新34750行。 mystat2脚本报告异同点。 set echo off set verify off select a.name, b.value V, to_char(b.value-&V,'999,999,999,999') diff from v$statname a, v$mystat b where a.statistic# = b.statistic# and lower(a.name) like '%' || lower('&S')||'%' NAME V DIFF ------------------------------------ redo size 26028120 9,619,660 可以看到这次UPDATE产生了9,619,660 bytes of redo. 四, Show_space 统计SEGMENT的空间使用具体情况。 create or replace procedure show_space ( p_segname in varchar2, --SGEMENT的NAME p_owner in varchar2 default user, --默任是当前用户 p_type in varchar2 default 'TABLE', --SEGMENT 的TYPE p_partition in varchar2 default NULL ) --PARTITION的NAME -- this procedure uses authid current user so it can query DBA_* -- views using privileges from a ROLE, and so it can be installed -- once per database, instead of once per user who wanted to use it authid current_user as l_free_blks number; l_total_blocks number; l_total_bytes number; l_unused_blocks number; l_unused_bytes number; l_LastUsedExtFileId number; l_LastUsedExtBlockId number; l_LAST_USED_BLOCK number; l_segment_space_mgmt varchar2(255); l_unformatted_blocks number; l_unformatted_bytes number; l_fs1_blocks number; l_fs1_bytes number; l_fs2_blocks number; l_fs2_bytes number; l_fs3_blocks number; l_fs3_bytes number; l_fs4_blocks number; l_fs4_bytes number; l_full_blocks number; l_full_bytes number; -- inline procedure to print out numbers nicely formatted -- with a simple label procedure p( p_label in varchar2, p_num in number ) is begin dbms_output.put_line( rpad(p_label,40,'.') || to_char(p_num,'999,999,999,999') ); end; begin -- this query is executed dynamically in order to allow this procedure -- to be created by a user who has access to DBA_SEGMENTS/TABLESPACES -- via a role as is customary. -- NOTE: at runtime, the invoker MUST have access to these two -- views! -- this query determines if the object is an ASSM object or not begin execute immediate 'select ts.segment_space_management from dba_segments seg, dba_tablespaces ts where seg.segment_name = :p_segname and (:p_partition is null or seg.partition_name = :p_partition) and seg.owner = :p_owner and seg.tablespace_name = ts.tablespace_name' into l_segment_space_mgmt using p_segname, p_partition, p_partition, p_owner; exception when too_many_rows then dbms_output.put_line ( 'This must be a partitioned table, use p_partition => '); return; end; -- if the object is in an ASSM tablespace, we must use this API -- call to get space information, otherwise we use the FREE_BLOCKS -- API for the user-managed segments if l_segment_space_mgmt = 'AUTO' then dbms_space.space_usage ( p_owner, p_segname, p_type, l_unformatted_blocks, l_unformatted_bytes, l_fs1_blocks, l_fs1_bytes, l_fs2_blocks, l_fs2_bytes, l_fs3_blocks, l_fs3_bytes, l_fs4_blocks, l_fs4_bytes, l_full_blocks, l_full_bytes, p_partition); p( 'Unformatted Blocks ', l_unformatted_blocks ); p( 'FS1 Blocks (0-25) ', l_fs1_blocks ); p( 'FS2 Blocks (25-50) ', l_fs2_blocks ); p( 'FS3 Blocks (50-75) ', l_fs3_blocks ); p( 'FS4 Blocks (75-100)', l_fs4_blocks ); p( 'Full Blocks ', l_full_blocks ); else dbms_space.free_blocks( segment_owner => p_owner, segment_name => p_segname, segment_type => p_type, freelist_group_id => 0, free_blks => l_free_blks); p( 'Free Blocks', l_free_blks ); end if; -- and then the unused space API call to get the rest of the -- information dbms_space.unused_space ( segment_owner => p_owner, segment_name => p_segname, segment_type => p_type, partition_name => p_partition, total_blocks => l_total_blocks, total_bytes => l_total_bytes, unused_blocks => l_unused_blocks, unused_bytes => l_unused_bytes, LAST_USED_EXTENT_FILE_ID => l_LastUsedExtFileId, LAST_USED_EXTENT_BLOCK_ID => l_LastUsedExtBlockId, LAST_USED_BLOCK => l_LAST_USED_BLOCK ); p( 'Total Blocks', l_total_blocks ); p( 'Total Bytes', l_total_bytes ); p( 'Total MBytes', trunc(l_total_bytes/1024/1024) ); p( 'Unused Blocks', l_unused_blocks ); p( 'Unused Bytes', l_unused_bytes ); p( 'Last Used Ext FileId', l_LastUsedExtFileId ); p( 'Last Used Ext BlockId', l_LastUsedExtBlockId ); p( 'Last Used Block', l_LAST_USED_BLOCK ); end; SQL> desc show_space PROCEDURE show_space 参数名称 类型 输入/输出默认值? ------------------------------ ----------------------- ------ -------- P_SEGNAME VARCHAR2 IN P_OWNER VARCHAR2 IN DEFAULT P_TYPE VARCHAR2 IN DEFAULT P_PARTITION VARCHAR2 IN DEFAULT ASSM和user space managed tablespaces得到的结果不同,以下用user space managed tablespaces来说明 SQL> exec show_spacE('WWM'); Free Blocks............................. 2 Total Blocks............................ 5,120 Total Bytes............................. 41,943,040 Total MBytes............................ 40 Unused Blocks........................... 13 Unused Bytes............................ 106,496 Last Used Ext FileId.................... 1 Last Used Ext BlockId................... 46,473 Last Used Block......................... 115 FULL BLOCKS: full表示在没有变化之前不会再被分配出去。 Total Blocks, Total Bytes, Total MBytes: 整个分配给此SEGMENT的空间。 Unused Blocks, Unused Bytes:分配给SEGMENT,但是还没有使用,也就是说在HWM之上的空闲块。 Last Used Ext FileId: 最后一个extent所在的FILE ID. Last Used Ext BlockId: 最后一个EXTENT 的BEGIN BLOCK ID. FS1 Blocks–FS4 Blocks: 例如,0-25,表示在0-25%之间的空块. 做一个大表,以供测试使用 create table big_table as select rownum id, a.* from all_objects a where 1=0 / alter table big_table nologging; insert /*+ append */ into big_table select rownum, a.* from all_objects a; 已创建29645行。 1 insert /*+ append */ 2 into big_table 3 select rownum+29646, a.* 4* from all_objects a SQL> / 已创建29645行。 alter table big_table add constraint big_table_pk primary key(id) 1 begin 2 dbms_stats.gather_table_stats 3 ( wnname => 'SYSTEM', 4 tabname => 'BIG_TABLE', 5 method_opt => 'for all indexed columns', 6 cascade => TRUE ); 7* end; 五, 绑定变量与非绑定变量 阅读本章可以参考我的 BIND VARIABLE http://sunmoonking.spaces.live.com/blog/cns!E3BD9CBED01777CA!175.entry 测试表 create table t ( x int ); 绑定变量 create or replace procedure proc1 as begin for i in 1 .. 10000 loop execute immediate 'insert into t values ( :x )' using i; end loop; end; / 非绑定变量 create or replace procedure proc2 as begin for i in 1 .. 10000 loop execute immediate 'insert into t values ( '||i||')'; end loop; end; / SQL> exec runstats_pkg.rs_start; PL/SQL 过程已成功完成。 SQL> exec proc1 PL/SQL 过程已成功完成。 SQL> exec runstats_pkg.rs_middle; PL/SQL 过程已成功完成。 SQL> exec proc2 PL/SQL 过程已成功完成。 SQL> exec runstats_pkg.rs_stop(1000); --差别大于1000的才输出 Run1 ran in 1622 hsecs Run2 ran in 4378 hsecs run 1 ran in 37.05% of the time Name Run1 Run2 Diff STAT...bytes received via SQL* 387 1,690 1,303 STAT...bytes sent via SQL*Net 276 1,798 1,522 STAT...parse count (hard) 5 10,009 10,004 ……………………………………………………………………………. Run1 latches total versus runs -- difference and pct Run1 Run2 Diff Pct 313,680 1,625,995 1,312,315 19.29% PL/SQL 过程已成功完成。 可以看到PRO2基本上是PRO1的三倍,也就是说解析时间占完整运行完一条语句所耗时间的2/3,会多占用LATCH.可见,尽量减少硬解析能明显提高性能. 六,read-consistent SQL> create table wwm as select * from all_users; 表已创建。 SQL> variable x refcursor SQL> begin 2 open :x for select * from wwm; 3 end; 4 / PL/SQL 过程已成功完成。 SQL> delete from wwm; 已删除27行。 SQL> commit; 提交完成。 SQL> print x USERNAME USER_ID ------------------------------------------------------------ ---------- CREATED -------------- BI 60 07-6月 -06 。。。。。。。。。。。。。。。 注意,这是在一个SESSION里做的实验。现建立一个表WWM,再OPEN一个CURSOR,再DELETE表WWM,甚至COMMIT之后还是可以PRINT CURSOR的原来的内容。 但是,需要明白的是,在OPEN一个CURSOR的时候并不取得数据或把数据放到某个地方;而是在DELETE的时候才将你需要的数据给你保留到一个地方(UNDO SEGMENT或叫ROLLBACK SEGMENT)。 七, flashback query. SCN 是ORACLE内部时钟, SQL> variable SCN number SQL> exec :scn:=dbms_flashback.get_system_change_number PL/SQL 过程已成功完成。 SQL> print scn SCN ---------- 1170636 SQL> select count(*) from wwm; COUNT(*) ---------- 0 SQL> desc wwm 名称 是否为空? 类型 ----------------------------------------- -------- ---------------------------- USERNAME NOT NULL VARCHAR2(30) USER_ID NOT NULL NUMBER CREATED NOT NULL DATE SQL> insert into wwm select * from all_users; 已创建27行。 SQL> select count(*) from wwm; COUNT(*) ---------- 27 SQL> select count(*) from wwm as of scn :scn; COUNT(*) ---------- 0 SQL> commit; 提交完成。 SQL> select count(*) from wwm as of scn :scn; COUNT(*) ---------- 0 甚至,我们可以用一个SQL得到两个SCN点的值 SQL> select * from (select count(*) from wwm), 2 (select count(*) from wwm as of scn :scn); COUNT(*) COUNT(*) ---------- ---------- 27 0