从PL/SQL FAQ中摘抄出来几篇比较有用的文章

全文阅读见PL/SQL FAQ

How can one see if somebody modified any code?

The source code for stored procedures, functions and packages are stored in the Oracle Data Dictionary. One can detect code changes by looking at the TIMESTAMP and LAST_DDL_TIME column in the USER_OBJECTS dictionary view. Example:

SELECT OBJECT_NAME,
       TO_CHAR(CREATED,       'DD-Mon-RR HH24:MI') CREATE_TIME,
       TO_CHAR(LAST_DDL_TIME, 'DD-Mon-RR HH24:MI') MOD_TIME,
       STATUS
FROM   USER_OBJECTS
WHERE  LAST_DDL_TIME > '&CHECK_FROM_DATE';

Note: If you recompile an object, the LAST_DDL_TIME column is updated, but the TIMESTAMP column is not updated. If you modified the code, both the TIMESTAMP and LAST_DDL_TIME columns are updated.


How can one search PL/SQL code for a string/ key value?

The following query is handy if you want to know where certain tables, columns and expressions are referenced in your PL/SQL source code.

SELECT type, name, line
  FROM   user_source
 WHERE  UPPER(text) LIKE UPPER('%&KEYWORD%');

If you run the above query from SQL*Plus, enter the string you are searching for when prompted for KEYWORD. If not, replace &KEYWORD with the string you are searching for.

What is the difference between %TYPE and %ROWTYPE?

Both %TYPE and %ROWTYPE are used to define variables in PL/SQL as it is defined within the database. If the datatype or precision of a column changes, the program automatically picks up the new definition from the database without having to make any code changes.

The %TYPE and %ROWTYPE constructs provide data independence, reduces maintenance costs, and allows programs to adapt as the database changes to meet new business needs.

%TYPE

%TYPE is used to declare a field with the same type as that of a specified table's column. Example:

DECLARE
   v_EmpName  emp.ename%TYPE;
BEGIN
   SELECT ename INTO v_EmpName FROM emp WHERE ROWNUM = 1;
   DBMS_OUTPUT.PUT_LINE('Name = ' || v_EmpName);
END;
/

%ROWTYPE

%ROWTYPE is used to declare a record with the same types as found in the specified database table, view or cursor. Examples:

DECLARE
  v_emp emp%ROWTYPE;
BEGIN
  v_emp.empno := 10;
  v_emp.ename := 'XXXXXXX';
END;
/

How does one loop through tables in PL/SQL?

One can make use of cursors to loop through data within tables. Look at the following nested loops code example.

DECLARE
   CURSOR dept_cur IS
   SELECT deptno
     FROM dept
    ORDER BY deptno;

   -- Employee cursor all employees for a dept number
   CURSOR emp_cur (v_dept_no DEPT.DEPTNO%TYPE) IS
   SELECT ename
     FROM emp
    WHERE deptno = v_dept_no;
BEGIN
   FOR dept_rec IN dept_cur LOOP
      dbms_output.put_line('Employees in Department '||TO_CHAR(dept_rec.deptno));

      FOR emp_rec in emp_cur(dept_rec.deptno) LOOP
         dbms_output.put_line('...Employee is '||emp_rec.ename);
      END LOOP;

  END LOOP;
END;
/

How often should one COMMIT in a PL/SQL loop? / What is the best commit strategy?

Contrary to popular belief, one should COMMIT less frequently within a PL/SQL loop to prevent ORA-1555 (Snapshot too old) errors. The higher the frequency of commit, the sooner the extents in the undo/ rollback segments will be cleared for new transactions, causing ORA-1555 errors.

To fix this problem one can easily rewrite code like this:

FOR records IN my_cursor LOOP
   ...do some stuff...
   COMMIT;
END LOOP;
COMMIT;

... to ...

FOR records IN my_cursor LOOP
   ...do some stuff...
   i := i+1;
   IF mod(i, 10000) = 0 THEN    -- Commit every 10000 records
      COMMIT;
   END IF;
END LOOP;
COMMIT;

If you still get ORA-1555 errors, contact your DBA to increase the undo/ rollback segments.

NOTE: Although fetching across COMMITs work with Oracle, is not supported by the ANSI standard.


Issuing frequent commits is bad, bad, BAD! It’s the WORST thing you can do… just don’t do it! In the following example I will create around 7 million rows and then attempt to update a portion of them serially. In addition, I will issue a commit every thousandth row.

Example 1.1: Creating a somewhat large table

 SQL> create table big_employee_table
   2    as
   3    select  rownum as eid
   4         ,  e.*
   5      from  hr.employees e
   6         ,  dba_objects do;
 Table created.
 Elapsed: 00:00:12.23
 SQL>   select  count(*)
   2      from  big_employee_table;
   COUNT(*)
 ----------
    7838713
 
 Elapsed: 00:00:08.11

Before I go on, notice that Oracle’s “Create Table As” (CTAS) method blazed thru table creation. That’s 7.84 Million rows in 12.23 seconds. Sometimes, this is the very best method of updating large data sets. The following block updates 100,000 rows, serially, committing every 1000 rows:

Example 1.2: Updating serially

 
SQL> declare
   2    cursor c is
   3      select  *
   4        from  big_employee_table
   5       where  rownum <= 100000;
   6  begin
   7    for r in c loop
   8      update  big_employee_table
   9         set  salary = salary * 1.03
  10       where  eid = r.eid;
  11
  12      if mod ( r.eid, 1000 ) = 0 then
  13        commit;
  14      end if;
  15    end loop;
  16  end;
  17  /

Observe that the update took more time than I have patience for ;). At 20 minutes I killed the session. It is painfully slow and should never be done. Moreover, it chewed up an entire CPU core for the duration. If you’re only updating a few rows, why do it in PL/SQL at all? I like Tom Kyte’s approach (paraphrasing):

 1.  Do it in SQL.
 2.  If SQL can’t do it, do it in PL/SQL.
 3.  If PL/SQL can’t do it, do it in Java.
 4.  If Java can’t do it ask yourself if it needs to be done.

The following block does the same work in bulk:

Example 1.3: Updating in bulk and committing at the end

 
SQL> declare
   2    type obj_rowid is table of rowid
   3      index by pls_integer;
   4
   5    lr_rowid    obj_rowid;
   6    lr_salary   dbms_sql.number_table;
   7
   8    cursor c is
   9      select  rowid rid
  10           ,  salary
  11        from  big_employee_table
  12       where  rownum <= 100000;
  13  begin
  14    open c;
  15      loop
  16        fetch c bulk collect
  17         into lr_rowid
  18            , lr_salary
  19        limit 500;
  20
  21        for a in 1 .. lr_rowid.count loop
  22          lr_salary ( a ) := lr_salary ( a ) * 1.03;
  23        end loop;
  24
  25        forall b in 1 .. lr_rowid.count
  26          update  big_employee_table
  27             set  salary = lr_salary ( b )
  28           where  rowid in ( lr_rowid ( b ));
  29
  30        exit when c%notfound;
  31      end loop;
  32    close c;
  33    commit; -- there! not in the loop
  34  exception
  35    when others then
  36      rollback;
  37      dbms_output.put_line ( sqlerrm );
  38  end;
  39  /
 
 PL/SQL procedure successfully completed.
 
 Elapsed: 00:00:02.11
 SQL>

Notice that the update completed in 2 seconds! I’ve seen faster but my two-gerbil sandbox machine doesn’t have the power that our newer servers do. The point is that the update was incredibly fast and chewed up only 10% of one core. So, in answer to the question of “how often should I commit?” I say don’t until you absolutely have to


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值