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

Oracle Built−in Packages- P27 doc

5 194 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 80,04 KB

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

Nội dung

As you do so, keep in mind the following tips: • When you declare index−by tables to be used in DBMS_SQL processing, you must declare them based on one of the DBMS_SQL table TYPES: DBMS

Trang 1

examine the consequences of using a placeholder.

Suppose my dynamic PL/SQL block were constructed as follows:

'BEGIN ' || nm_in || ' := :val; END;',

When I bind in my value with a call to DBMS_SQL.BIND_VARIABLE, it is not treated as a literal For example, if I call dynvar.copyto with the following arguments,

dynvar.copyto ('steven''s hairline', 'rapid.retreat');

then the PL/SQL block I would execute is,

BEGIN rapid.retreat := steven's hairline; END;

which would definitely run into compile problems When I first encountered this problem, I figured that I would embed the :val placeholder inside single quotes to make sure that the value I pass in was treated as literal My parse statement then looked like this:

DBMS_SQL.PARSE

(cur, 'BEGIN ' || nm_in || ' := '':val''; END;', DBMS_SQL.NATIVE);

But this would not work; DBMS_SQL did not recognize :val as a placeholder, any longer since it was inside single quotes Sigh The only solution was to remove :val, and, instead of using placeholders, simply

concatenate the value directly into the string inside single quotes

The dynvar package should get you comfortable with executing dynamic PL/SQL code and also with the concept of indirect referencing I suggest that you try extending dynvar to support other datatypes (numbers and dates, in particular) See what challenges you encounter You will definitely need to make some changes

to incorporate these different datatypes into your dynamically constructed blocks

2.5.6 Array Processing with DBMS_SQL

One of the most significant advances for DBMS_SQL in Oracle8 is the support for "array processing" through use of the BIND_ARRAY and DEFINE_ARRAY procedures Using these procedures, you can greatly speed

up dynamic SQL processing that involves multiple rows of data This section offers some general suggestions and then provides detailed examples of using array processing in DBMS_SQL to perform fetches, queries, and dynamic PL /SQL

In order to take advantage of array processing, you will need to be comfortable with the use of index−by

tables (called PL /SQL tables in Oracle7) These structures are like single−dimension arrays and are described

in detail in Chapter 10 of Oracle PL /SQL Programming With this feature under your belt, you are ready to

implement array processing with DBMS_SQL As you do so, keep in mind the following tips:

When you declare index−by tables to be used in DBMS_SQL processing, you must declare them based on one of the DBMS_SQL table TYPES:

DBMS_SQL.NUMBER_TABLE DBMS_SQL.VARCHAR2_TABLE DBMS_SQL.DATE_TABLE DBMS_SQL.BLOB_TABLE DBMS_SQL.CLOB_TABLE DBMS_SQL.BFILE_TABLE

Here is an example of declaring a table used to receive multiple pointers to BFILEs from a

dynamically constructed query:

Trang 2

DECLARE bfiles_list DBMS_SQL.BFILE_TABLE;

One very significant downside to the approach taken by Oracle to support array processing with DBMS_SQL is that you cannot use index−by tables of records Instead, you will need to declare individual tables for each of the columns you wish to query or placeholders you wish to put in your SQL statements

If you are going to fetch more than one row with a single call to FETCH_ROWS, you must define the columns in that cursor as "array columns." To do this, you must provide an index−by table in the call

to DEFINE_ARRAY The following example shows the steps required to fetch a whole bunch of salaries (up to 100 at a time) from the emp table:

DECLARE cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR;

sal_table DBMS_SQL.NUMBER_TABLE;

BEGIN DBMS_SQL.PARSE (cur, 'SELECT sal FROM emp', DBMS_SQL.NATIVE);

DBMS_SQL.DEFINE_COLUMN (cur, 1, sal_table, 100, 10);

When you call DEFINE_ARRAY, you also can specify the starting row in which data is to be placed when you call DBMS_SQL.COLUMN_VALUE (in the previous example, I specified that the starting row to be 10) This is a little bit odd and worth emphasizing That fifth argument does not have anything to do with the index−by table you pass as the third argument to DEFINE_ARRAY It applies

to the index−by table that is passed to the call to COLUMN_VALUE (which might or might not

actually be the same index−by table) If you don't provide a value for this starting row, values are

assigned to the receiving index−by table from row 1 In all cases, the rows are filled sequentially

Why would you not use the default value of 1 for starting row? You might want to preserve the values

already stored in the index−by table, and simply add the new rows of data to the table

If you want to bind multiple values into a SQL statement, you must call BIND_ARRAY instead of the scalar BIND_VARIABLE procedure You will do this when you want to modify more than one row

in the table with a single DML statement

If you are using more than one array or index−by table in a single SQL statement, remember that DBMS_SQL applies a "lowest common denominator" rule It determines the highest starting row number and the lowest ending row number across all arrays and then uses only those rows between

those endpoints in all of the arrays.

The following section shows in more detail how to use array processing in DBMS_SQL to fetch multiple rows with a single call to DBMS_SQL.FETCH_ROWS, update multiple rows of data with multiple bind variables, and manipulate index−by table contents with dynamic PL/SQL

2.5.6.1 Using array processing to insert

There are three different ways to change the contents of a table: INSERT, DELETE, and UPDATE This section shows examples of the first of these DML statements, INSERT, utilizing the array features of

DBMS_SQL The next two sections show examples of DELETE and UPDATE

The following procedure inserts the contents of a index−by table into a database table using single−row INSERTs (the Version 7 behavior) Notice that I parse once, but bind and execute for each individual row in the index−by table I do not need to reparse, since I am not changing the SQL statement, only the value I am

Trang 3

binding into that SQL statement.

Notice also that I first create a package to define two index−by table types Why do I do this? I am passing in index−by tables as arguments to my insert procedure The parameter list of the procedure must therefore reference existing index−by table types in order to compile I could also have put the table type declarations and the procedure into a single package

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

CREATE OR REPLACE PACKAGE mytables

IS

TYPE number_table IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;

TYPE varchar2_table IS TABLE OF VARCHAR2(2000) INDEX BY BINARY_INTEGER;

END;

/

CREATE OR REPLACE PROCEDURE instab7

(empnotab IN mytables.number_table,

enametab IN mytables.varchar2_table)

IS

cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR;

fdbk PLS_INTEGER;

totfdbk PLS_INTEGER := 0;

BEGIN

DBMS_SQL.PARSE (cur,

'INSERT INTO emp2 (empno, ename) VALUES (:empno, :ename)',

DBMS_SQL.NATIVE);

FOR emprow IN empnotab.FIRST empnotab.LAST

LOOP

DBMS_SQL.BIND_VARIABLE (cur, 'empno', empnotab(emprow));

DBMS_SQL.BIND_VARIABLE (cur, 'ename', enametab(emprow));

fdbk := DBMS_SQL.EXECUTE (cur);

totfdbk := totfdbk + fdbk;

END LOOP;

DBMS_OUTPUT.PUT_LINE ('Rows inserted: ' || TO_CHAR (totfdbk));

DBMS_SQL.CLOSE_CURSOR (cur);

END;

/

How would this implementation change with Oracle8? You no longer have to perform separate INSERTs for each of the rows in the index−by table Instead, you can bind an index−by table directly into the SQL

statement The following version of the instab procedure relies on this new feature Notice that the FOR LOOP is gone Instead, I call BIND_ARRAY once for each column in the INSERT statement, passing in the appropriate index−by table Notice that I must now use the DBMS_SQL table types, whereas in the previous example I created my own index−by table types and used those in the parameter list

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

CREATE OR REPLACE PROCEDURE instab8

(empnotab IN DBMS_SQL.NUMBER_TABLE,

enametab IN DBMS_SQL.VARCHAR2_TABLE)

IS

cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR;

fdbk PLS_INTEGER;

BEGIN

DBMS_SQL.PARSE (cur,

'INSERT INTO emp2 (empno, ename) VALUES (:empno, :ename)',

DBMS_SQL.NATIVE);

DBMS_SQL.BIND_ARRAY (cur, 'empno', empnotab);

DBMS_SQL.BIND_ARRAY (cur, 'ename', enametab);

fdbk := DBMS_SQL.EXECUTE (cur);

Trang 4

DBMS_OUTPUT.PUT_LINE ('Rows inserted: ' || TO_CHAR (fdbk));

DBMS_SQL.CLOSE_CURSOR (cur);

END;

So in this case I end up with less code to write In other situations, you may have to copy your data from your own index−by tables, perhaps a table or record that is not supported by DBMS_SQL, into the appropriate DBMS_SQL−declared index−by tables before you can call BIND_ARRAY But putting aside code volume, what about the impact on performance? To compare these two approaches, I wrote the following SQL*Plus script:

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

DECLARE

timing PLS_INTEGER;

empnos7 mytables.number_table;

enames7 mytables.varchar2_table;

empnos8 DBMS_SQL.NUMBER_TABLE;

enames8 DBMS_SQL.VARCHAR2_TABLE;

BEGIN

/* Load up the index−by tables */

FOR i IN 1 &1

LOOP

empnos&2(i) := 10000 + i;

enames&2(i) := 'Eli ' || TO_CHAR (i);

END LOOP;

timing := DBMS_UTILITY.GET_TIME;

instab&2 (empnos&2, enames&2);

DBMS_OUTPUT.PUT_LINE

('V&2 style = ' || TO_CHAR (DBMS_UTILITY.GET_TIME − timing));

END;

/

Though SQL*Plus is a fairly crude tool, those substitution parameters let you write some clever, concise utilities In this case, I populate my index−by tables with &1 number of rows −− but the index−by table I fill

up depends on the &2 or second argument: the Oracle7 or Oracle8 versions Once the tables have their data, I pass them to the appropriate version of "instab," and, using DBMS_UTILITY.GET_TIME, display the

number of hundredths of seconds that elapsed

I ran this script a number of times to even out the bumps of initial load and parse and so on The following numbers are representative:

SQL> @dynins.tst 1000 7

Rows inserted: 1000

V7 style = 90

SQL> @dynins.tst 1000 8

Rows inserted: 1000

V8 style = 2

As you can see, dynamic SQL using arrays or index−by tables will perform significantly better than the single−row processing available in Oracle7

So that's how you do INSERTs: for each column for which you are inserting a value, you must bind in an array or index−by table You can also mix together scalars and arrays If, for example, I wanted to insert a set

of new employees and make the hiredate the value returned by SYSDATE, the bind steps in my instab8 procedure would be modified as follows:

DBMS_SQL.BIND_ARRAY (cur, 'empno', empnotab);

DBMS_SQL.BIND_ARRAY (cur, 'ename', enametab);

Trang 5

DBMS_SQL.BIND_VARIABLE (cur, 'hiredate', SYSDATE);

In other words: two array binds and one scalar bind The same value returned by SYSDATE is then applied to

all rows inserted.

2.5.6.2 Using array processing to delete

The process for deleting multiple rows with a single call to the EXECUTE function is similar to that for INSERTSs: one call to BIND_ARRAY for each placeholder in the SQL statement With DELETEs, however, the placeholders are in the WHERE clause, and each row in the index−by table is used to identify one or more rows in the database table

The following procedure removes all rows as specified in the array of names Notice the use of LIKE and UPPER operators to increase the flexibility of the table entries (if that is what you want!)

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

CREATE OR REPLACE PROCEDURE delemps

(enametab IN DBMS_SQL.VARCHAR2_TABLE)

IS

cur PLS_INTEGER := DBMS_SQL.OPEN_CURSOR;

fdbk PLS_INTEGER;

BEGIN

DBMS_SQL.PARSE (cur,

'DELETE FROM emp WHERE ename LIKE UPPER (:ename)',

DBMS_SQL.NATIVE);

DBMS_SQL.BIND_ARRAY (cur, 'ename', enametab);

fdbk := DBMS_SQL.EXECUTE (cur);

DBMS_OUTPUT.PUT_LINE ('Rows deleted: ' || TO_CHAR (fdbk));

DBMS_SQL.CLOSE_CURSOR (cur);

END;

/

The standard emp table contains the following 14 names:

ADAMS JAMES SCOTT

ALLEN JONES SMITH

BLAKE KING TURNER

CLARK MARTIN WARD

FORD MILLER

Now let's run the following script:

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

DECLARE

empnos8 DBMS_SQL.NUMBER_TABLE;

enames8 DBMS_SQL.VARCHAR2_TABLE;

BEGIN

/* Load up the index−by table */

enames8(1) := '%S%';

enames8(2) := '%I%';

delemps (enames8);

END;

/

SQL> @dyndel.tst

Rows deleted: 8

And we are then left with the following employees:

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