技术开发 频道

点评Oracle11g新特性:PLSQL新特性


    以前取SEQUENCE的NEXTVAL和CURRVAL只能通过SQL语句,现在可以在PL/SQL中通过赋值语句获取:

SQL> CREATE SEQUENCE S_ID; 序列已创建。 SQL> SET SERVEROUT ON SQL> DECLARE 2 V_ID NUMBER; 3 BEGIN 4 V_ID := S_ID.NEXTVAL; 5 DBMS_OUTPUT.PUT_LINE(V_ID); 6 END; 7 / 1 PL/SQL 过程已成功完成。

    这种方法比使用SELECT INTO要方便,更重要的是,由于不执行SQL语句,这种方式的效率更高。

SQL> CREATE SEQUENCE S_1; 序列已创建。 SQL> CREATE SEQUENCE S_2; 序列已创建。 SQL> CREATE OR REPLACE PROCEDURE P1 AS 2 V_SEQ NUMBER; 3 BEGIN 4 FOR I IN 1..100000 LOOP 5 SELECT S_1.NEXTVAL INTO V_SEQ FROM DUAL; 6 END LOOP; 7 END; 8 / 过程已创建。 SQL> CREATE OR REPLACE PROCEDURE P2 AS 2 V_SEQ NUMBER; 3 BEGIN 4 FOR I IN 1..100000 LOOP 5 V_SEQ := S_2.NEXTVAL; 6 END LOOP; 7 END; 8 / 过程已创建。 SQL> SET TIMING ON SQL> EXEC P1 PL/SQL 过程已成功完成。 已用时间: 00: 00: 06.81 SQL> EXEC P2 PL/SQL 过程已成功完成。 已用时间: 00: 00: 06.35 SQL> EXEC P1
PL/SQL 过程已成功完成。
     从SQL的运行时间上看,二者的差别不大,使用赋值的方法似乎略快一些。不过采用赋值的方法最大的好处是可以减少LATCH,在多用户并发访问的情况下效率更高。

    下面通过查询V$SESSTAT视图和V$LATCH视图,分布记录两个存储过程的执行统计信息,并将二者的差异显示出来。

    在执行之前,需要先建立一张临时表: 
SQL> SET TIMING OFF SQL> CREATE GLOBAL TEMPORARY TABLE T_SESSION_STAT 2 (ID NUMBER, NAME VARCHAR2(100), VALUE NUMBER); 表已创建。 SQL> SET SERVEROUT ON SQL> DECLARE 2 V_NUMBER1 NUMBER; 3 V_NUMBER2 NUMBER; 4 BEGIN 5 6 INSERT INTO T_SESSION_STAT SELECT 1, 'STAT:' || NAME, VALUE 7 FROM V$SESSTAT A, V$STATNAME B 8 WHERE A.STATISTIC# = B.STATISTIC# 9 AND A.SID = (SELECT SID FROM V$MYSTAT WHERE ROWNUM = 1) 10 UNION ALL 11 SELECT 1, 'LATCH:' || NAME, GETS VALUE FROM V$LATCH; 12 13 V_NUMBER1 := DBMS_UTILITY.GET_TIME; 14 P1; 15 V_NUMBER1 := DBMS_UTILITY.GET_TIME - V_NUMBER1; 16 17 INSERT INTO T_SESSION_STAT SELECT 2, 'STAT:' || NAME, VALUE 18 FROM V$SESSTAT A, V$STATNAME B 19 WHERE A.STATISTIC# = B.STATISTIC# 20 AND A.SID = (SELECT SID FROM V$MYSTAT WHERE ROWNUM = 1) 21 UNION ALL 22 SELECT 2, 'LATCH:' || NAME, GETS VALUE FROM V$LATCH; 23 24 V_NUMBER2 := DBMS_UTILITY.GET_TIME; 25 P2; 26 V_NUMBER2 := DBMS_UTILITY.GET_TIME - V_NUMBER2; 27 28 INSERT INTO T_SESSION_STAT SELECT 3, 'STAT:' || NAME, VALUE 29 FROM V$SESSTAT A, V$STATNAME B 30 WHERE A.STATISTIC# = B.STATISTIC# 31 AND A.SID = (SELECT SID FROM V$MYSTAT WHERE ROWNUM = 1) 32 UNION ALL 33 SELECT 3, 'LATCH:' || NAME, GETS VALUE FROM V$LATCH; 34 35 DBMS_OUTPUT.PUT_LINE('P1 EXECUTE ' || V_NUMBER1/100 || ' SECONDS'); 36 DBMS_OUTPUT.PUT_LINE('P2 EXECUTE ' || V_NUMBER2/100 || ' SECONDS'); 37 38 FOR C IN 39 ( 40 SELECT * 41 FROM 42 ( 43 SELECT A.NAME, C.VALUE + A.VALUE - 2 * B.VALUE VALUE 44 FROM 45 T_SESSION_STAT A, 46 T_SESSION_STAT B, 47 T_SESSION_STAT C 48 WHERE A.NAME = B.NAME 49 AND A.NAME = C.NAME 50 AND A.ID = 1 51 AND B.ID = 2 52 AND C.ID = 3 53 ) 54 WHERE ABS(VALUE) > 100 55 ) LOOP 56 DBMS_OUTPUT.PUT_LINE(RPAD(C.NAME, 50, ' ') || C.VALUE); 57 END LOOP; 58 59 END; 60 / P1 EXECUTE 6.29 SECONDS P2 EXECUTE 6.34 SECONDS STAT:session pga memory 131072 STAT:redo size -308 STAT:undo change vector size 292 LATCH:cache buffers chains -171 LATCH:redo allocation -103 LATCH:shared pool simulator -99993 PL/SQL 过程已成功完成。
    二者的执行时间仍然相差不多,上面的差异中redo和undo都不是关注的内容,采用SELECT INTO的方式会导致共享池的争用,而采用赋值的方式会占用更多的PGA内存。

    LATCH是串行操作,一遍情况下内存不是问题,因此应该尽量选择来提高并发性能。不过根据测试也可以看到,二者的性能差异很小,选择哪种方法都不会带来明显的性能变化。
0
相关文章