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

Oracle Built−in Packages- P24 docx

5 236 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,66 KB

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

Nội dung

*/ IF num BETWEEN firstrow AND lastrow THEN retval := desctabnum; END IF; RETURN retval; END; END; / 2.5.4 Displaying Table Contents with Method 4 Dynamic SQL This section examines

Trang 1

Column 2

SAL

2

Column 3

HIREDATE

12

In this next example, I load up the column information, use the nthcol function to get the information about just one column, deposit it in a locally declared record, and then check the column type

DECLARE

cur integer := dbms_sql.open_cursor;

rec DBMS_SQL.DESC_REC;

BEGIN

dbms_sql.PARSE

(cur, 'SELECT ename, sal, hiredate FROM emp', DBMS_SQL.NATIVE);

DBMS_SQL.DEFINE_COLUMN (cur, 1, 'a', 60);

DBMS_SQL.DEFINE_COLUMN (cur, 1, 1);

DBMS_SQL.DEFINE_COLUMN (cur, 1, SYSDATE);

desccols.forcur (cur);

rec := desccols.nthcol (1);

IF rec.col_type = desccols.varchar2_type

THEN

DBMS_OUTPUT.PUT_LINE ('Process as string!');

END IF;

DBMS_SQL.CLOSE_CURSOR (cur);

END;

/

And I get this output when executed:

Process as string!

Notice how I have shifted from dealing with the lowưlevel details of the DESCRIBE_COLUMNS builtưin to manipulating all that data through a clear, easyưtoưuse API It is not as though the code you need to write (and you will find in the body of the next package) is all that complicated But why bother with this code again and again when you can write it once and then just pass in the pointer to the cursor and let the package

do all the work?

The implementation of the desccols package is straightforward

/* Filename on companion disk: desccols.spp */*

CREATE OR REPLACE PACKAGE BODY desccols

IS

/* Here is the PL/SQL table holding the column information */

desctab DBMS_SQL.DESC_TAB;

desccnt PLS_INTEGER;

firstrow PLS_INTEGER;

lastrow PLS_INTEGER;

PROCEDURE forcur (cur IN INTEGER)

IS

BEGIN

/* Clear out the PL/SQL table */

desctab.DELETE;

/* Fill up the PL/SQL table */

DBMS_SQL.DESCRIBE_COLUMNS (cur, desccnt, desctab);

/* Get the first and last row numbers to avoid future lookups */

firstrow := desctab.FIRST;

lastrow := desctab.LAST;

END;

Trang 2

PROCEDURE show (fst IN INTEGER := 1, lst IN INTEGER := NULL)

IS

BEGIN

IF desccnt > 0

THEN

/* Show the specified rows */

FOR colind IN

GREATEST (fst, firstrow)

LEAST (NVL (lst, lastrow), lastrow)

LOOP

/* Add additional lines of output as you desire */

DBMS_OUTPUT.PUT_LINE ('Column ' || TO_CHAR (colind));

DBMS_OUTPUT.PUT_LINE (desctab(colind).col_name);

DBMS_OUTPUT.PUT_LINE (desctab(colind).col_type);

END LOOP;

END IF;

END;

FUNCTION numcols RETURN INTEGER

IS

BEGIN

RETURN desccnt;

END;

FUNCTION nthcol (num IN INTEGER) RETURN DBMS_SQL.DESC_REC

IS

retval DBMS_SQL.DESC_REC;

BEGIN

/* If a valid row number, retrieve that entire record */

IF num BETWEEN firstrow AND lastrow

THEN

retval := desctab(num);

END IF;

RETURN retval;

END;

END;

/

2.5.4 Displaying Table Contents with Method 4 Dynamic SQL

This section examines the kind of code you need to write to perform dynamic SQL Method 4 Method 4, introduced early in this chapter, supports queries that have a variable (defined only at runtime) number of items in the SELECT list and/or a variable number of host variables Here is an example of Method 4 dynamic SQL:

'SELECT ' || variable_select_list ||

' FROM ' || table_name ||

' WHERE sal > :minsal

' AND ' || second_clause ||

order_by_clause

Notice that with this SQL statement, I do not know how many columns or expressions are returned by the query The names of individual columns are "hidden" in the variable select list I also do not know the full contents of the WHERE clause; the minsal bind variable is obvious, but what other bind variable references might I find in the second_clause string? As a result of this uncertainty, Method 4 dynamic SQL is the most complicated kind of dynamic query to handle with DBMS_SQL

What's so hard about that? Well, if I am going to use the DBMS_SQL package to execute and fetch from such

a query, I need to write and compile a PL/SQL program Specifically, to parse the SQL statement, I need to define the columns in the cursor with calls to DEFINE_COLUMN −− yet I do not know the list of columns

at the time I am writing my code To execute the query, I must associate values to all of my bind variables

Trang 3

(identifiers with a ":" in front of them) by calling BIND_VARIABLE −− yet I do not know the names of those bind variables at the time I write my code Finally, to retrieve data from the result set of the query I also need to call COLUMN_VALUE for each column But, again, I do not know the names or datatypes of those columns up front

Sounds challenging, doesn't it? In fact, working with these incredibly dynamic SQL strings requires some interesting string parsing and some even more creative thinking

When would you run into Method 4? It arises when you build a frontend to support ad−hoc query generation

by users, or when you want to build a generic report program, which constructs the report format and contents dynamically at runtime I also encountered it recently when I decided to build a PL/SQL procedure to display the contents of a table −− any table, as specified by the user at runtime This section explores what it took for

me to implement this "in table" procedure

2.5.4.1 The "in table" procedural interface

Before I dive into the PL/SQL required to create my procedure, I should explore my options After all, it is certainly very easy for me to build a script in SQL*Plus to display the contents of any table

SELECT * FROM &1

I could even spice it up with a variable select list and WHERE clause as follows:

SELECT &1

FROM &2

WHERE &3

ORDER BY &4

In fact, SQL*Plus is a very flexible, powerful front−end tool for SQL scripts Yet no matter how fancy I get with substitution parameters in SQL*Plus, this is not code I can run from within PL/SQL Furthermore, PL/SQL gives me more procedural control over how to specify the data I want to see and how to display the data Finally, if I use PL/SQL, then I get to play with DBMS_SQL! On the downside, however, from within PL/SQL I must rely on DBMS_OUTPUT (described in Chapter 7, Defining an Application Profile) to display

my table contents, so I must reckon with the buffer limitations of that built−in package (a maximum of

1,000,000 bytes of data −− you will clearly not use my procedure to display very large quantities of data)

So I will use PL/SQL and DBMS_SQL But before building any code, I need to come up with a specification How will the procedure be called? What information do I need from my user (a developer, in this case)? What should a user have to type to retrieve the desired output? I want my procedure (which I call "intab" for "in table") to accept the inputs in the following table

Parameter Description

Name of the

table

Required Obviously, a key input to this program

Maximum length

of string

displayed

Optional Sets an upper limit on the size of string columns I do not even attempt to do the kind of string wrapping performed in SQL*Plus Instead, SUBSTR simply truncates the values

WHERE clause Optional Allows you to restrict the rows retrieved by the query If not specified, all rows

are retrieved You can also use this parameter to pass in ORDER BY and HAVING clauses, since they follow immediately after the where clause

Format for date

columns

Optional Allows you to set the standard format for date displays The default includes date and time information When using SQL*Plus, I find it very irritating to constantly have to use TO_CHAR to see the time portion of my date fields

Given these inputs, the specification for my procedure becomes the following:

2.5.4 Displaying Table Contents with Method 4 Dynamic SQL 108

Trang 4

PROCEDURE intab

(table_in IN VARCHAR2,

string_length_in IN INTEGER := 20,

where_in IN VARCHAR2 := NULL,

date_format_in IN VARCHAR2

:= 'MM/DD/YY HHMISS')

Here are some examples of calls to intab:

execute intab ('emp');

execute intab

('emp', 20, 'deptno = ' || v_deptno || ' order by sal');

These two calls to intab produce the following output:

execute intab ('emp');

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

Contents of emp

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

EMPNO ENAMEJOBMGR HIREDATESALCOMMDEPTNO

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

7839 KINGPRESIDENT11/17/81 120000500010

7698 BLAKE MANAGER7839 05/01/81 120000285030

7782 CLARKMANAGER7839 06/09/81 120000245010

7566 JONESMANAGER7839 04/02/81 120000297520

7654 MARTINSALESMAN7698 09/28/81 1200001250 140030

7499 ALLENSALESMAN7698 02/20/81 1200001600 3000

7844 TURNERSALESMAN7698 09/08/81 1200001500 0 30

7900 JAMESCLERK7698 12/03/81 12000095030

7521 WARDSALESMAN7698 02/22/81 1200001250 500 30

7902 FORDANALYST7566 12/03/81 120000300020

7369 SMITHCLERK7902 12/17/80 12000080020

7788 SCOTTANALYST7566 12/09/82 120000300020

7876 ADAMSCLERK7788 01/12/83 120000110020

7934 MILLERCLERK7782 01/23/82 120000130010

execute intab ('emp', 20, 'deptno = 10 order by sal');

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

Contents of emp

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

EMPNO ENAMEJOB MGR HIREDATESALCOMMDEPTNO

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

7934 MILLERCLERK7782 01/23/82 120000130010

7782 CLARKMANAGER7839 06/09/81 120000245010

7839 KINGPRESIDENT11/17/81 120000500010

Notice that the user does not have to provide any information about the structure of the table My program will get that information itself −− precisely the aspect of intab that makes it a Method 4 dynamic SQL

example

While this version of intab will certainly be useful, I am the first to recognize that there are many other

possible enhancements to intab, including:

Supplying a list of only those columns you wish to display This will bypass the full list of columns for the table (which, as you will see, is extracted from the data dictionary)

Supplying a list of those columns you wish to exclude from display If your table has 50 columns, and

you don't want to display three of those columns, it's a lot easier to list the three you don't want than

the 47 you do want.

Trang 5

Supplying an ORDER BY clause for the output You could do this through the WHERE clause, but it

is certainly more structured to provide a separate input

Providing a format for numeric data in addition to the date format

So, yes, there is always more one can do, but this one (yours truly) would like to leave some interesting work for his readers To encourage you to take my intab and "run with it," I will, in this section, step you through the usage of DBMS_SQL required to implement the intab procedure (The full program is contained on the companion disk.)

2.5.4.2 Steps for intab construction

In order to display the contents of a table, follow these steps:

1

Construct and parse the SELECT statement (using OPEN_CURSOR and PARSE)

2

Bind all local variables with their placeholders in the query (using BIND_VARIABLE)

3

Define each column in the cursor for this query (using DEFINE_COLUMN)

4

Execute and fetch rows from the database (using EXECUTE and FETCH_ROWS)

5

Retrieve values from the fetched row and place them into a string for display purposes (using

COLUMN_VALUE) Then display that string with a call to the PUT_LINE procedure of the

DBMS_OUTPUT package

NOTE: My intab implementation does not currently support bind variables I assume, in other

words, that the where_clause_in argument does not contain any bind variables As a result, I

will not be exploring in detail the code required for step 2

2.5.4.3 Constructing the SELECT

In order to extract the data from the table, I have to construct the SELECT statement The structure of the query is determined by the various inputs to the procedure (table name, WHERE clause, etc.) and the contents

of the data dictionary Remember that the user does not have to provide a list of columns Instead, I must identify and extract the list of columns for that table from a data dictionary view I have decided to use

all_tab_columns in intab so the user can view the contents not only of tables he, or she, owns (which are accessible in user_tab_columns), but also any table for which he, or she, has SELECT access

Here is the cursor I use to fetch information about the table's columns:

CURSOR col_cur

(owner_in IN VARCHAR2,

table_in IN VARCHAR2)

IS

SELECT column_name, data_type,

data_length,

data_precision, data_scale

FROM all_tab_columns

WHERE owner = owner_in

2.5.4 Displaying Table Contents with Method 4 Dynamic SQL 110

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