Locations of visitors to this page
Showing posts with label sql. Show all posts
Showing posts with label sql. Show all posts

Monday, September 21, 2009

DML Error Logging

DML Error Logging

提问:一个问题, 在存储过程中,有对20万条记录进行修改的一个UPDATE,当中间某个表由于字段长度问题而导致修改失败,在异常处理中,如何定位是哪条记录问题导致失败?

这个无法显示哪行出现超出?你能定位到哪行??
SQL> CREATE OR REPLACE PROCEDURE test111 AS
  2 
  3    v_sqlcode number;
  4    v_sqlerrm varchar2(100);
  5  BEGIN
  6 
  7 
  8  update test set aa=aa||'string' ;
  9      commit;
 10  EXCEPTION
 11      when others then
 12      v_sqlcode:=Sqlcode;
 13      v_sqlerrm:=Sqlerrm;
 14       rollback;
 15  DBMS_OUTPUT.PUT_LINE('AAA' || SQLCODE || SQLERRM || 'START');
 16 
 17  END;
 18  /
 
Procedure created.
 
SQL> set serveroutput on
SQL> exec test111;
AAA-12899ORA-12899: value too large for column "HR"."TEST"."AA" (actual: 11,
maximum: 10)START

回答: 数据操纵语言错误记录(DML Error Logging)能够满足您的要求

运行SQL语句, 如果只有一条记录出错, 也会立刻终止语句运行, 回滚事务, 导致对数据的修改全部失败, 尤其是当用一条语句批量处理大量记录时这个问题更加突出
10gR2增加了DML错误记录功能, 在运行SQL语句发生某些异常时, 不会中断整个事务, 而是自动将错误信息记录到另一个指定的表, 然后继续处理

1. 举例
创建测试表
set serveroutput on size unlimited
set pages 50000 line 130
drop table t purge;
drop table err$_t purge;
create table t(a number(1) primary key, b char);

然后需要建一个记录错误信息的表, 用Oracle提供的DBMS_ERRLOG包自动创建或手工创建
exec dbms_errlog.create_error_log('t');
缺省名称是ERR$_加原表名的前25个字符
SQL> select * from tab;

TNAME                          TABTYPE  CLUSTERID
------------------------------ ------- ----------
T                              TABLE
ERR$_T                         TABLE

SQL> desc ERR$_T
 Name                                                                    Null?    Type
 ----------------------------------------------------------------------- -------- ------------------------------------------------
 ORA_ERR_NUMBER$                                                                  NUMBER
 ORA_ERR_MESG$                                                                    VARCHAR2(2000)
 ORA_ERR_ROWID$                                                                   ROWID
 ORA_ERR_OPTYP$                                                                   VARCHAR2(2)
 ORA_ERR_TAG$                                                                     VARCHAR2(2000)
 A                                                                                VARCHAR2(4000)
 B                                                                                VARCHAR2(4000)

SQL>
ORA_ERR_NUMBER$ 错误编号
ORA_ERR_MESG$ 错误信息
ORA_ERR_ROWID$ 出错行的rowid(只对update和delete)
ORA_ERR_OPTYP$ 错误类型 I:插入 U:更新 D:删除
ORA_ERR_TAG$ 由用户定义的标签
(如果采用手工创建, 必须包括上述字段)
后两个字段与原表对应, 数据类型为varchar2(4000), 用于存储出错的记录(数据类型转换见Table 15-2 Error Logging Table Column Data Types)

用原始方式插入测试数据
SQL> insert into t (a) select level from dual connect by level <= 12;
insert into t (a) select level from dual connect by level <= 12
                               *
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column


字段a只有一位数字, 不能超过9, 插入10导致出错了

增加LOG ERRORS子句后
SQL> insert into t (a) select level from dual connect by level <= 12 log errors reject limit unlimited;

9 rows created.

SQL> select * from t;

         A B
---------- -
         1
         2
         3
         4
         5
         6
         7
         8
         9

9 rows selected.

SQL> col ORA_ERR_MESG$ for a50
SQL> col ORA_ERR_TAG$ for a10
SQL> col ORA_ERR_ROWID$ for a10
SQL> col A for a10
SQL> col b for a10
SQL> select * from err$_t;

ORA_ERR_NUMBER$ ORA_ERR_MESG$                                      ORA_ERR_RO OR ORA_ERR_TA A          B
--------------- -------------------------------------------------- ---------- -- ---------- ---------- ----------
           1438 ORA-01438: value larger than specified precision a            I             10
                llowed for this column

           1438 ORA-01438: value larger than specified precision a            I             11
                llowed for this column

           1438 ORA-01438: value larger than specified precision a            I             12
                llowed for this column


SQL语句运行成功并且将错误记录到了err$_t



2. 语法
Error logging的语法是:
LOG ERRORS [INTO [schema.]table]
[ (simple_expression) ]
[ REJECT LIMIT {integer|UNLIMITED} ]

INTO子句可选, 缺省表名是err$_原表名的前25个字符
simple_expression可以是一个由表达式构成字符串, 作为为标签插入到字段ORA_ERR_TAG$
REJECT LIMIT表示最多允许记录多少个错误, 超过这个范围就抛出异常. 如果是0, 表示不记录错误(见下)


3. REJECT LIMIT 子句
在10.2.0.4测试结果和10gR2文档说的有些出入

缺省情况也记录错误, 只记录一条错误
SQL> truncate table t;

Table truncated.

SQL> truncate table err$_t;

Table truncated.

SQL> insert into t (a) select level from dual connect by level <= 13 log errors;
insert into t (a) select level from dual connect by level <= 13 log errors
                               *
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column


SQL> select * from err$_t;

ORA_ERR_NUMBER$ ORA_ERR_MESG$                                                          ORA_ERR_RO OR ORA_ERR_TA A
--------------- ---------------------------------------------------------------------- ---------- -- ---------- ----------
B
----------
           1438 ORA-01438: value larger than specified precision allowed for this colu            I             10
                mn



SQL>

如果指定了数量n, 会记录n+1条
SQL> truncate table t;

Table truncated.

SQL> truncate table err$_t;

Table truncated.

SQL> insert into t (a) select level from dual connect by level <= 13 log errors reject limit 2;
insert into t (a) select level from dual connect by level <= 13 log errors reject limit 2
                               *
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column


SQL> select * from err$_t;

ORA_ERR_NUMBER$ ORA_ERR_MESG$                                                          ORA_ERR_RO OR ORA_ERR_TA A
--------------- ---------------------------------------------------------------------- ---------- -- ---------- ----------
B
----------
           1438 ORA-01438: value larger than specified precision allowed for this colu            I             10
                mn


           1438 ORA-01438: value larger than specified precision allowed for this colu            I             11
                mn


           1438 ORA-01438: value larger than specified precision allowed for this colu            I             12
                mn



SQL>

11gR1文档似乎修正了这个错误:
This subclause indicates the maximum number of errors that can be encountered before the INSERT statement terminates and rolls back. You can also specify UNLIMITED. The default reject limit is zero, which means that upon encountering the first error, the error is logged and the statement rolls back. For parallel DML operations, the reject limit is applied to each parallel server.

11gR2文档:
If REJECT LIMIT X had been specified, the statement would have failed with the error message of error X=1. The error message can be different for different reject limits. In the case of a failing statement, only the DML statement is rolled back, not the insertion into the DML error logging table. The error logging table will contain X+1 rows.


4. 错误记录表
错误记录表不会自动清除, 以自治事务运行. 如超过REJECT LIMIT限制, DML语句回滚, 错误记录表不回滚
错误记录表所属的用户和运行DML语句的用户可以不相同, 运行语句的用户对错误记录表必须具有插入权限


5. 使用限制
对以下情况记录错误:
  • 列值太大
  • 违反非空,唯一,引用(referential),或检查(check)约束条件
  • 由触发器抛出的异常
  • 数据类型转换错误
  • 分区映射错误
  • 某些merge操作错误(如 ORA-30926: Unable to get a stable set of rows for MERGE operation.)

下述情况不记录错误:
  • 违反了延迟的(deferred)约束条件
  • 空间不够
  • 直接路径插入操作(insert或merge)抛出的唯一约束或唯一索引错误
  • 更新操作(update或merge)抛出的唯一约束或唯一索引错误




外部链接:
DML Error Logging
Error Logging and Handling Mechanisms
Inserting Data with DML Error Logging
38 DBMS_ERRLOG

Faster Batch Processing By Mark Rittman
10gR2 New Feature: DML Error Logging
DML Error Logging in Oracle 10g Database Release 2
Oracle DBMS_ERRLOG
Oracle DML Error Logging
dml error logging in oracle 10g release 2



-fin-

Monday, July 6, 2009

returning clause returning子句

returning clause
returning子句

使用RETURNING子句返回DML语句影响的记录的值
1.
drop table t purge;
create table t(a char, b int);
insert into t values ('a',1);
insert into t values ('b',3);
insert into t values ('c',4);
commit;

var v_a char
var v_b number
insert into t values ('d',2) returning a,b into :v_a,:v_b;
print v_a v_b
update t set b=9 where a='c' returning a,b into :v_a,:v_b;
print v_a v_b
delete t where a='b' returning a,b into :v_a,:v_b;
print v_a v_b

rollback;

SQL> var v_a char
SQL> var v_b number
SQL> insert into t values ('d',2) returning a,b into :v_a,:v_b;

1 row created.

SQL> print v_a v_b

V_A
--------------------------------
d


       V_B
----------
         2

SQL> update t set b=9 where a='c' returning a,b into :v_a,:v_b;

1 row updated.

SQL> print v_a v_b

V_A
--------------------------------
c


       V_B
----------
         9

SQL> delete t where a='b' returning a,b into :v_a,:v_b;

1 row deleted.

SQL> print v_a v_b

V_A
--------------------------------
b


       V_B
----------
         3

SQL>
对于INSERT语句, RETURNING返回的是插入后的值
对于UPDATE语句, RETURNING返回的是变更之后的值
对于DELETE语句, RETURNING返回的是删除前的值



2. RETURNING后面不仅可以是字段, 还可以是一个或多个表达式
var v_a varchar2(50)
var v_b number
var v_b2 number
insert into t values ('d',2) returning rowid,b into :v_a,:v_b;
print v_a v_b
update t set b=b+1 returning sum(b) into :v_b;
print v_b
delete t where a='b' returning rowid,sum(b) into :v_rid,:v_b;
delete t where a in ('b','c') returning min(b),sum(b) into :v_b,:v_b2;
print v_b v_b2
rollback;

SQL> var v_a varchar2(50)
SQL> var v_b number
SQL> var v_b2 number
SQL> insert into t values ('d',2) returning rowid,b into :v_a,:v_b;

1 row created.

SQL> print v_a v_b

V_A
--------------------------------------------------------------------------------------------------------------------------------
AAAFY8AAGAAAACPAAD


       V_B
----------
         2

SQL> update t set b=b+1 returning sum(b) into :v_b;

4 rows updated.

SQL> print v_b

       V_B
----------
        14

SQL> delete t where a='b' returning rowid,sum(b) into :v_rid,:v_b;
delete t where a='b' returning rowid,sum(b) into :v_rid,:v_b
                               *
ERROR at line 1:
ORA-00937: not a single-group group function


SQL> delete t where a in ('b','c') returning min(b),sum(b) into :v_b,:v_b2;

2 rows deleted.

SQL> print v_b v_b2

       V_B
----------
         4


      V_B2
----------
         9

SQL> rollback;

Rollback complete.

SQL>
表达式可以是rowid, 函数, 或是聚集函数(10g新特性)


3. RETURNING BULK COLLECT INTO返回多条记录
如果DML语句影响了多条记录, 而RETURNING子句后也不是聚集函数, 那么使用BULK COLLECT INTO一次返回多条记录
set serveroutpu on size unlimited
select * from t;
declare
  type t_t is table of t%rowtype index by binary_integer;
  v_t_a t_t;
begin
  update t set b=b+1 returning a,b bulk collect into v_t_a;
  for i in 1..v_t_a.count loop
    dbms_output.put_line(i||':a='||v_t_a(i).a||',b='||v_t_a(i).b);
  end loop;
  rollback;
end;
/
SQL> set serveroutpu on size unlimited
SQL> select * from t;

A          B
- ----------
a          1
b          3
c          4

SQL> declare
  2    type t_t is table of t%rowtype index by binary_integer;
  3    v_t_a t_t;
  4  begin
  5    update t set b=b+1 returning a,b bulk collect into v_t_a;
  6    for i in 1..v_t_a.count loop
  7      dbms_output.put_line(i||':a='||v_t_a(i).a||',b='||v_t_a(i).b);
  8    end loop;
  9    rollback;
 10  end;
 11  /
1:a=a,b=2
2:a=b,b=4
3:a=c,b=5

PL/SQL procedure successfully completed.

SQL>


4.使用中的限制

a.聚集函数和非聚集函数表达式不能一起用, 见前面的例子

b.聚集函数中不能用DISTINCT
select * from t;
var v_b number
update t set b=b+1 returning count(distinct b) into :v_b;
print v_b
rollback;
SQL> select * from t;

A          B
- ----------
a          2
b          4
c          5

SQL> var v_b number
SQL> update t set b=b+1 returning count(distinct b) into :v_b;
update t set b=b+1 returning count(distinct b) into :v_b
                             *
ERROR at line 1:
ORA-00934: group function is not allowed here


SQL> print v_b

       V_B
----------


SQL> rollback;

Rollback complete.

SQL>

c.聚集函数不能在INSERT语句里使用
var v_b number
insert into t values ('d',9) returning count(b) into :v_b;
print v_b
rollback;
SQL> var v_b number
SQL> insert into t values ('d',9) returning count(b) into :v_b;
insert into t values ('d',9) returning count(b) into :v_b
                                       *
ERROR at line 1:
ORA-00934: group function is not allowed here


SQL> print v_b

       V_B
----------


SQL> rollback;

Rollback complete.

SQL>

d.如果表达式包含主键字段或非空字段, 且存在BEFORE UPDATE触发器, UPDATE语句会失败
这是文档写的
"If the expr list contains a primary key column or other NOT NULL column, then the update statement fails if the table has a BEFORE UPDATE trigger defined on it."
但实际测试没有发现问题
alter table t modify (b not null);
alter table t modify (a primary key);
create or replace trigger tr_t
  before update on t for each row
begin
  :new.b := 2;
end;
/
var v_b number
var v_a number
update t set b=b+1 returning count(a),sum(b) into :v_b,:v_a;
print v_a v_b

e. INSERT INTO子查询不支持RETURNING
var v_b number
insert into t (select 'g',8 from t) returning sum(b) into v_b;
SQL> insert into t (select 'g',8 from t) returning sum(b) into v_b;
insert into t (select 'g',8 from t) returning sum(b) into v_b
                                    *
ERROR at line 1:
ORA-00933: SQL command not properly ended


SQL>

f.多表插入语句,MERGE语句,并行DML,远程对象,LONG类型,视图,INSTEAD OF触发器均不支持RETURNING
(都没测试过...)




外部链接:
DELETE
INSERT
UPDATE
RETURNING INTO Clause
Examples of Dynamic Bulk Binds


-fin-

Wednesday, June 10, 2009

Asynchronous Commit 异步提交

Asynchronous Commit
Oracle 10gR2+ 的异步提交

Oracle 10gR2开始, 增强了提交的功能, 实现异步/批量的提交


1. 异步提交

默认时提交的步骤:
1) 向系统全局区(SGA)中的重做缓冲区(redo log buffer)中写'事务结束(end of transaction)'记录
2) 通知(发送消息到)写日志进程(LGWR), 告诉它刷新重做缓冲区到磁盘
3) 等待磁盘刷新完成. 这就是常见的'日志文件同步'('log file sync')事件

10gR2 COMMIT增加了新选项:
WAIT: 等待相应的重做信息写到在线重做日志文件中后,提交命令才返回(缺省)
NOWAIT: 不等重做信息写到日志中, 提交命令就返回
IMMEDIATE: 写日志进程立刻写重做信息(缺省). 即强制执行一次磁盘 IO.
BATCH: 将重做信息缓冲起来. 写日志进程到时再写重做信息.

虽然提高了性能, 但是一旦系统宕机, 缓冲区中的已提交事务的重做信息将丢失. 如果遭遇磁盘IO错误, 也会丢失重做信息.



2. COMMIT命令

新的 COMMIT 命令增加了如下选项:
COMMIT WRITE IMMEDIATE|BATCH WAIT|NOWAIT;
见WRITE Clause

COMMIT WRITE NOWAIT: 不做前面提到的第3步
COMMIT WRITE BATCH,NOWAIT: 不做第2和3步, 重做记录保留在缓冲区内, 直到其他人提交导致刷新, 或后台事件导致缓冲区异步的刷新
后台事件如下:
缓冲区1/3满
缓冲区充满1M
每3秒

测试:
CREATE TABLE commit_test (
  id           NUMBER(10),
  description  VARCHAR2(50),
  CONSTRAINT commit_test_pk PRIMARY KEY (id)
);

CONN A/A
SET SERVEROUTPUT ON
DECLARE
  function get_waits(p_event in varchar2) return number
  is
 l_waits  NUMBER;
  begin
 select total_waits
      into l_waits
      from v$session_event
     where event = p_event
       and sid = (select sid from v$mystat where rownum=1);
 return l_waits;
  exception
      when no_data_found then return 0;
  end;
  PROCEDURE do_loop (p_type  IN  VARCHAR2) AS
    l_start  NUMBER;
    l_loops  NUMBER := 1000;
 l_lfs    NUMBER;
  BEGIN
    EXECUTE IMMEDIATE 'TRUNCATE TABLE commit_test';

 l_lfs := get_waits('log file sync');
    l_start := DBMS_UTILITY.get_time;
    FOR i IN 1 .. l_loops LOOP
      INSERT INTO commit_test (id, description)
      VALUES (i, 'Description for ' || i);
     
      CASE p_type
        WHEN ' ' THEN COMMIT;
        WHEN 'WRITE' THEN COMMIT WRITE;
        WHEN 'WRITE WAIT' THEN COMMIT WRITE WAIT;
        WHEN 'WRITE NOWAIT' THEN COMMIT WRITE NOWAIT;
        WHEN 'WRITE BATCH' THEN COMMIT WRITE BATCH;
        WHEN 'WRITE IMMEDIATE' THEN COMMIT WRITE IMMEDIATE;
        WHEN 'WRITE BATCH WAIT' THEN COMMIT WRITE BATCH WAIT;
        WHEN 'WRITE BATCH NOWAIT' THEN COMMIT WRITE BATCH NOWAIT;
        WHEN 'WRITE IMMEDIATE WAIT' THEN COMMIT WRITE IMMEDIATE WAIT;
        WHEN 'WRITE IMMEDIATE NOWAIT' THEN COMMIT WRITE IMMEDIATE NOWAIT;
      END CASE;
    END LOOP;
    DBMS_OUTPUT.put_line(RPAD('COMMIT ' || p_type, 30)
      || ': ' || (DBMS_UTILITY.get_time - l_start)
      || ': ' || (get_waits('log file sync') - l_lfs)
   );
  END;
BEGIN
  do_loop(' ');
  do_loop('WRITE');
  do_loop('WRITE WAIT');
  do_loop('WRITE NOWAIT');
  do_loop('WRITE BATCH');
  do_loop('WRITE IMMEDIATE');
  do_loop('WRITE BATCH WAIT');
  do_loop('WRITE BATCH NOWAIT');
  do_loop('WRITE IMMEDIATE WAIT');
  do_loop('WRITE IMMEDIATE NOWAIT');
END;
/
COMMIT                        : 19: 0
COMMIT WRITE                  : 151: 1000
COMMIT WRITE WAIT             : 150: 1000
COMMIT WRITE NOWAIT           : 20: 0
COMMIT WRITE BATCH            : 151: 1000
COMMIT WRITE IMMEDIATE        : 152: 1000
COMMIT WRITE BATCH WAIT       : 151: 1000
COMMIT WRITE BATCH NOWAIT     : 15: 0
COMMIT WRITE IMMEDIATE WAIT   : 153: 1000
COMMIT WRITE IMMEDIATE NOWAIT : 20: 0
可以看出
1) COMMIT什么参数都不带, 等于NOWAIT, 原因后面讲
2) 参数默认是IMMEDIATE, WAIT
3) BATCH和IMMEDIATE速度差不多(为啥?)
4) WAIT产生等待事件, NOWAIT不产生
5) BATCH+NOWAIT最快, IMMEDIATE+WAIT最慢


3. 系统初始化参数

新增的系统参数是 COMMIT_WRITE
语法: COMMIT_WRITE = '{IMMEDIATE | BATCH},{WAIT |NOWAIT}'
可以在系统级或会话级设置, ALTER SYSTEM, ALTER SESSION
比如:
SQL> show parameter commit

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
commit_point_strength                integer     1
commit_write                         string
max_commit_propagation_delay         integer     0
SQL> alter system set commit_write='batch,nowait';

System altered.

SQL> show parameter commit

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
commit_point_strength                integer     1
commit_write                         string      batch,nowait
max_commit_propagation_delay         integer     0
SQL>

11g取消了COMMIT_WRITE (为了兼容仍保留), 拆分为2个单独的参数 COMMIT_LOGGING 和 COMMIT_WAIT, 分别对应 IMMEDIATE | BATCH 和 WAIT | NOWAIT

测试:
conn a/a
SET SERVEROUTPUT ON
DECLARE
  function get_waits(p_event in varchar2) return number
  is
 l_waits  NUMBER;
  begin
 select total_waits
      into l_waits
      from v$session_event
     where event = p_event
       and sid = (select sid from v$mystat where rownum=1);
 return l_waits;
  exception
      when no_data_found then return 0;
  end;
  PROCEDURE do_loop (p_type  IN  VARCHAR2) AS
    l_start  NUMBER;
    l_loops  NUMBER := 1000;
 l_lfs    NUMBER;
  BEGIN
    if p_type is not null then
    EXECUTE IMMEDIATE 'ALTER SESSION SET COMMIT_WRITE=''' || p_type || '''';
 end if;
    EXECUTE IMMEDIATE 'TRUNCATE TABLE commit_test';

 l_lfs := get_waits('log file sync');
    l_start := DBMS_UTILITY.get_time;
    FOR i IN 1 .. l_loops LOOP
      INSERT INTO commit_test (id, description)
      VALUES (i, 'Description for ' || i);
      COMMIT;
    END LOOP;
    DBMS_OUTPUT.put_line(RPAD('COMMIT_WRITE=' || p_type, 30)
      || ': ' || (DBMS_UTILITY.get_time - l_start)
      || ': ' || (get_waits('log file sync') - l_lfs)
   );
  END;
BEGIN
  do_loop(NULL);
  do_loop('WAIT');
  do_loop('NOWAIT');
  do_loop('BATCH');
  do_loop('IMMEDIATE');
  do_loop('BATCH,WAIT');
  do_loop('BATCH,NOWAIT');
  do_loop('IMMEDIATE,WAIT');
  do_loop('IMMEDIATE,NOWAIT');
END;
/
COMMIT_WRITE=                 : 20: 0
COMMIT_WRITE=WAIT             : 151: 1000
COMMIT_WRITE=NOWAIT           : 19: 0
COMMIT_WRITE=BATCH            : 14: 0
COMMIT_WRITE=IMMEDIATE        : 19: 0
COMMIT_WRITE=BATCH,WAIT       : 150: 1000
COMMIT_WRITE=BATCH,NOWAIT     : 15: 0
COMMIT_WRITE=IMMEDIATE,WAIT   : 153: 1000
COMMIT_WRITE=IMMEDIATE,NOWAIT : 20: 0
第因为在第3步设置了NOWAIT, 所以后面第4,5步也继承了这个配置


4. PLSQL中的优化

PL/SQL 会自动将其中的 COMMIT 优化成为"COMMIT WRITE NOWAIT", 只有最后一次 COMMIT 才是真正的"COMMIT"

conn a/a
set serveroutput on size unlimited
truncate table commit_test;
select total_waits
  from v$session_event
 where event = 'log file sync'
   and sid = (select sid from v$mystat where rownum=1);
declare
  l_loops number := 1000;
begin
  FOR i IN 1 .. l_loops LOOP
    INSERT INTO commit_test (id, description)
    VALUES (i, 'Description for ' || i);
    COMMIT;
  END LOOP;
end;
/
select total_waits
  from v$session_event
 where event = 'log file sync'
   and sid = (select sid from v$mystat where rownum=1);
TOTAL_WAITS
-----------
          1

SQL>   2    3    4    5    6    7    8    9   10
PL/SQL procedure successfully completed.

SQL>   2    3    4
TOTAL_WAITS
-----------
          2

只产生了1次等待事件

10gR2版本以前也发现有异步提交, 见The LGWR dilemma



外部链接:
Commit Enhancements in Oracle 10g Database Release 2
10gR2 New Feature: Asynchronous Commit
On setting commit_write
Quantifying Commit Time
Asynchronous Commit - New Feature in Oracle 10GR2 (10.2)
Expert Oracle Database 11g Administration By Sam R. Alapati


-fin-

Thursday, May 7, 2009

select trigger 查询触发器

select trigger
查询触发器


提问1:有基于SELECT的触发器吗?
回答:没有

提问2:如何使一个查询触发更新操作
回答:如下

两个表:
conn a/a
drop table t1;
drop table t2;
create table t1 (a number, b number);
create table t2 (a number, b number);
truncate table t1;
truncate table t2;
insert into t1 values(1,1);
insert into t1 values(2,2);
commit;

表t2等于
insert into t2
select a, sum(b)-avg(b) from t1 group by a;
commit;
select * from t1;
select * from t2;
SQL> select * from t1;

         A          B
---------- ----------
         1          1
         2          2

SQL> select * from t2;

         A          B
---------- ----------
         1          0
         2          0

SQL>

t1随时更新, 当查询t2时,t2的字段b根据t1更新为最新的值


方法1:
建立视图, 使用自治事务(autonomous transaction)函数更新表和返回值. 程序不直接查询表, 而改查询这个视图
使用自治事务是因为在一个查询里不能有DML操作(insert,update,delete)

create or replace function f_t2(p_a in t1.a%type)
  return t1.b%type
is
  pragma autonomous_transaction;
  v_b t1.b%type;
begin
  select sum(b)-avg(b) into v_b from t1 where a = p_a;
  update t2 set b = v_b where a = p_a;
  commit;
  return v_b;
end;
/
create or replace view v2 as select a, f_t2(a) as b from t2;

测试
select * from v2 where a=1;
select * from t2;
SQL> select * from v2 where a=1;

         A          B
---------- ----------
         1          0

SQL> select * from t2;

         A          B
---------- ----------
         1          0
         2          0

SQL>


t1插入几条数据, 查询v2, 查看结果
insert into t1 values (1,2);
insert into t1 values (2,2);
commit;
select * from t1;
select * from t2;
select * from v2 where a=1;
select * from t2;
select * from v2 where a=2;
select * from t2;
SQL> insert into t1 values (1,2);

1 row created.

SQL> insert into t1 values (2,2);

1 row created.

SQL> commit;

Commit complete.

SQL> select * from t1;

         A          B
---------- ----------
         1          1
         2          2
         1          2
         2          2

SQL> select * from t2;

         A          B
---------- ----------
         1          0
         2          0

SQL> select * from v2 where a=1;

         A          B
---------- ----------
         1        1.5

SQL> select * from t2;

         A          B
---------- ----------
         1        1.5
         2          0

SQL> select * from v2 where a=2;

         A          B
---------- ----------
         2          2

SQL> select * from t2;

         A          B
---------- ----------
         1        1.5
         2          2

SQL>
查询视图v2触发函数运行, 导致t2被更新
查询了哪条记录就更新哪条, 一次只更新一条

清除测试数据
conn a/a
drop function f_t2;
drop view v2;
drop table t2;
drop table t1;


方法2:
使用细粒度审计(Fine-Grained Auditing)调用事件处理模块(handler module)更新表
处理函数也是作为自治事务运行的

set pages 9999 line 140
set serveroutput on size unlimited
conn a/a
drop table t1;
drop table t2;
create table t1 (a number, b number);
create table t2 (a number, b number);
truncate table t1;
truncate table t2;
insert into t1 values(1,1);
insert into t1 values(2,2);
commit;

建立处理函数
conn / as sysdba
create or replace procedure p_update_t2 (
  p_object_schema VARCHAR2,
  p_object_name   VARCHAR2,
  p_policy_name   VARCHAR2
) is
begin
  merge into a.t2
    using (select a, sum(b)-avg(b) b from a.t1 group by a) t3
    on (t2.a = t3.a)
  when matched then
    update set t2.b=t3.b
  when not matched then
    insert values (t3.a, t3.b);
end;
/

添加审计策略
conn / as sysdba
begin
  dbms_fga.drop_policy (
    object_schema    => 'A'
    ,object_name     => 'T2'
    ,policy_name     => 'UPDATE_T2'
  );
end;
/
begin
  dbms_fga.add_policy (
    object_schema    => 'A'
    ,object_name     => 'T2'
    ,policy_name     => 'UPDATE_T2'
    ,audit_column    => 'B'
    ,handler_schema  => 'SYS'
    ,handler_module  => 'P_UPDATE_T2'
  );
end;
/
truncate table fga_log$;
select * from dba_audit_policies;
SQL> select * from dba_audit_policies;

OBJECT_SCHEMA                  OBJECT_NAME                    POLICY_NAME
------------------------------ ------------------------------ ------------------------------
POLICY_TEXT
--------------------------------------------------------------------------------------------------------------------------------------------
POLICY_COLUMN                  PF_SCHEMA                      PF_PACKAGE                     PF_FUNCTION                    ENA SEL INS UPD
------------------------------ ------------------------------ ------------------------------ ------------------------------ --- --- --- ---
DEL AUDIT_TRAIL  POLICY_COLU
--- ------------ -----------
A                              T2                             UPDATE_T2

B                              SYS                                                           P_UPDATE_T2                    YES YES NO  NO
NO  DB+EXTENDED  ANY_COLUMNS


SQL>

handler_schema指定的用户好像必须要和审计策略的拥有者一致, 否则查询时报错:
SQL>  select * from t2 where a=1;
 select * from t2 where a=1
  *
ERROR at line 1:
ORA-06550: line 1, column 9:
PLS-00302: component 'P_UPDATE_T2' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored


SQL>

查询表t2
conn a/a
select * from t1;
select * from t2 where a=1;
SQL> select * from t1;

         A          B
---------- ----------
         1          1
         2          2

SQL> select * from t2 where a=1;

no rows selected

SQL>
这时t2啥也没查出来
其实已经更新了, 也记录了审计日志
conn / as sysdba
select * from a.t2;
col db_user for a10
col os_user for a10
col object_schema for a10
col object_name for a10
col sql_text for a50
select to_char(timestamp,'yyyymmddhh24miss'), db_user, os_user, object_schema, object_name, sql_text from dba_fga_audit_trail;
SQL> select * from a.t2;

         A          B
---------- ----------
         1          0
         2          0

SQL>...
SQL> select to_char(timestamp,'yyyymmddhh24miss'), db_user, os_user, object_schema, object_name, sql_text from dba_fga_audit_trail;

TO_CHAR(TIMEST DB_USER    OS_USER    OBJECT_SCH OBJECT_NAM SQL_TEXT
-------------- ---------- ---------- ---------- ---------- --------------------------------------------------
20090507131441 A          oracle     A          T2         select * from t2 where a=1

SQL>
(使用sys用户查询了表a.t2, 却没有触发审计, 好像可能是因为初始化参数AUDIT_SYS_OPERATIONS设置为false.)
查询到的不是最新的,是不及时的

conn a/a
insert into t1 values (1,2);
insert into t1 values (2,2);
commit;
select * from t1;
select * from t2;
conn / as sysdba
select * from a.t2;
select to_char(timestamp,'yyyymmddhh24miss'), db_user, os_user, object_schema, object_name, sql_text from dba_fga_audit_trail;
SQL> select * from t1;

         A          B
---------- ----------
         1          1
         2          2
         1          2
         2          2

SQL> select * from t2 where a=1;

         A          B
---------- ----------
         1          0
         2          0

SQL>
...
SQL> select * from a.t2;

         A          B
---------- ----------
         1        1.5
         2          2

SQL> select to_char(timestamp,'yyyymmddhh24miss'), db_user, os_user, object_schema, object_name, sql_text from dba_fga_audit_trail;

TO_CHAR(TIMEST DB_USER    OS_USER    OBJECT_SCH OBJECT_NAM SQL_TEXT
-------------- ---------- ---------- ---------- ---------- --------------------------------------------------
20090507131441 A          oracle     A          T2         select * from t2 where a=1
20090507131602 A          oracle     A          T2         select * from t2

SQL>
有2条记录满足审计条件, 只触发了一次
这也会产生不少审计日志, 需要定时清除

清除测试数据
conn / as sysdba
begin
  dbms_fga.drop_policy (
    object_schema    => 'A'
    ,object_name     => 'T2'
    ,policy_name     => 'UPDATE_T2'
  );
end;
/
drop procedure p_update_t2;
drop table a.t2;
drop table a.t1;
truncate table fga_log$;


外部链接:
Fine-Grained Auditing
DBMS_FGA

9i/9.2: Fine Grained Auditing
10g: Fine Grained Auditing

How to cleanup the log table FGA_LOG$ ?
用truncate或delete均可

How To Exclude Users Being Audited Through the DBMS_FGA Package
如何不审计某些用户: 在审计条件中使用SYS_CONTEXT判断当前用户, 比如audit_condition => 'SYS_CONTEXT(''USERENV'',''SESSION_USER'') <> ''TST'' '

How to Avoid Common Flaws and Errors Using Fine Grained Auditing
9i下,如果表没有被分析或没有使用CBO, 会得到意外的审计结果(比预期的要多)





-fin-

Tuesday, April 28, 2009

generate random value in MySQL MySQL中产生随机值

generate random value in MySQL
MySQL里产生随机值

没Oracle好用, 只有有限的几个功能, 除非写函数实现

1. 生成随机数

用rand函数
如, 产生大于等于7,小于12的整数
rand函数产生了一个大于等于0小于1的浮点数
select floor(7+rand()*(12-7));
mysql> select floor(7+rand()*(12-7));
+------------------------+
| floor(7+rand()*(12-7)) |
+------------------------+
|                      9 |
+------------------------+
1 row in set (0.00 sec)

mysql>


2. 产生一个随机字母

用elt(round(rand())+1,'A','a')返回字母'A'或'a', 用ascii函数转换成ascii代码, 即65或97
然后加上用floor(rand()*26)产生的大于等于0小于26的整数, 最后用char函数转换为ascii字符
select char(floor(rand()*26)+ascii(elt(round(rand())+1,'A','a')));
mysql> select char(floor(rand()*26)+ascii(elt(round(rand())+1,'A','a')));
+------------------------------------------------------------+
| char(floor(rand()*26)+ascii(elt(round(rand())+1,'A','a'))) |
+------------------------------------------------------------+
| u                                                          |
+------------------------------------------------------------+
1 row in set (0.00 sec)


或
用floor(1+(rand()*52)产生一个大于等于1小于等于52的数字, 以此用substr函数从大小写英文字符串取出其中一个
select substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',floor(1+(rand()*52)),1);
mysql> select substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',floor(1+(rand()*52)),1);
+---------------------------------------------------------------------------------------+
| substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',floor(1+(rand()*52)),1) |
+---------------------------------------------------------------------------------------+
| y                                                                                     |
+---------------------------------------------------------------------------------------+
1 row in set (0.00 sec)


或
同前, 用elt函数从后面的若干字符串中取出一个, 每个字符串只有一个字符
select elt(floor(1+(rand()*52)),
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
mysql> select elt(floor(1+(rand()*52)),
    -> 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
    -> 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| elt(floor(1+(rand()*52)),
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z') |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| I                                                                                                                                                                                                                                           |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)





3. 生成小写和数字混合的字符串

用rand函数产生一个大于等于0小于1的的浮点数, 然后用md5函数计算它的MD5值, 返回一个由32个16进制数组成的字符串
select md5(rand());
mysql> select md5(rand());
+----------------------------------+
| md5(rand())                      |
+----------------------------------+
| 1450f3993ca13b8b500fe9275d3ee8fa |
+----------------------------------+
1 row in set (0.00 sec)



4. 生成大写和数字混合的字符串

用rand函数产生一个大于等于0小于1的的浮点数, 与一个很大数相乘后取整(floor函数), 然后用conv函数转换成36进制数
(36进制数包括26个英文字母和10个数字)
select conv(floor(rand() * 99999999999999), 10, 36);
mysql> select conv(floor(rand() * 99999999999999), 10, 36);
+----------------------------------------------+
| conv(floor(rand() * 99999999999999), 10, 36) |
+----------------------------------------------+
| T13IANG02                                    |
+----------------------------------------------+
1 row in set (0.00 sec)




外部链接:
Chapter 11. Functions and Operators



-fin-

Wednesday, March 18, 2009

bitmap conversion 位图转换

bitmap conversion
位图转换

在多个字段连接, 没有联合索引, 高基数(high-cardinality)的情况下, 可能会产生位图转换(bitmap conversion)

Jonathan Lewis "Cost-Based Oracle Fundamentals" P456:
B-tree to Bitmap Conversions
One of the optimizer’s strategies is to range scan B-tree indexes to acquire lists of rowids, convert the lists of rowids into the equivalent bitmaps, and perform bitwise operations to identify a small set of rows. Effectively, the optimizer can take sets of rowids from index range scans and convert them to bitmap indexes on the fly before doing an index_combine on the resulting bitmap indexes.
In 8i, only tables with existing bitmap indexes could be subject to this treatment, unless the parameter _b_tree_bitmap_plans had been set to relax the requirement for a preexisting bitmap index.
In 9i, the default value for this parameter changed from false to true—so you may see execution plans involving bitmap conversions after you’ve upgraded, even though you don’t have a single bitmap index in your database. Unfortunately, because of the implicit packing assumption that the optimizer uses for bitmap indexes, this will sometimes be a very bad idea.
As a related issue, this change can make it worth using the minimize_records_per_block option on all your important tables.


比如:
conn a/a
set autot off
drop table t1;
create table t1 as
select floor(dbms_random.value(1,90000)) a,
       floor(dbms_random.value(1,50000)) b,
       floor(dbms_random.value(1,10000)) c,
       cast('1' as char(2000)) x,
       '111111' aa
  from dual
connect by level <= 100000;
create index ind_t1_a on t1(a);
create index ind_t1_b on t1(b);
create index ind_t1_c on t1(c);
analyze table t1 compute statistics for table for all columns for all indexes;
SQL> set autot trace exp stat
SQL> select aa from t1 where (a between 1000 and 3000 or a between 9010 and 9015) and ((b between 3000 and 7000) or (c between 3000 and 9000));

1428 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 2150721541

------------------------------------------------------------------------------------------------------
| Id  | Operation                         | Name     | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |          |  1429 | 24293 |       |  1825   (1)| 00:00:22 |
|   1 |  TABLE ACCESS BY INDEX ROWID      | T1       |  1429 | 24293 |       |  1825   (1)| 00:00:22 |
|   2 |   BITMAP CONVERSION TO ROWIDS     |          |       |       |       |            |          |
|   3 |    BITMAP AND                     |          |       |       |       |            |          |
|   4 |     BITMAP OR                     |          |       |       |       |            |          |
|   5 |      BITMAP CONVERSION FROM ROWIDS|          |       |       |       |            |          |
|   6 |       SORT ORDER BY               |          |       |       |       |            |          |
|*  7 |        INDEX RANGE SCAN           | IND_T1_A |       |       |       |     7   (0)| 00:00:01 |
|   8 |      BITMAP CONVERSION FROM ROWIDS|          |       |       |       |            |          |
|   9 |       SORT ORDER BY               |          |       |       |       |            |          |
|* 10 |        INDEX RANGE SCAN           | IND_T1_A |       |       |       |     2   (0)| 00:00:01 |
|  11 |     BITMAP OR                     |          |       |       |       |            |          |
|  12 |      BITMAP CONVERSION FROM ROWIDS|          |       |       |       |            |          |
|  13 |       SORT ORDER BY               |          |       |       |  1896K|            |          |
|* 14 |        INDEX RANGE SCAN           | IND_T1_C |       |       |       |   127   (0)| 00:00:02 |
|  15 |      BITMAP CONVERSION FROM ROWIDS|          |       |       |       |            |          |
|  16 |       SORT ORDER BY               |          |       |       |   264K|            |          |
|* 17 |        INDEX RANGE SCAN           | IND_T1_B |       |       |       |    19   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   7 - access("A">=1000 AND "A"<=3000)
  10 - access("A">=9010 AND "A"<=9015)
  14 - access("C">=3000 AND "C"<=9000)
  17 - access("B">=3000 AND "B"<=7000)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1560  consistent gets
          0  physical reads
          0  redo size
      25080  bytes sent via SQL*Net to client
       1537  bytes received via SQL*Net from client
         97  SQL*Net roundtrips to/from client
          4  sorts (memory)
          0  sorts (disk)
       1428  rows processed

SQL>
由多个字段索引取得的ROWID转换成位图, 然后进行与或操作, 最后转换回ROWID

修改参数,禁止位图转换
alter session set "_b_tree_bitmap_plans"=false;
SQL> select aa from t1 where (a between 1000 and 3000 or a between 9010 and 9015) and ((b between 3000 and 7000) or (c between 3000 and 9000));

1428 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 4246370027

-----------------------------------------------------------------------------------------
| Id  | Operation                    | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |          |  1301 | 22117 |  2071   (1)| 00:00:25 |
|   1 |  CONCATENATION               |          |       |       |            |          |
|*  2 |   TABLE ACCESS BY INDEX ROWID| T1       |     4 |    68 |     8   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | IND_T1_A |     6 |       |     2   (0)| 00:00:01 |
|*  4 |   TABLE ACCESS BY INDEX ROWID| T1       |  1297 | 22049 |  2063   (1)| 00:00:25 |
|*  5 |    INDEX RANGE SCAN          | IND_T1_A |  2055 |       |     7   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("C">=3000 AND "C"<=9000 OR "B"<=7000 AND "B">=3000)
   3 - access("A">=9010 AND "A"<=9015)
   4 - filter("C">=3000 AND "C"<=9000 OR "B"<=7000 AND "B">=3000)
   5 - access("A">=1000 AND "A"<=3000)
       filter(LNNVL("A"<=9015) OR LNNVL("A">=9010))


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       2354  consistent gets
          0  physical reads
          0  redo size
      25080  bytes sent via SQL*Net to client
       1537  bytes received via SQL*Net from client
         97  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       1428  rows processed

SQL>
位图转换减少了一致性读(consistent gets)的次数, 但增加了一些内存排序(sorts (memory))



奇怪的是, 如果WHERE条件中只查了一个字段, 也可能出现bitmap conversion
alter session set "_b_tree_bitmap_plans"=true;
set autot off
drop table t1;
create table t1 as
select level a,
       cast('1' as char(2000)) x,
       '111111' aa
  from dual
connect by level <= 100000
 order by dbms_random.value;
create index ind_t1_a on t1(a);
analyze table t1 compute statistics for table for all columns for all indexes;
SQL> set autot trace exp stat
SQL> select aa from t1 where a between 1 and 3 or a between 10 and 15;

9 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 768713482

---------------------------------------------------------------------------------------------
| Id  | Operation                        | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |          |     7 |    70 |    13  (16)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID     | T1       |     7 |    70 |    13  (16)| 00:00:01 |
|   2 |   BITMAP CONVERSION TO ROWIDS    |          |       |       |            |          |
|   3 |    BITMAP OR                     |          |       |       |            |          |
|   4 |     BITMAP CONVERSION FROM ROWIDS|          |       |       |            |          |
|   5 |      SORT ORDER BY               |          |       |       |            |          |
|*  6 |       INDEX RANGE SCAN           | IND_T1_A |       |       |     2   (0)| 00:00:01 |
|   7 |     BITMAP CONVERSION FROM ROWIDS|          |       |       |            |          |
|   8 |      SORT ORDER BY               |          |       |       |            |          |
|*  9 |       INDEX RANGE SCAN           | IND_T1_A |       |       |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   6 - access("A">=10 AND "A"<=15)
   9 - access("A">=1 AND "A">=3)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         13  consistent gets
          0  physical reads
          0  redo size
        600  bytes sent via SQL*Net to client
        492  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
          9  rows processed

SQL>
用use_concat提示后变成
SQL> select /*+use_concat*/ aa from t1 where a between 1 and 3 or a between 10 and 15;

9 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 4246370027

-----------------------------------------------------------------------------------------
| Id  | Operation                    | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |          |     7 |    70 |    13   (0)| 00:00:01 |
|   1 |  CONCATENATION               |          |       |       |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| T1       |     2 |    20 |     5   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | IND_T1_A |     2 |       |     2   (0)| 00:00:01 |
|   4 |   TABLE ACCESS BY INDEX ROWID| T1       |     5 |    50 |     8   (0)| 00:00:01 |
|*  5 |    INDEX RANGE SCAN          | IND_T1_A |     5 |       |     2   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("A">=1 AND "A"<=3)
   5 - access("A">=10 AND "A"<=15)
       filter(LNNVL("A"<=3) OR LNNVL("A">=1))


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         14  consistent gets
          0  physical reads
          0  redo size
        600  bytes sent via SQL*Net to client
        492  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          9  rows processed

有必要bitmap conversion吗, 不用它多好啊, 还能少2次内存排序




外部链接:
Execution plan operation shows bitmap conversion from rowids
Optimization of large inlists/multiple OR`s
Using the USE_CONCAT hint with IN/OR Statements
Oracle Database 10g Performance Tuning Tips & Techniques By Richard J. Niemiec
NO_EXPAND Hint
Table 19-3 OPERATION and OPTIONS Values Produced by EXPLAIN PLAN
Sorry I did not phrase my question right



-fin-

Friday, February 20, 2009

generate random string - 产生随机字符串

generate random string - 产生随机字符串



提问: 如何产生随机字符串?

回答: 用dbms_random.string(opt, len)

第一个参数表示字符的类型, 可以是:
'u', 'U' - returning string in uppercase alpha characters 产生大写字符
'l', 'L' - returning string in lowercase alpha characters 产生小写字符
'a', 'A' - returning string in mixed case alpha characters 产生大小写混合字符
'x', 'X' - returning string in uppercase alpha-numeric characters 产生大写字母和数字混合字符
'p', 'P' - returning string in any printable characters. 产生所有可打印的字符(ASCII编码从32-126)
Otherwise the returning string is in uppercase alpha characters. 默认产生大写字符

第二个参数是字符串的长度


举例:
生成12到16个字符长度的包含大小写英文字母和数字的字符串
substr(translate(dbms_random.string('P', 1000)
                 ,'A~!@#$%^&*()_+=-`{}|\][:;"''?/>.<, '
                 ,'A')
       ,1,dbms_random.value(12,16+1))
首先生成一堆可显字符, 再删除其它特殊字符, 得到的就只有大小写和数字了
而且生成了1000个字符, 这样保证删除后的字符串长度也能够满足要求(12-16)
删除字符串使用的是translate函数, 比如translate('12345','a32','a')删除字符'2'和'3'


select substr(translate(dbms_random.string('P', 1000)
                        ,'A~!@#$%^&*()_+=-`{}|\][:;"''?/>.<, '
                        ,'A')
              ,1,dbms_random.value(12,16+1))
  from all_objects
 where rownum <= 10;
SQL> select substr(translate(dbms_random.string('P', 1000)
                        ,'A~!@#$%^&*()_+=-`{}|\][:;"''?/>.<, '
                        ,'A')
              ,1,dbms_random.value(12,16+1))
  from all_objects
 where rownum <= 10;
  2    3    4    5    6
SUBSTR(TRANSLATE(DBMS_RANDOM.STRING('P',1000),'A~!@#$%^&*()_+=-`{}|\][:;"''?/>.<,','A'),1,DBMS_RANDOM.VALUE(12,16+1))
--------------------------------------------------------------------------------------------------------------------------------------------
XQzByZYj33KVS
IyjBGqGPbwT0
dZtwIWc7Iw2y
SFMpCzfdsOJnB4
V0lZTKKhjDwbiV
xQr1bUz5FmmHnQv
HLbfMpxwTkTw
p1wij3yRCsBexU1z
UdiXMgPEzrvN8FC
c57XveMzreePm

10 rows selected.

SQL>




产生随机数字:

产生16位随机数字
select to_char(round(dbms_random.value(0,9999999999999999)),'FM0999999999999999') from dual;
或
select substr(translate(dbms_random.string('X', 1000)
                        ,'-ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                        ,'-')
              ,1,16)
  from dual;





外部连接:
DBMS_RANDOM
ASCII



-fin-

Wednesday, February 18, 2009

how to find the executing subprogram's name 如何找到正在运行的存储过程

how to find the executing subprogram's name
如何找到正在运行的存储过程

10.2.0.3版本以后v$session等视图增加了PLSQL_*_ID字段, 可以显示出会话正在运行的存储过程
10.2.0.3以前,可以查看x$表得到运行的存储过程名



10.2.0.3 中 v$session,v$active_session_history,dba_hist_active_sess_history增加了以下字段
PLSQL_ENTRY_OBJECT_ID,PLSQL_ENTRY_SUBPROGRAM_ID,
PLSQL_OBJECT_ID,PLSQL_SUBPROGRAM_ID
用来查看会话正在运行哪个存储过程

11g文档上的解释
PLSQL_ENTRY_OBJECT_ID  NUMBER  Object ID of the top-most PL/SQL subprogram on the stack; NULL if there is no PL/SQL subprogram on the stack
PLSQL_ENTRY_SUBPROGRAM_ID  NUMBER  Subprogram ID of the top-most PL/SQL subprogram on the stack; NULL if there is no PL/SQL subprogram on the stack
PLSQL_OBJECT_ID  NUMBER  Object ID of the currently executing PL/SQL subprogram; NULL if executing SQL
PLSQL_SUBPROGRAM_ID  NUMBER  Subprogram ID of the currently executing PL/SQL object; NULL if executing SQL

关联all_procedures的字段object_id和subprogram_id可以查出存储过程名

1.
建存储过程p_test3, 调用p_test2, 调用p_test, 调用dbms_lock.sleep
conn / as sysdba
grant execute on dbms_lock to a;
conn a/a
set pages 50000 line 160
set serveroutput on
create or replace procedure p_test
is
begin
  dbms_lock.sleep(60);
end;
/
show err
create or replace procedure p_test2
is
begin
  p_test;
end;
/
show err
create or replace procedure p_test3
is
begin
  p_test2;
end;
/
show err
exec p_test3
SQL> conn / as sysdba
Connected.
SQL> grant execute on dbms_lock to a;

Grant succeeded.

SQL> conn a/a
Connected.
SQL> set pages 50000 line 160
SQL> set serveroutput on
SQL> create or replace procedure p_test
is
begin
  dbms_lock.sleep(60);
end;
/
show err
  2    3    4    5    6
Procedure created.

SQL> No errors.
SQL> create or replace procedure p_test2
is
begin
  p_test;
end;
/
show err
  2    3    4    5    6
Procedure created.

SQL> No errors.
SQL> create or replace procedure p_test3
is
begin
  p_test2;
end;
/
show err
  2    3    4    5    6
Procedure created.

SQL> No errors.
SQL> exec p_test3

赶紧打开一新的会话
conn / as sysdba
set pages 500 linesize 160
col calling_code for a30
col username for a20
col sqltext for a40
select s.sid, s.username,
       p1.object_name ||' '|| p1.procedure_name || ' ' ||
       p2.object_name ||' '|| p2.procedure_name
         "calling_code",
       s.sql_id,
       substr(st.sql_text,1,40) sqltext
  from v$session  s,
       all_procedures p1,
       all_procedures p2,
       v$sql st
 where s.plsql_entry_object_id  = p1.object_id (+)
   and s.plsql_entry_subprogram_id = p1.subprogram_id (+)
   and s.plsql_object_id   = p2.object_id (+)
   and s.plsql_subprogram_id  = p2.subprogram_id (+)
   and s.sql_id = st.sql_id(+)
 order by 1,2
/
SQL> conn / as sysdba
Connected.
SQL> set pages 500 linesize 160
SQL> col calling_code for a30
SQL> col username for a20
SQL> col sqltext for a40
SQL> select s.sid, s.username,
  2         p1.object_name ||' '|| p1.procedure_name || ' ' ||
  3         p2.object_name ||' '|| p2.procedure_name
  4           "calling_code",
  5         s.sql_id,
  6         substr(st.sql_text,1,40) sqltext
  7    from v$session  s,
  8         all_procedures p1,
  9         all_procedures p2,
 10         v$sql st
 11   where s.plsql_entry_object_id  = p1.object_id (+)
 12         and s.plsql_entry_subprogram_id = p1.subprogram_id (+)
 13         and s.plsql_object_id   = p2.object_id (+)
 14         and s.plsql_subprogram_id  = p2.subprogram_id (+)
 15         and s.sql_id = st.sql_id(+)
 16   order by 1,2
 17  /

       SID USERNAME             calling_code                   SQL_ID        SQLTEXT
---------- -------------------- ------------------------------ ------------- ----------------------------------------
      1623 A                    P_TEST3  DBMS_LOCK SLEEP       0hp250x22fzbz BEGIN p_test3; END;
      1627 SYS                                                 11rw4hjaa25xf select s.sid, s.username,        p1.obje
      1633
      1635                                                     4gd6b1r53yt88
      1636
      1640
      1641
      1645
      1646                                                     4gd6b1r53yt88
      1647
      1648
      1649
      1650
      1651
      1652
      1653
      1654
      1655

18 rows selected.

SQL>
存储过程是嵌套调用的, 第一级调用的是P_TEST2, 最后一级是DBMS_LOCK.SLEEP


2. 查询ASH
查询活动会话历史调用了什么存储过程
conn / as sysdba
set pages 500 linesize 160
col calling_code for a70
select p1.object_name ||' '|| p1.procedure_name || ' ' ||
       p2.object_name ||' '|| p2.procedure_name
         "calling_code",
       s.sql_id,
       count(*)
  from v$active_session_history  s,
       all_procedures p1,
       all_procedures p2,
       v$sql st
 where s.plsql_entry_object_id  = p1.object_id (+)
   and s.plsql_entry_subprogram_id = p1.subprogram_id (+)
   and s.plsql_object_id   = p2.object_id (+)
   and s.plsql_subprogram_id  = p2.subprogram_id (+)
   and s.sql_id = st.sql_id(+)
   and s.sample_time > sysdate - &minutes/(60*24)
 group by p1.object_name, p1.procedure_name,
       p2.object_name, p2.procedure_name,
       s.sql_id
 order by count(*)
/
SQL> conn / as sysdba
Connected.
SQL> set pages 500 linesize 160
SQL> col calling_code for a70
SQL> select p1.object_name ||' '|| p1.procedure_name || ' ' ||
  2         p2.object_name ||' '|| p2.procedure_name
  3           "calling_code",
  4         s.sql_id,
  5         count(*)
  6    from v$active_session_history  s,
  7         all_procedures p1,
  8         all_procedures p2,
  9         v$sql st
 10   where s.plsql_entry_object_id  = p1.object_id (+)
 11         and s.plsql_entry_subprogram_id = p1.subprogram_id (+)
 12         and s.plsql_object_id   = p2.object_id (+)
 13         and s.plsql_subprogram_id  = p2.subprogram_id (+)
 14         and s.sql_id = st.sql_id(+)
 15         and s.sample_time > sysdate - &minutes/(60*24)
 16   group by p1.object_name, p1.procedure_name,
 17            p2.object_name, p2.procedure_name,
 18            s.sql_id
 19  order by count(*)
 20  /
Enter value for minutes: 60*24
old  15:        and s.sample_time > sysdate - &minutes/(60*24)
new  15:        and s.sample_time > sysdate - 60*24/(60*24)

calling_code                                                           SQL_ID          COUNT(*)
---------------------------------------------------------------------- ------------- ----------
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC                              cqjwytk1ghamg          1
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC                              4y1y43113gv8f          1
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC                              11zqjqwdjb6tp          1
                                                                       b2kf2cxf30jh3          1
DBMS_SPACE AUTO_SPACE_ADVISOR_JOB_PROC DBMS_SPACE OBJECT_GROWTH_TREND_ 3h4d1uux6kd0x          1
CURTAB

DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC                              b6usrg82hwsa3          1
                                                                       fskdwb6ppw164          1
DBMS_SPACE AUTO_SPACE_ADVISOR_JOB_PROC                                 ffrxyztt4415k          1
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC                              1fp87jmavgnvs          1
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC                              402690djv4v60          1
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC                              g5kzvfybhsr96          1
P_TEST2                                                                dvt1n1r9p84vj          1
DBMS_SPACE AUTO_SPACE_ADVISOR_JOB_PROC                                 0jrz5kc6a3jry          1
DBMS_SPACE AUTO_SPACE_ADVISOR_JOB_PROC                                 cvn54b7yz0s8u          1
                                                                       32hbap2vtmf53          1
                                                                       9zmwy5hkhq8h7          1
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC DBMS_SYS_SQL PARSE           3j1qd2tnzd26w          1
PRVT_HDM AUTO_EXECUTE                                                                         1
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC                              8wpwh54q0y4ky          1
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC STANDARD SYSDATE             b6usrg82hwsa3          1
MGMT_CONFIG COLLECT_CONFIG                                             cvn54b7yz0s8u          1
PRVT_ADVISOR DELETE_EXPIRED_TASKS                                                             1
                                                                       9babjv8yq8ru3          1
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC                              b4anb2n74m7rv          1
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC                              5ps3p5ma94bkh          2
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC                              0ybwd63u2any5          2
                                                                       bh0cvm22fks6k          2
                                                                       4gd6b1r53yt88          3
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC                              cydgw456rs7z0          3
DBMS_STATS GATHER_DATABASE_STATS_JOB_PROC                              8a1pvy4cy8hgv          7
                                                                       3vaa5k7us1n4g          8
                                                                       0syfh8fyz2x2g        122
P_TEST2  P_TEST                                                        dvt1n1r9p84vj        232
                                                                                            930

34 rows selected.

SQL>


3. 用触发器捕获存储过程名
conn / as sysdba
grant select on sys.v_$session to a;
grant select on sys.v_$sql to a;
conn a/a
drop table t1;
create table t1 (a int);
create or replace procedure p_test
is
begin
  insert into t1 values (1);
end;
/
show err
create or replace procedure p_test2
is
begin
  p_test;
commit;
end;
/
show err
create or replace trigger tr_t1
  after insert or update on t1
  for each row
declare
  v_sid int;
  v_usr varchar2(30);
  v_po1 varchar2(30);
  v_ps1 varchar2(30);
  v_po2 varchar2(30);
  v_ps2 varchar2(30);
  v_sql varchar2(40);
begin
  select s.sid, s.username,
         p1.object_name, p1.procedure_name,
         p2.object_name, p2.procedure_name,
         substr(sql.sql_text,1,40)
    into v_sid,v_usr,v_po1,v_ps1,v_po2,v_ps2,v_sql
    from v$session s, all_procedures p1, all_procedures p2, v$sql sql
   where s.sid = userenv('sid')
     and s.plsql_entry_object_id = p1.object_id(+)
     and s.plsql_entry_subprogram_id = p1.subprogram_id(+)
     and s.plsql_object_id = p2.object_id(+)
     and s.plsql_subprogram_id = p2.subprogram_id(+)
     and s.sql_id = sql.sql_id
    ;
    dbms_output.put_line('sid:'||v_sid);
    dbms_output.put_line('usr:'||v_usr);
    dbms_output.put_line('po1:'||v_po1);
    dbms_output.put_line('ps1:'||v_ps1);
    dbms_output.put_line('po2:'||v_po2);
    dbms_output.put_line('ps2:'||v_ps2);
    dbms_output.put_line('sql:'||v_sql);
end;
/
show err
set serveroutput on
exec p_test2
SQL> conn / as sysdba
Connected.
SQL> grant select on sys.v_$session to a;

Grant succeeded.

SQL> grant select on sys.v_$sql to a;

Grant succeeded.

SQL> conn a/a
Connected.
SQL> drop table t1;

Table dropped.

SQL> create table t1 (a int);

Table created.

SQL> create or replace procedure p_test
is
begin
  insert into t1 values (1);
end;
/
show err
  2    3    4    5    6
Procedure created.

SQL> No errors.
SQL> create or replace procedure p_test2
is
begin
  p_test;
  commit;
end;
/
show err
  2    3    4    5    6    7
Procedure created.

SQL> No errors.
SQL> create or replace trigger tr_t1
  2    after insert or update on t1
  3    for each row
  4  declare
  5    v_sid int;
  6    v_usr varchar2(30);
  7    v_po1 varchar2(30);
  8    v_ps1 varchar2(30);
  9    v_po2 varchar2(30);
 10    v_ps2 varchar2(30);
 11    v_sql varchar2(40);
 12  begin
 13    select s.sid, s.username,
 14           p1.object_name, p1.procedure_name,
 15           p2.object_name, p2.procedure_name,
 16           substr(sql.sql_text,1,40)
 17      into v_sid,v_usr,v_po1,v_ps1,v_po2,v_ps2,v_sql
 18      from v$session s, all_procedures p1, all_procedures p2, v$sql sql
 19     where s.sid = userenv('sid')
 20       and s.plsql_entry_object_id = p1.object_id(+)
 21       and s.plsql_entry_subprogram_id = p1.subprogram_id(+)
 22       and s.plsql_object_id = p2.object_id(+)
 23       and s.plsql_subprogram_id = p2.subprogram_id(+)
 24       and s.sql_id = sql.sql_id
 25    ;
 26    dbms_output.put_line('sid:'||v_sid);
 27    dbms_output.put_line('usr:'||v_usr);
 28    dbms_output.put_line('po1:'||v_po1);
 29    dbms_output.put_line('ps1:'||v_ps1);
 30    dbms_output.put_line('po2:'||v_po2);
 31    dbms_output.put_line('ps2:'||v_ps2);
 32    dbms_output.put_line('sql:'||v_sql);
 33  end;
 34  /
show err

Trigger created.

SQL> No errors.
SQL> set serveroutput on
SQL> exec p_test2
sid:1623
usr:A
po1:P_TEST2
ps1:
po2:
ps2:
sql:SELECT S.SID, S.USERNAME, P1.OBJECT_NAME

PL/SQL procedure successfully completed.

SQL>


4.例子:用SYSTEM用户触发器记录存储过程名

conn / as sysdba
revoke select on sys.v_$session from a;
revoke select on sys.v_$sql from a;
conn a/a
drop table t1;
create table t1 (a int);
create or replace procedure p_test
is
begin
  insert into t1 values (1);
end;
/
show err
create or replace procedure p_test2
is
begin
  p_test;
  commit;
end;
/
show err
drop trigger tr_t1;

conn / as sysdba
grant select on sys.v_$session to system;
grant select on dba_procedures to system;
grant select on sys.v_$sql to system;
conn system/manager
drop table t_a;
create table t_a (
  sid int not null,
  username varchar2(30),
  plsql_entry_obj_name varchar2(40),
  plsql_entry_sub_name varchar2(40),
  plsql_obj_name varchar2(40),
  plsql_sub_name varchar2(40),
  sqltext varchar2(100),
  presqltext varchar2(100),
  timestamp date default sysdate not null
)
/
create or replace trigger tr_t1
  before insert or update on a.t1
  for each row
declare
begin
  insert into t_a (
         sid, username,
         plsql_entry_obj_name, plsql_entry_sub_name,
         plsql_obj_name, plsql_sub_name, sqltext, presqltext)
  select s.sid, s.username
         ,p1.object_name, p1.procedure_name
         ,p2.object_name, p2.procedure_name
         ,substr(sql.sql_text,1,100)
         ,substr(presql.sql_text,1,100)
    from v$session s
         ,dba_procedures p1, dba_procedures p2
         ,v$sql sql, v$sql presql
   where s.sid = userenv('sid')
     and s.plsql_entry_object_id = p1.object_id(+)
     and s.plsql_entry_subprogram_id = p1.subprogram_id(+)
     and s.plsql_object_id = p2.object_id(+)
     and s.plsql_subprogram_id = p2.subprogram_id(+)
     and s.sql_id = sql.sql_id(+)
     and s.prev_sql_id = presql.sql_id(+);
end;
/
show err
SQL> conn / as sysdba
Connected.
SQL> grant select on sys.v_$session to system;

Grant succeeded.

SQL> grant select on dba_procedures to system;

Grant succeeded.

SQL> grant select on sys.v_$sql to system;

Grant succeeded.

SQL> conn system/manager
Connected.
SQL> drop table t_a;

Table dropped.

SQL> create table t_a (
  2   sid int not null,
  3   username varchar2(30),
  4   plsql_entry_obj_name varchar2(40),
  5   plsql_entry_sub_name varchar2(40),
  6   plsql_obj_name varchar2(40),
  7   plsql_sub_name varchar2(40),
  8   sqltext varchar2(100),
  9   presqltext varchar2(100),
 10   timestamp date default sysdate not null
 11  )
 12  /

Table created.

SQL> create or replace trigger tr_t1
  2    before insert or update on a.t1
  3    for each row
  4  declare
  5  begin
  6    insert into t_a (
  7           sid, username,
  8           plsql_entry_obj_name, plsql_entry_sub_name,
  9           plsql_obj_name, plsql_sub_name, sqltext, presqltext)
 10    select s.sid, s.username
 11           ,p1.object_name, p1.procedure_name
 12           ,p2.object_name, p2.procedure_name
 13           ,substr(sql.sql_text,1,100)
 14           ,substr(presql.sql_text,1,100)
 15      from v$session s
 16           ,dba_procedures p1, dba_procedures p2
 17           ,v$sql sql, v$sql presql
 18     where s.sid = userenv('sid')
 19       and s.plsql_entry_object_id = p1.object_id(+)
 20       and s.plsql_entry_subprogram_id = p1.subprogram_id(+)
 21       and s.plsql_object_id = p2.object_id(+)
 22       and s.plsql_subprogram_id = p2.subprogram_id(+)
 23       and s.sql_id = sql.sql_id(+)
 24       and s.prev_sql_id = presql.sql_id(+);
 25  end;
 26  /
show err

Trigger created.

SQL> No errors.
SQL>

用户A运行p_test2和insert语句
conn a/a
exec p_test2
insert into t1 values (2);
commit;
SQL> conn a/a
Connected.
SQL> exec p_test2

PL/SQL procedure successfully completed.

SQL> insert into t1 values (2);
commit;

1 row created.

SQL>
Commit complete.

SQL>

SYSTEM查询
conn system/manager
select * from t_a;
SQL> select * from t_a;

    SID USERNAME   PLSQL_ENTRY_OBJ_NAME                     PLSQL_ENTRY_SUB_NAME
------- ---------- ---------------------------------------- ----------------------------------------
PLSQL_OBJ_NAME                           PLSQL_SUB_NAME                           SQLTEXT
---------------------------------------- ---------------------------------------- ----------------------------------------
PRESQLTEXT                                                                                           TIMESTAMP
---------------------------------------------------------------------------------------------------- ------------------
   1623 A          P_TEST2
                                                                                  INSERT INTO T_A ( SID, USERNAME, PLSQL_E
                                                                                  NTRY_OBJ_NAME, PLSQL_ENTRY_SUB_NAME, PLS
                                                                                  QL_OBJ_NAME, PLSQL_S
SELECT DECODE('A','A','1','2') FROM DUAL                                                             18-FEB-09

   1623 A          TR_T1
                                                                                  insert into t1 values (2)
BEGIN p_test2; END;                                                                                  18-FEB-09


SQL>
第1条记录: 调用存储过程插表,触发了SYSTEM的触发器, 存储过程名是P_TEST2
第2条记录: 运行SQL语句插表,触发了SYSTEM的触发器, 存储过程名是触发器名TR_T1


5. 使用dbms_utility.format_call_stack显示PL/SQL调用栈的信息

修改4
conn a/a
drop table t1;
create table t1 (a int);
create or replace procedure p_test
is
begin
  insert into t1 values (1);
end;
/
show err
create or replace procedure p_test2
is
begin
  p_test;
  commit;
end;
/
show err

conn / as sysdba
grant select on sys.v_$session to system;
grant select on dba_procedures to system;
grant select on sys.v_$sql to system;
conn system/manager
drop table t_a;
create table t_a (
  sid int not null,
  audsid int not null,
  username varchar2(30),
  plsql_entry_obj_name varchar2(40),
  plsql_entry_sub_name varchar2(40),
  plsql_obj_name varchar2(40),
  plsql_sub_name varchar2(40),
  sqltext varchar2(100),
  presqltext varchar2(100),
  call_stack varchar2(2000),
  timestamp date default sysdate not null
)
/
create or replace trigger tr_t1
  before insert or update on a.t1
  for each row
declare
begin
  insert into t_a (
         sid, audsid ,username
         ,plsql_entry_obj_name, plsql_entry_sub_name
         ,plsql_obj_name, plsql_sub_name, sqltext, presqltext
         ,call_stack)
  select s.sid, s.audsid, s.username
         ,p1.object_name, p1.procedure_name
         ,p2.object_name, p2.procedure_name
         ,substr(sql.sql_text,1,100)
         ,substr(presql.sql_text,1,100)
         ,substr(dbms_utility.format_call_stack,1,2000)
    from v$session s
         ,dba_procedures p1, dba_procedures p2
         ,v$sql sql, v$sql presql
   where s.audsid = userenv('sessionid')
     and s.plsql_entry_object_id = p1.object_id(+)
     and s.plsql_entry_subprogram_id = p1.subprogram_id(+)
     and s.plsql_object_id = p2.object_id(+)
     and s.plsql_subprogram_id = p2.subprogram_id(+)
     and s.sql_id = sql.sql_id(+)
     and s.prev_sql_id = presql.sql_id(+);
end;
/
show err

conn a/a
exec p_test2
insert into t1 values (2);
commit;

conn system/manager
select * from t_a;
SQL> select * from t_a;

    SID     AUDSID USERNAME   PLSQL_ENTRY_OBJ_NAME                     PLSQL_ENTRY_SUB_NAME
------- ---------- ---------- ---------------------------------------- ----------------------------------------
PLSQL_OBJ_NAME                           PLSQL_SUB_NAME
---------------------------------------- ----------------------------------------
SQLTEXT
----------------------------------------------------------------------------------------------------
PRESQLTEXT
----------------------------------------------------------------------------------------------------
CALL_STACK
--------------------------------------------------------------------------------------------------------------------------------------------
TIMESTAMP
------------------
   1627     400061 A          P_TEST2
TR_T1
INSERT INTO T1 VALUES (1)

----- PL/SQL Call Stack -----
  object      line  object
  handle    number  name
0xcfef6008         1  anonymous block
0xcff032f0         3  SYSTEM.TR_T1
0xde5c57f0         4  procedure A.P_TEST
0xde5ac390         4  procedure A.P_TEST2
0xde5a3000         1  anonymous block
18-FEB-09

   1627     400061 A          TR_T1

insert into t1 values (2)
BEGIN p_test2; END;
----- PL/SQL Call Stack -----
  object      line  object
  handle    number  name
0xcfef6008         1  anonymous block
0xcff032f0         3  SYSTEM.TR_T1
18-FEB-09


SQL>


6. 查询x$表得到正在运行的存储过程名
conn / as sysdba
grant execute on dbms_lock to a;
conn a/a
set pages 50000 line 160
set serveroutput on
create or replace procedure p_test
is
begin
  dbms_lock.sleep(60);
end;
/
show err
create or replace procedure p_test2
is
begin
  p_test;
end;
/
show err
create or replace procedure p_test3
is
begin
  p_test2;
end;
/
show err
drop table t1;
create table t1 (a int);
create or replace trigger tr_t1
  before insert on t1
  for each row
declare
begin
  p_test3;
end;
/
show err
insert into t1 values (1);

conn / as sysdba
set pages 50000 line 140
col owner for a10
col name for a20
col sid for 999999
col username for a10
col program for a20
col module for a20
col action for a20
col client_info for a20
select decode(o.kglobtyp,
              7, 'PROCEDURE',
              8, 'FUNCTION',
              9, 'PACKAGE',
              12, 'TRIGGER',
              13, 'CLASS')      "TYPE",
       substr(o.kglnaown,1,20)  "OWNER",
       substr(o.kglnaobj,1,35)  "NAME",
       s.indx     "SID",
       s.ksuseser "SERIAL",
       s.ksuudnam "USERNAME",
       s.ksuseapp "PROGRAM",
       x.app      "MODULE",
       x.act      "ACTION",
       x.clinfo   "CLIENT_INFO"
  from sys.x$kglob  o,
       sys.x$kglpn  p,
       sys.x$ksuse  s,
       sys.x$ksusex x
 where o.inst_id = userenv('Instance')
   and p.inst_id = userenv('Instance')
   and s.inst_id = userenv('Instance')
   and o.kglhdpmd = 2
   and o.kglobtyp in (7, 8, 9, 12, 13)
   and p.kglpnhdl = o.kglhdadr
   and s.addr = p.kglpnses
   and x.inst_id = userenv('Instance')
   and x.sid = s.indx
   and x.serial = s.ksuseser
 order by 1,2,3
/
SQL> set pages 50000 line 140
SQL> col owner for a10
SQL> col name for a20
SQL> col sid for 999999
SQL> col username for a10
SQL> col program for a20
SQL> col module for a20
SQL> col action for a20
SQL> col client_info for a20
SQL> select decode(o.kglobtyp,
  2                7, 'PROCEDURE',
  3                8, 'FUNCTION',
  4                9, 'PACKAGE',
  5                12, 'TRIGGER',
  6                13, 'CLASS')      "TYPE",
  7         substr(o.kglnaown,1,20)  "OWNER",
  8         substr(o.kglnaobj,1,35)  "NAME",
  9         s.indx     "SID",
 10         s.ksuseser "SERIAL",
 11         s.ksuudnam "USERNAME",
 12         s.ksuseapp "PROGRAM",
 13         x.app      "MODULE",
 14         x.act      "ACTION",
       x.clinfo   "CLIENT_INFO"
 15   16    from sys.x$kglob  o,
 17         sys.x$kglpn  p,
 18         sys.x$ksuse  s,
 19         sys.x$ksusex x
 20   where o.inst_id = userenv('Instance')
 21     and p.inst_id = userenv('Instance')
 22     and s.inst_id = userenv('Instance')
 23     and o.kglhdpmd = 2
 24     and o.kglobtyp in (7, 8, 9, 12, 13)
 25     and p.kglpnhdl = o.kglhdadr
 26     and s.addr = p.kglpnses
 27     and x.inst_id = userenv('Instance')
 28     and x.sid = s.indx
 29     and x.serial = s.ksuseser
 30   order by 1,2,3
 31  /

TYPE      OWNER      NAME                     SID     SERIAL USERNAME   PROGRAM              MODULE               ACTION
--------- ---------- -------------------- ------- ---------- ---------- -------------------- -------------------- --------------------
CLIENT_INFO
--------------------
PACKAGE   SYS        DBMS_LOCK               1627        271 A          SQL*Plus             SQL*Plus


PROCEDURE A          P_TEST                  1627        271 A          SQL*Plus             SQL*Plus


PROCEDURE A          P_TEST2                 1627        271 A          SQL*Plus             SQL*Plus


PROCEDURE A          P_TEST3                 1627        271 A          SQL*Plus             SQL*Plus


TRIGGER   A          TR_T1                   1627        271 A          SQL*Plus             SQL*Plus



SQL>


7. 查询x$表得到运行过的SQL和存储过程的对应关系
见
what package/procedure did SQL come from?
Relationship between SQL statements in shared pool
SCRIPT: HOW TO IDENTIFY what packages are in the shared_pool and how many times have they been executed




外部链接:
-----
10.2.0.3新增字段
ASH – Active Session History Feel the Power
Action, Module, Program ID and V$SQL...
V$SESSION
V$ACTIVE_SESSION_HISTORY
DBA_HIST_ACTIVE_SESS_HISTORY

-----
X$表
How can I tell if a procedure/package is running?, 2
executing_packages.sql
How can I track the execution of PL/SQL and SQL?
该文也介绍了用dbms_application_info跟踪PLSQL的运行

Relationship between SQL statements in shared pool
SCRIPT: HOW TO IDENTIFY what packages are in the shared_pool and how many times have they been executed
Troubleshooting and Diagnosing ORA-4031 Error

解读X$表
Oracle X$ Tables
ORA-600 Lookup Error Categories

-----
PLSQL调用栈
FORMAT_CALL_STACK Function
How Can I find out who called me or what my name is



-fin-

Tuesday, October 28, 2008

WITH Clause







----------
Forwarded message ----------
From: XIE WEN-MFK346
<wenxie at motorola.com>
Date:
2008/10/28
Subject: WITH
子句(WITH
Clause)
To: xiewenxiewen at gmail.com





也叫子查询分解(subquery
factoring)
子句,或公用表表达式(common
table expression
)



是SQL99的标准, Oracle
9i开始支持





在一个复杂查询中,如果同样的查询块被调用了多次,就可以考虑使用WITH子句



WITH子句定义了子查询的别名,你能够在查询中引用这个别名多次,这样提高了SQL语句的可读性,有时也能提高运行效能





语法是:



WITH



别名1
AS (
子查询1)



别名2
AS (
子查询2)



...



SELECT ...







优化器对WITH子句有两种处理:





a.WITH子句被当成内嵌视图(inline
view)
处理



语句中别名的部分被替换成子查询,SELECT语句被扩展成带有子查询的语句,然后运行





b.或实例化(materialize),即建立临时表(temporary
table)



一般,如果别名被调用了多次,Oracle会创建全局临时表(global
temporary table)
用于保存子查询结果,因为子查询不会被计算多次,也就提高了查询性能









举例





创建测试表



create table t1 as
select * from all_objects;
create table t2 as select * from
dba_objects;
analyze table t1 compute statistics for table for all
indexes for all indexed columns;
analyze table t2 compute
statistics for table for all indexes for all indexed columns;





用传统SQL语句查询



set pages 9999
line 140
set autot on
select count(*)
from t1,
(select distinct owner username from t1) owners
where
t1.owner = owners.username
union all
select count(*)

from t2, (select distinct owner username from t1) owners
where
t2.owner = owners.username
/





T1全表扫描了两次,计算distinct
owner





改写成用WITH子句查询



with
owners
as (select distinct owner username from t1)
select count(*) from
t1, owners where t1.owner = owners.username
union all
select
count(*) from t2, owners where t2.owner = owners.username
/





执行计划里出现TEMP
TABLE TRANSFORMATION
表明因为子查询调用了两次,所以系统自动为子查询建立了临时表,表名是SYS_TEMP_....



统计信息里consistent
gets+physical reads
比不使用WITH子句少,读的次数减少了,说明子查询只计算了一次,对性能提高是有一定效果的



还产生了744的redo
size
,主要是因为建立了临时表









可以用优化器提示(hint)强制使用内嵌视图或临时表





加上inline提示使用内嵌视图



set autot off



explain plan for



with
owners
as (select /*+inline*/ distinct owner username from t1)
select
count(*) from t1, owners where t1.owner = owners.username
union
all
select count(*) from t2, owners where t2.owner =
owners.username
/
select * from table (dbms_xplan.display);





加上materialize提示使用临时表



explain plan
for
with
owners as (select /*+materialize*/ distinct
owner username from t1)
select count(*) from t1, owners where
t1.owner = owners.username
/
select * from table
(dbms_xplan.display);





inline,materialize提示在正式文档中没有讲,有时也不一定管用,所以一般不建议使用









WITH子句使用上有一些限制





1.不允许WITH嵌套使用



with
outer_subquery as (
with nested_subquery as (select sysdate
as date_column from dual))
select date_column from outer_subquery;




WITH
子句中的子查询里不能再用WITH





但可以用在后面其它别名中



with

subquery1 as (select sysdate as date_column from dual),

subquery2 as (select date_column from subquery1)
select
date_column from subquery2;





2.如果定义了子查询而没有用到,就会出错



with
unused_subquery
as (select dummy from dual)
select sysdate from dual;



必须用,不用都不行











外部链接:





subquery_factoring_clause



difference
between sql with clause and inline

subquery
factoring in oracle 9i

Subquery
Factoring (2)
或
2









Xie Wen (谢文)

Network &
Operations,
Multimedia Applications & Services (MDB)
MOTOROLA Inc.
NO.104 mail box,
8th floor, Motorola Tower,
No.
1 Wang Jing East Road, Chao Yang District,
Beijing 100102 P. R.
China
e-mail wenxie at motorola.com





Website Analytics

Followers