The DBMS_SQL package offers access to dynamic SQL and dynamic PL/SQL from within PL/SQL programs. "Dynamic" means that the SQL statements you execute with this package are not prewritten into your programs. They are, instead, constructed at runtime as character strings and then passed to the SQL engine for execution.
The DBMS_SQL package allows you to perform actions that are otherwise impossible from within PL/SQL programs, including:
DDL (Data Definition Language) statements, such as DROP TABLE or CREATE INDEX, are not legal in native PL/SQL. On the other hand, you can use DBMS_SQL to issue any DDL statement and create generic programs to perform such actions as dropping the specified table. Of course, your session will still need the appropriate database privileges to perform the requested actions.
With DBMS_SQL, you no longer have to hard-code a SELECT statement for a query or a cursor. Instead, you can let a user specify different sort orders, conditions, and any other portion of a SELECT statement.
In a database table you can store the names of procedures that perform certain calculations. Then build a front-end to that table, which allows a user to select the computation of interest, provide the inputs to that program, and then execute it. When other computations need to be offered to the user, you add a row in a table, instead of modifying one or more screens.
DBMS_SQL is simultaneously one of the most complex, most useful, and most rewarding of the built-in packages. It may take some time for you to get comfortable with how to apply the technology. Once you are up and running, however, you will be amazed at the feats you will be able to perform!
Before explaining the details of DBMS_SQL, let's look at a few concrete examples.
When you issue a SQL statement via DBMS_SQL, you will have to write much more code than you would by simply executing a native SQL statement, such as an implicit cursor created with a SELECT statement. To get a feel for the differences between these two approaches, consider the following code. This first procedure uses native SQL to give every employee in the specified department a raise:
PROCEDURE giveraise (dept_in IN INTEGER, raise_in IN NUMBER) IS BEGIN UPDATE employee SET salary = salary + raise_in WHERE department_id = dept_in; END;
The following procedure does the same thing, but with DBMS_SQL. Given the volume of code (and the subsequent overhead), you should only use DBMS_SQL when your SQL statement is truly dynamic or involves DDL.
PROCEDURE giveraise (dept_in IN INTEGER, raise_in IN NUMBER) IS cursor_handle INTEGER; emps_updated INTEGER; BEGIN /* Create a cursor to use for the dynamic SQL */ cursor_handle := DBMS_SQL.OPEN_CURSOR; /* || Construct the SQL statement and parse it in Version 7 mode. || Notice that the statement includes two bind variables; these || are "placeholders" in the SQL statement. */ DBMS_SQL.PARSE (cursor_handle, 'UPDATE employee SET salary = salary + :raise_amount ' || 'WHERE department_id = :dept', DBMS_SQL.V7); /* Now I must supply values for the bind variables */ DBMS_SQL.BIND_VARIABLE (cursor_handle, 'raise_amount', raise_in); DBMS_SQL.BIND_VARIABLE (cursor_handle, 'dept', dept_in); /* Execute the SQL statement */ emps_updated := DBMS_SQL.EXECUTE (cursor_handle); /* Close the cursor */ DBMS_SQL.CLOSE_CURSOR (cursor_handle); EXCEPTION WHEN OTHERS THEN /* Clean up on failure too. */ DBMS_SQL.CLOSE_CURSOR (cursor_handle); END;
Truly dynamic SQL occurs when you literally construct the SQL statement from runtime variable values. This is shown in the next example. The create_index procedure creates an index where the name of the index, the name of the table, and the column on which the index is to be created are passed as parameters to the procedure. This action would be impossible without DBMS_SQL for two reasons: this is a DDL call and the SQL statement isn't known until the procedure is called.
/* Filename on companion disk: creind.sp */ CREATE OR REPLACE PROCEDURE create_index (index_in IN VARCHAR2, table_in IN VARCHAR2, column_in in VARCHAR2) IS cursor_handle INTEGER; feedback INTEGER; BEGIN /* Create a cursor to use for the dynamic SQL */ cursor_handle := DBMS_SQL.OPEN_CURSOR; /* Construct the SQL statement and parse it in native mode. */ DBMS_SQL.PARSE (cursor_handle, 'CREATE INDEX ' || index_in || ' ON ' || table_in || '( ' || column_in || ')', DBMS_SQL.NATIVE); /* You should always execute your DDL! */ feedback := DBMS_SQL.EXECUTE (cursor_handle); DBMS_SQL.CLOSE_CURSOR (cursor_handle); END create_index; /
Copyright (c) 2000 O'Reilly & Associates. All rights reserved.