1. Trang chủ
  2. » Công Nghệ Thông Tin

Oracle Built−in Packages- P20 pps

5 179 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 5
Dung lượng 79,75 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Among other great features, the Janfast/jandyn package she developed contains a procedure called create_index to create an index for any table and columns on the table.. Just a few keyst

Trang 1

that most wonderful of PL/SQL constructs, the package Among other great features, the Janfast/jandyn package she developed contains a procedure called create_index to create an index for any table and

column(s) on the table The code for this procedure is shown at the beginning of this chapter

Jan installed the Janfast/jandyn package in the JANDBA account (what can I say? She likes her name!) and granted EXECUTE privilege on that package to all users, including her account named (yep, you guessed it) JAN To make things even more exciting, she built an Oracle Forms front−end to her package

(Janfast/jandyn) She could then take advantage of the stored code through a fill−in−the−form interface, rather than the command−line approach of SQL*Plus

One day Jan receives a call: it seems that the company has added many employees over the years, and the emp table now has six million rows All of the user accounts (working against a shared data source in the

PERSONNEL Oracle account) are experiencing serious performance problems A new index (at least one) is needed on the emp table to improve query performance

So Jan connects to the production PERSONNEL account and starts up Janfast/jandyn Just a few keystrokes and mouse clicks later, she has constructed the following statement,

jandyn.create_index ('empname_idx', 'emp', 'ename, sal');

which is to say: create an index named empname_idx on the emp table on the ename and sal columns (in that order) She clicks on the Execute button and Janfast does its thing Notified through a chime of the successful completion of her task, Jan is impressed at how rapidly the index was built She notifies the application development team that all is better now Fifteen minutes of quiet contemplation pass before she gets an angry call from a developer: "The performance hasn't changed one bit!" he says angrily "The screens still work just

as slowly as before when I try to search for an employee by name."

Jan the DBA is bewildered and quickly runs the following script to examine the indexes on the emp table:

SQL> SELECT i.index_name, i.tablespace_name,

uniqueness u, column_name col, column_position pos

FROM all_indexes i, all_ind_columns c

WHERE i.index_name = c.index_name

AND i.table_name = 'EMP';

INDEX_NAME TABLESPACE_NAME U COL POS

−−−−−−−−−−−−−−−− −−−−−−−−−−−−−−−−−−−− −−−−−−−−− −−−−−−−−− −−−

EMP_PRIMARY_KEY USER_DATA UNIQUE EMPNO 1

There is no empname_idx index! What has gone wrong? Where did the index go? How and why did

Janfast/jandyn fail our industrious and creative database administrator?

Remember: when you execute stored code, you run it under the privileges of the owner of that code, not the

privileges of the account that called the program When Jan executed the index creation statement from within

a call to jandyn.creind, the DDL statement was processed as though it were being executed by JANDBA, not

by PERSONNEL If the DBA had wanted to create an index in her own schema, she should have entered the following command:

jandyn.creind

('personnel.empname_idx', 'personnel.emp', 'ename, sal');

If this command had been executed, Jan would have had a much better chance at solving her performance problems

86

Trang 2

2.4.2.2 The tale of Scott

Here's another common gotcha: my SCOTT account has been granted the standard CONNECT and

RESOURCE roles As a result, I can create a table as follows:

SQL> CREATE TABLE upbeat (tempo NUMBER);

Table created

Now suppose that I have created a little program to make it easier for me to execute DDL from within

PL/SQL:

/* Filename on companion disk: runddl.sp */*

CREATE OR REPLACE PROCEDURE runddl (ddl_in in VARCHAR2)

IS

cur INTEGER:= DBMS_SQL.OPEN_CURSOR;

fdbk INTEGER;

BEGIN

DBMS_SQL.PARSE (cur, ddl_in, DBMS_SQL.V7);

fdbk := DBMS_SQL.EXECUTE (cur);

DBMS_SQL.CLOSE_CURSOR (cur);

END;

/

I then issue the same CREATE TABLE statement as before, this time within PL/SQL, but now I get an error:

SQL> exec runddl ('CREATE TABLE upbeat (tempo NUMBER)');

*

ERROR at line 1:

ORA−01031: insufficient privileges

ORA−06512: at "SYS.DBMS_SYS_SQL", line 239

Don't beat your head against the wall when this happens! Just remember that role−based privileges do not help you when executing SQL from within PL/SQL The RESOURCE role is ignored when the CREATE TABLE

statement is executed SCOTT doesn't have any CREATE TABLE privileges, so the dynamic SQL fails.

2.4.3 Combining Operations

Every PARSE must be preceded by a call to OPEN_CURSOR Every call to PARSE must include a DBMS mode argument, even though 99.99% of the time, it is going to be DBMS_SQL.NATIVE

When I find myself repeating the same steps over and over again in using a package or particular feature, I look for ways to bundle these steps into a single procedure or function call to save myself time The next function shows such a function, open_and_parse, which opens a cursor and parses the specified SQL

statement Using open_and_parse, I can replace the following statements,

cursor_handle := DBMS_SQL.OPEN_CURSOR;

DBMS_SQL.PARSE (cursor_handle, 'UPDATE emp ', DBMS_SQL.NATIVE);

with just this:

cursor_handle := open_and_parse ('UPDATE emp ');

Here, then, is the open_and_parse bundled procedure:

/* Filename on companion disk: openprse.sf */*

CREATE OR REPLACE FUNCTION open_and_parse

(sql_statement_in IN VARCHAR2,

2.4.2 Privileges and Execution Authority with DBMS_SQL 87

Trang 3

dbms_mode_in IN INTEGER := DBMS_SQL.NATIVE)

RETURN INTEGER

IS

/* Declare cursor handle and assign it a pointer */

return_value INTEGER := DBMS_SQL.OPEN_CURSOR;

BEGIN

/* Parse the SQL statement */

DBMS_SQL.PARSE (return_value, sql_statement_in, dbms_mode_in);

/* Pass back the pointer to this parsed statement */

RETURN return_value;

END;

Now, one problem with this otherwise handy little procedure is that it always declares a new cursor What if

you already have a cursor? Then you should go straight to the parse step You can combine the functionality

of open_and_parse with the initcur procedure shown in the next section to produce your own enhanced program

2.4.4 Minimizing Memory for Cursors

As noted earlier, you will not want to allocate a cursor via the OPEN_CURSOR procedure if you can instead use an already defined cursor that is not currently in use The best way to minimize memory usage with dynamic SQL cursors is to encapsulate the open action inside a procedure The initcur procedure shown below demonstrates this technique

With initcur, you pass a variable into the procedure If that variable points to a valid, open DBMS_SQL cursor, then it is returned unchanged If, on the other hand, that cursor is closed (the IS_OPEN function

returns FALSE) or IS_OPEN for any reason raises the INVALID_CURSOR exception, then

OPEN_CURSOR is called and the new cursor pointer is returned

CREATE OR REPLACE PROCEDURE initcur (cur_inout IN OUT INTEGER) IS

BEGIN

IF NOT DBMS_SQL.IS_OPEN (cur_inout)

THEN

cur_inout := DBMS_SQL.OPEN_CURSOR;

END IF;

EXCEPTION

WHEN invalid_cursor

THEN

cur_inout := DBMS_SQL.OPEN_CURSOR;

END;

/

You could also implement this functionality as a function, or could overload both inside a package, as

follows:

CREATE OR REPLACE PACKAGE dyncur

IS

PROCEDURE initcur (cur_inout IN OUT INTEGER);

FUNCTION initcur (cur_in IN INTEGER) RETURN INTEGER;

END dyncur;

/

In addition to allocating cursor areas only when necessary, you should make sure that you close your cursors when you are done with them Unlike static cursors, which close automatically when their scope terminates, a dynamic SQL cursor will remain open even if the block in which it was defined finishes execution And remember that you should perform the close operation at the end of the executable code, but also in any exception sections in the block This technique is shown here:

CREATE OR REPLACE PROCEDURE do_dynamic_stuff

Trang 4

cur1 INTEGER := DBMS_SQL.OPEN_CURSOR;

cur2 INTEGER := DBMS_SQL.OPEN_CURSOR;

PROCEDURE closeall IS

BEGIN

/* Only close if open Defined in Closing the Cursor section */

closeif (cur1);

closeif (cur2);

END;

BEGIN

/* Do the dynamic stuff, then close the cursors.*/

.

closeall;

EXCEPTION

WHEN DUP_VAL_ON_INDEX

THEN

/* Special handling, then cleanup */

.

closeall;

WHEN OTHERS

THEN

/* Catch−all cleanup, then reraise to propagate out the error.*/

closeall;

RAISE;

END;

/

The openprse.ssp file contains a package that implements both the initcur and an enhanced version of

open_and_parse (see the section called Section 2.4.3, "Combining Operations") This package allows you to keep to an absolute minimum the number of cursors allocated to perform dynamic SQL operations

2.4.5 Improving the Performance of Dynamic SQL

You can improve the performance of your dynamic SQL operations by taking advantage of two aspects of DBMS_SQL:

Reuse DBMS_SQL cursors whenever possible (this technique was demonstrated in the last section)

Avoid reparsing your dynamic SQL when the only thing that changes is a bind variable

The following script (written with the help of John Beresniewicz) demonstrates the gains you can see by paying attention to both of these considerations The script illustrates three different ways to do the same thing

in dynamic SQL using DBMS_SQL, namely fetch rows from a table

Approach 1 parses and executes each query without using any bind variables It is the most

inefficient, performing 1000 parses, executing 1000 statements, and requiring room in the shared pool for up to 1000 different SQL statements

Approach 2 parses and executes for each query, but uses bind variables, so it works a bit more

efficiently With this approach, you still perform 1000 parses and execute 1000 times, but at least you need room for only one SQL statement in the shared pool

2.4.5 Improving the Performance of Dynamic SQL 89

Trang 5

Approach 3 parses just once and executes each query using host variables It is by far the most

efficient technique: requiring just one parse, 1000 executions, and a single preparsed SQL statement

in the shared pool

I have used a very large and cumbersome SELECT in this test to make sure that there was enough overhead in parsing to both simulate a "real−world" query and also to demonstrate a clear difference in performance between the second and third approaches (for very simple SQL statements, you will not see too much of a difference) For the sake of brevity, I will not show the entire query in the code

/* Filename on companion disk: effdsql.tst */*

DECLARE

/*

|| Approach 1: the worst

*/

v_start INTEGER;

cursor_id INTEGER;

exec_stat INTEGER;

BEGIN

v_start := DBMS_UTILITY.GET_TIME;

cursor_id := DBMS_SQL.OPEN_CURSOR;

FOR i IN 1 &1

LOOP

/*

|| parse and excecute each loop iteration

|| without using host vars, this is worst case

*/

DBMS_SQL.PARSE (cursor_id, 'SELECT ', DBMS_SQL.native);

exec_stat := DBMS_SQL.EXECUTE(cursor_id);

END LOOP;

DBMS_SQL.CLOSE_CURSOR(cursor_id);

DBMS_OUTPUT.PUT_LINE

('Approach 1: ' || TO_CHAR (DBMS_UTILITY.GET_TIME − v_start));

END;

/

DECLARE

/*

|| Approach 2: a little better

*/

v_start INTEGER;

cursor_id INTEGER;

exec_stat INTEGER;

BEGIN

v_start := DBMS_UTILITY.GET_TIME;

cursor_id := DBMS_SQL.OPEN_CURSOR;

FOR i IN 1 &1

LOOP

/*

|| parse and excecute each loop iteration using host vars

*/

DBMS_SQL.PARSE (cursor_id, 'SELECT ', DBMS_SQL.native);

DBMS_SQL.BIND_VARIABLE(cursor_id,'i',i);

exec_stat := DBMS_SQL.EXECUTE(cursor_id);

END LOOP;

DBMS_SQL.CLOSE_CURSOR(cursor_id);

DBMS_OUTPUT.PUT_LINE

('Approach 2: ' || TO_CHAR (DBMS_UTILITY.GET_TIME − v_start));

END;

/

DECLARE

/*

|| Approach 3: the best

*/

v_start INTEGER;

cursor_id INTEGER;

exec_stat INTEGER;

2.4.5 Improving the Performance of Dynamic SQL 90

Ngày đăng: 07/07/2014, 00:20

TỪ KHÓA LIÊN QUAN