- Using SQL Plus and SPOOL command
- Using UTL_FILE built-in package
Unloading oracle data to flat files
How to validate values in PL/SQL
Validating NULL Values
NVL Function
This function is boon to programmers. I always chose to use this function because of its simplicity. In Oracle one must always remember that while validating with a NULL value the result you are going to get is also a NULL. So in order to avoid this it is better to use NVL() function.
Syntax:
NVL(variable1, variable2)
Example:
SELECT NVL(NULL, 'This is the output') null_test FROM dual;
Result:
null_test
This is the output
Both the parameters are required for NVL function to perform. You can use this function in WHERE clause so that you are not going to omit any NULL values from your query.
NVL2 Function
NVL2 function is an extension of NVL function that it can return two values, one when the variable to be tested is NULL and when the variable to be tested is NOT NULL.
Syntax:
NVL2(variable1, variable1_if_not_null, variable1_if_null)
Example 1:
SELECT NVL2(NULL,'NOT NULL','NULL') nvl2_test FROM dual;
Result:
nvl2_test
NULL
Example 2:
SELECT NVL2('some value','NOT NULL','NULL') nvl2_test FROM dual;
Result:
nvl2_test
NOT NULL
Validating NOT NULL Values
While validating NOT NULL values there is no need to use such functions as the value will be present already in the variable. But if the check is for whether the value is null or not then NVL2 function will be best suitable for the purpose.
How to enter a single quotation mark in Oracle
Ans: Although this may be a undervalued question, I got many a search for my blog with this question. This is where I wanted to address this question elaborately or rather in multiple ways.
Method 1
The most simple and most used way is to use a single quotation mark with two single quotation marks in both sides.
SELECT 'test single quote''' from dual;
The output of the above statement would be:
test single quote'
Simply stating you require an additional single quote character to print a single quote character. That is if you put two single quote characters Oracle will print one. The first one acts like an escape character.
This is the simplest way to print single quotation marks in Oracle. But it will get complex when you have to print a set of quotation marks instead of just one. In this situation the following method works fine. But it requires some more typing labour.
Method 2
I like this method personally because it is easy to read and less complex to understand. I append a single quote character either from a variable or use the CHR() function to insert the single quote character.
The same example inside PL/SQL I will use like following:
DECLARE
l_single_quote CHAR(1) := '''';
l_output VARCHAR2(20);
BEGIN
SELECT 'test single quote'||l_single_quote
INTO l_output FROM dual;
DBMS_OUTPUT.PUT_LINE(l_single_quote);
END;
The output above is same as the Method 1.
Now my favourite while in SQL is CHR(39) function. This is what I would have used personally:
SELECT 'test single quote'||CHR(39) FROM dual;
The output is same in all the cases.
Now don't ask me any other methods, when I come to know of any other methods I will share here.
PLSQL predefined exceptions
An internal exception is raised implicitly whenever your PL/SQL program violates an Oracle rule or exceeds a system-dependent limit. Every Oracle error has a number, but exceptions must be handled by name. So, PL/SQL predefines some common Oracle errors as exceptions. For example, PL/SQL raises the predefined exception NO_DATA_FOUND
if a SELECT
INTO
statement returns no rows.
To handle other Oracle errors, you can use the OTHERS
handler. The functions SQLCODE
and SQLERRM
are especially useful in the OTHERS
handler because they return the Oracle error code and message text. Alternatively, you can use the pragma EXCEPTION_INIT
to associate exception names with Oracle error codes.
PL/SQL declares predefined exceptions globally in package STANDARD
, which defines the PL/SQL environment. So, you need not declare them yourself. You can write handlers for predefined exceptions using the names shown in the list below. Also shown are the corresponding Oracle error codes and SQLCODE
return values.
ACCESS_INTO_NULL
COLLECTION_IS_NULL
CURSOR_ALREADY_OPEN
DUP_VAL_ON_INDEX
INVALID_CURSOR
INVALID_NUMBER
LOGIN_DENIED
NO_DATA_FOUND
NOT_LOGGED_ON
PROGRAM_ERROR
ROWTYPE_MISMATCH
SELF_IS_NULL
STORAGE_ERROR
SUBSCRIPT_BEYOND_COUNT
SUBSCRIPT_OUTSIDE_LIMIT
SYS_INVALID_ROWID
TIMEOUT_ON_RESOURCE
TOO_MANY_ROWS
VALUE_ERROR
ZERO_DIVIDE
For a brief description of these exceptions please check this link:
http://www.cs.umbc.edu/help/oracle8/server.815/a67842/06_errs.htm#784
Error logging using DBMS_ERRLOG
If your DML statement encounters an exception, it immediately rolls back any changes already made by that statement, and propagates an exception out to the calling block. Sometimes that's just what you want. Sometimes, however, you'd like to continue past such errors, and apply your DML logic to as many rows as possible.
DBMS_ERRLOG, a package introduced in Oracle10g Release 2, allows you to do precisely that. Here is a quick review of the way this package works.
First you need to create an error log table where the errors will be inserted, and while issuing any DML statements use a clause newly introduced in 10g, LOG ERRORS.
1. Create an error log table for the table against which you will execute DML statements:
BEGIN
DBMS_ERRLOG.create_error_log (dml_table_name => 'EMP');
END;
Oracle then creates a table named ERR$_EMP that contains error-related columns as well as VARCHAR2 columns for each of your table's columns (where it makes sense).
For example in our case, the EMP table has this structure:
EMPNO NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)
The ERR$_EMP table has this structure:
ORA_ERR_NUMBER$ NUMBER
ORA_ERR_MESG$ VARCHAR2(2000)
ORA_ERR_ROWID$ UROWID(4000)
ORA_ERR_OPTYP$ VARCHAR2(2)
ORA_ERR_TAG$ VARCHAR2(2000)
EMPNO VARCHAR2(4000)
ENAME VARCHAR2(4000)
JOB VARCHAR2(4000)
MGR VARCHAR2(4000)
HIREDATE VARCHAR2(4000)
SAL VARCHAR2(4000)
COMM VARCHAR2(4000)
DEPTNO VARCHAR2(4000)
From this we can understand that there are columns ORA_ERR_NUMBER$, ORA_ERR_MESG$, ORA_ERR_ROWID$, ORA_ERR_OPTYP$, ORA_ERR_TAG$ which will be created for extra information on the error encountered, the data at the time of the error etc.
2. Add the LOG ERRORS clause to your DML statement:
BEGIN
UPDATE emp
SET sal = sal * 2
LOG ERRORS REJECT LIMIT UNLIMITED;
END;
3. After running your code, you can check the contents of the ERR$ table to see if any errors occurred. You can then perform row by row recovery, or transfer that error information to your application error log, etc.
This approach will not only allow you to continue past exceptions; it will also greatly improve performance of DML processing in which lots of exceptions would normally be raised, because exception handling is quite slow compared to writing a row to a log table via an autonomous transaction procedure.
Check out DBMS_ERRLOG. It could offer some very powerful alternatives ways of executing large numbers of DML statements, each of which might change many rows.
ConclusionThe disadvantage of this approach is in maintenance. The user is likely to report errors like "my data is not showing up, and no errors are shown". My advice on using this feature is to have a highly sophasticated error handling mechanism that handles such errors and throw messages to users. This way the debugging person will also have exact data as to what caused the error. Unless used responsibly this feature can be a disaster.
LONG to BLOB Migration
Create the LOB Tablespace
CREATE TABLESPACE lob1
DATAFILE '/lh4/lob1.dbf' SIZE 2048064K REUSE
EXTENT MANAGEMENT LOCAL UNIFORM SIZE 50M
PERMANENT
ONLINE;
Disable temporarily all Foreign Keys
set feed off;
spool gen_dis_cons.sql;
SELECT 'ALTER TABLE ' table_name
' DISABLE CONSTRAINT ' constraint_name ';'
FROM user_constraints WHERE UPPER(constraint_name) like 'FK_%'
/
spool off;
set feed on;
@gen_dis_cons.sql;
Convert LONG to LOB in temporary Table
Create a temporary table with converted BLOB field.
CREATE TABLE lob_tmp
TABLESPACE tab
AS SELECT id, TO_LOB(bdata) bdata FROM document;
Drop and Rename Tables
DROP TABLE document;
RENAME lob_tmp TO document;
Create the necessary Constraints and enable the Foreign Keys again
set feed off;
set heading off;
spool gen_ena_cons.sql;
SELECT 'ALTER TABLE ' table_name
' ENABLE CONSTRAINT ' constraint_name ';'
FROM user_constraints WHERE UPPER(constraint_name) like 'FK_%'
/
spool off;
set feed on;
@gen_ena_cons.sql;
Courtesy: akadia
Read a file word by word using DBMS_LOB
Oracle offers several possibilities to process file from within PL/SQL. The most used package is UTL_FILE, but with the disadvantage that the read-buffer is limited to 1023 bytes. If you want to read huge chunks of files you can use the DBMS_LOB package, even for the processing of plain ASCII files.
There are two solutions to read a file with DBMS_LOB
-
The file is treaded as a large binary object LOB. The whole file is read and saved in a table column of the data type LOB and then processed.
-
The file is read and processed directly from the filesystem location. This Tip shows exactly this case.
Example:
Suppose we want to read a big file word by word directly from PL/SQL without saving the whole file in a table column. The words in the file are separated with a blank. For simplicity we assume, that there is exactly one blank between the words and the file is a stream with a newline at the end of the file.
First we have to create an ORACLE directory as the schema owner. Do not add a trailing "/" at the end of the directory path.
sqlplus scott/tiger
SQL> create directory READ_LOB_DIR as 'C:\Users\Zahn\Work';
Next we create the procedure READ_FILE_LOB, which reads the file word by word.
CREATE OR REPLACE
Procedure READ_FILE_LOB IS
----------------------------------------------------------------
-- Read an ASCII file word by word with DBMS_LOB package.
--
-- Before you can use this procedure create an ORACLE directory
-- object as the owner of this peocedure.
--
-- sqlplus scott/tiger
-- SQL> create directory READ_LOB_DIR as 'C:\Users\Zahn\Work';
--
-- Do not add a trailing "/" at the end of the directory path.
--
----------------------------------------------------------------
-- Input Directory as specified in create directory
l_dir CONSTANT VARCHAR2(30) := 'READ_LOB_DIR';
-- Input File which is read word by word
l_fil CONSTANT VARCHAR2(30) := 'testfile.txt';
-- Separator Character between words is a BLANK (ascii = 32)
l_seb CONSTANT RAW(100) := UTL_RAW.CAST_TO_RAW(CHR(32));
-- Character at the end of the file is NEWLINE (ascii = 10)
l_sen CONSTANT RAW(100) := UTL_RAW.CAST_TO_RAW(CHR(10));
-- Pointer to the BFILE
l_loc BFILE;
-- Current position in the file (file begins at position 1)
l_pos NUMBER := 1;
-- Amount of characters have been read
l_sum BINARY_INTEGER := 0;
-- Read Buffer
l_buf VARCHAR2(500);
-- End of the current word which will be read
l_end NUMBER;
-- Return value
l_ret BOOLEAN := FALSE;
BEGIN
-- Mapping the physical file with the pointer to the BFILE
l_loc := BFILENAME(l_dir,l_fil);
-- Check if the file exists
l_ret := DBMS_LOB.FILEEXISTS(l_loc) = 1;
IF (l_ret) THEN
dbms_output.put_line('File ' ||
l_fil || ' in Directory ' || l_dir || ' exists');
-- Open the file in READ_ONLY mode
DBMS_LOB.OPEN(l_loc,DBMS_LOB.LOB_READONLY);
LOOP
-- Calculate the end of the current word
l_end := DBMS_LOB.INSTR(l_loc,l_seb,l_pos,1);
-- Process end-of-file
IF (l_end = 0) THEN
l_end := DBMS_LOB.INSTR(l_loc,l_sen,l_pos,1);
l_sum := l_end - l_pos - 1;
DBMS_LOB.READ(l_loc,l_sum,l_pos,l_buf);
dbms_output.put_line(UTL_RAW.CAST_TO_VARCHAR2(l_buf));
EXIT;
END IF;
-- Read until end-of-file
l_sum := l_end - l_pos;
DBMS_LOB.READ(l_loc,l_sum,l_pos,l_buf);
dbms_output.put_line(UTL_RAW.CAST_TO_VARCHAR2(l_buf));
l_pos := l_pos + l_sum + 1;
END LOOP;
DBMS_LOB.CLOSE(l_loc);
ELSE
dbms_output.put_line('File ' ||
l_fil || ' in Directory ' || l_dir || ' does not exist');
END IF;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Error:' || SQLERRM);
DBMS_LOB.CLOSE(l_loc);
END;
/
The file testfile.txt has the following content
martin zahn seftigen
Output of the procedure
sqlplus scott/tiger
SQL> exec read_file_lob;
File testfile.txt in Directory READ_LOB_DIR exists
martin
zahn
seftigen
PL/SQL procedure successfully completed.
Article Source: akadia dot com
Which Code Runs Slower
Test Your PL/SQL Knowledge
This puzzler has come from Steven Feuerstein for the month of February 2008. So I thought to reproduce the puzzler with its answer:
The employees table in the production Oracle Database 10g Release 2 instance of the MassiveGlobalCorp company contains 2.5 million rows.
Below are three different blocks of code, each of which fetch all rows from this table and then "do stuff" with each fetched record. Which will run much slower than the other two, and why?
a.
DECLARE
CURSOR employees_cur IS SELECT * FROM employees;
BEGIN
FOR employee_rec IN employees_cur LOOP
do_stuff (employee_rec);
END LOOP ;
END;
b.
DECLARE
CURSOR employees_cur IS SELECT * FROM employees;
l_employee employees%ROWTYPE;
BEGIN
OPEN employees_cur;
LOOP
FETCH employees_cur INTO l_employee;
EXIT WHEN employees_cur%NOTFOUND;
do_stuff (l_employee);
END LOOP ;
CLOSE employees_cur;
END;
c.
DECLARE
CURSOR employees_cur IS SELECT * FROM employees;
TYPE employees_aat IS TABLE OF employees%ROWTYPE INDEX BY PLS_INTEGER;
l_employees employees_aat;
BEGIN
OPEN employees_cur;
LOOP
FETCH employees_cur
BULK COLLECT INTO l_employees LIMIT 100;
EXIT WHEN l_employees.COUNT () = 0;
FOR indx IN 1 .. l_employees.COUNT
LOOP
do_stuff (l_employees (indx));
END LOOP ;
END LOOP ;
CLOSE employees_cur;
END;
Scroll below to see the answer:
.
.
.
.
.
.
.
.
.
.
.
.
.
.
(b) Is the slowest. That's because on Oracle 10g and higher, the PL/SQL optimizer will automatically rewrite cursor FOR loops so that they are executed in the same way as the BULK COLLECT query.
Sounds Interesting???
Steven Feuerstein is considered one of the world's leading experts on the Oracle PL/SQL language, having written ten books on PL/SQL all published by O'Reilly Media, including Oracle PL/SQL Programming and Oracle PL/SQL Best Practices (all from O'Reilly Media).
Steven has been developing software since 1980, spent five years with Oracle (1987-1992), and serves as a PL/SQL Evangelist to Quest Software.
He publishes puzzlers every month and here is where you can see all of them.
Dynamic Ref Cursor with Dynamic Fetch
This tip comes from Zlatko Sirotic, Software Developer at Istra Informaticki Inzenjering d.o.o., in
Suppose you've got a function that is based on a dynamically generated query that returns a ref cursor variable. Now suppose you want to use this ref cursor variable in your procedure, but you don't know the record structure. So how do you make a "FETCH l_ref_cur INTO record_variable" when you don't know the record variable structure.
Because ref cursors do not (directly) support description, the solution is quite complicated and requires that the function (or package) returns not only a ref cursor variable but a (dynamically) generated query, too.
I am going to use "a good old" DBMS_SQL package and its PARSE and DESCRIBE_COLUMNS procedures in order to make an unknown record variable.
1. Make the "generic" package.
First I am going to make a "dyn_fetch" package in which the "describe_columns" procedure (using query recorded in a global "g_query" variable) creates a "g_desc_tab" PL/SQL table used by the "record_def" function for making a record structure:
CREATE OR REPLACE PACKAGE dyn_fetch IS
TYPE ref_cur_t IS REF CURSOR;
g_query VARCHAR2 (32000);
g_count NUMBER;
g_desc_tab DBMS_SQL.DESC_TAB;
varchar2_type CONSTANT PLS_INTEGER := 1;
number_type CONSTANT PLS_INTEGER := 2;
date_type CONSTANT PLS_INTEGER := 12;
rowid_type CONSTANT PLS_INTEGER := 11;
char_type CONSTANT PLS_INTEGER := 96;
long_type CONSTANT PLS_INTEGER := 8;
raw_type CONSTANT PLS_INTEGER := 23;
mlslabel_type CONSTANT PLS_INTEGER := 106;
clob_type CONSTANT PLS_INTEGER := 112;
blob_type CONSTANT PLS_INTEGER := 113;
bfile_type CONSTANT PLS_INTEGER := 114;
PROCEDURE describe_columns;
FUNCTION record_def RETURN VARCHAR2;
END;
/
CREATE OR REPLACE PACKAGE BODY dyn_fetch IS
PROCEDURE describe_columns IS
l_cur INTEGER;
BEGIN
l_cur := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE (l_cur, g_query, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS (l_cur, g_count, g_desc_tab);
DBMS_SQL.CLOSE_CURSOR (l_cur);
EXCEPTION
WHEN OTHERS THEN
IF DBMS_SQL.IS_OPEN (l_cur) THEN
DBMS_SQL.CLOSE_CURSOR (l_cur);
END IF;
RAISE;
END;
FUNCTION record_def RETURN VARCHAR2 IS
l_record_def VARCHAR2 (32000);
l_type VARCHAR2 (100);
l_col_type PLS_INTEGER;
l_col_max_len PLS_INTEGER;
l_col_precision PLS_INTEGER;
l_col_scale PLS_INTEGER;
BEGIN
FOR i IN 1..g_count LOOP
l_col_type := g_desc_tab(i).col_type;
l_col_max_len := g_desc_tab(i).col_max_len;
l_col_precision := g_desc_tab(i).col_precision;
l_col_scale := g_desc_tab(i).col_scale;
IF l_col_type = varchar2_type THEN
l_type := 'VARCHAR2(' || l_col_max_len || ')';
ELSIF l_col_type = number_type THEN
l_type := 'NUMBER(' || l_col_precision || ',' || l_col_scale || ')';
ELSIF l_col_type = date_type THEN
l_type := 'DATE';
ELSIF l_col_type = rowid_type THEN
l_type := 'ROWID';
ELSIF l_col_type = char_type THEN
l_type := 'CHAR(' || l_col_max_len || ')';
-- ELSIF l_col_type = ...
-- long_type, raw_type ...
END IF;
l_record_def := l_record_def || ' col_' || i || ' ' || l_type || ',';
END LOOP ;
l_record_def := RTRIM (l_record_def, ',');
RETURN l_record_def;
END;
END;
/
Note that the RECORD_DEF procedure creates column names as col_1 (col_2, ...) because the SELECT clause in the query can be without aliases, for example, "SELECT deptno || dname FROM dept".
2. Create the package that returns the query and ref cursor.
The function that returns the ref cursor variable should return the query, too. So it's better to make two separate functions and put them into the package.
The "set_query" procedure saves the query into the global package variable and the ref cursor is returned by the "ref_cur" function:
CREATE OR REPLACE PACKAGE test IS
PROCEDURE set_query (p_query VARCHAR2 := NULL);
FUNCTION ref_cur RETURN dyn_fetch.ref_cur_t;
END;
/
CREATE OR REPLACE PACKAGE BODY test IS
PROCEDURE set_query (p_query VARCHAR2 := NULL) IS
l_query VARCHAR2 (32000) :=
' SELECT e.empno, e.ename,' ||
' e.deptno, d.dname' ||
' FROM emp e,' ||
' dept d' ||
' WHERE e.deptno = d.deptno';
BEGIN
IF p_query IS NULL THEN
dyn_fetch.g_query := l_query;
ELSE
dyn_fetch.g_query := p_query;
END IF;
END;
FUNCTION ref_cur RETURN dyn_fetch.ref_cur_t IS
l_ref_cur dyn_fetch.ref_cur_t;
BEGIN
OPEN l_ref_cur FOR dyn_fetch.g_query;
RETURN l_ref_cur;
END;
END;
/
So why do I need two separate procedures (functions) in the package?
a) The receiving program must use dynamic SQL, but in the dynamic block I can access only PL/SQL code elements that have a global scope (standalone functions and procedures, and elements defined in the specification of a package). Unfortunately, cursor variables cannot be defined in the specification of a package (so they cannot be global variables).
b) The receiving program must get the column list before ref cursor.
So, there are two options:
a.) Call (in the receiving program) the same function two times (once to get the column list and once to return a ref cursor), or
b.) Use one procedure (or function) for returning query (to get the column list) and a second function for returning a ref cursor.
3. Create the receiving program.
Finally I create a procedure that reads the ref cursor. First, the procedure calls the "test.set_query" and "dyn_fetch.describe_columns" in order to get dynamically generated record structure through the "dyn_fetch.record_def" function and to get process definition through (internal) "process_def" function (in this case, to show rows with DBMS_SQL.PUT_LINE):
CREATE OR REPLACE PROCEDURE test_fetch_ref_cur (p_query VARCHAR2 :=
NULL) IS
l_statement VARCHAR2 (32000);
FUNCTION process_def RETURN VARCHAR2 IS
l_process_def VARCHAR2 (32000);
BEGIN
l_process_def := 'DBMS_OUTPUT.PUT_LINE (';
FOR i IN 1 .. dyn_fetch.g_count LOOP
l_process_def := l_process_def || ' l_record.col_' || i || ' || ''>>'' || ';
END LOOP ;
l_process_def := RTRIM (l_process_def, ' || ''>>'' || ') || ');';
RETURN l_process_def;
END;
BEGIN
test.set_query (p_query);
dyn_fetch.describe_columns;
l_statement :=
' DECLARE' ||
' TYPE record_t IS RECORD (' ||
dyn_fetch.record_def || ');' ||
' l_record record_t;' ||
' l_ref_cur dyn_fetch.ref_cur_t;' ||
' BEGIN' ||
' l_ref_cur := test.ref_cur;' ||
' LOOP ' ||
' FETCH l_ref_cur INTO l_record;' ||
' EXIT WHEN l_ref_cur%NOTFOUND;' ||
process_def ||
' END LOOP ;' ||
' CLOSE l_ref_cur;' ||
' END;';
EXECUTE IMMEDIATE l_statement;
END;
/
I can test this with:
SET SERVEROUTPUT ON;
EXECUTE test_fetch_ref_cur;
Note that I can try to use a more generic solution. If I take a look at the "test_fetch_ref_cur" procedure, I can see that the part I use for loop can be used in more cases. So I move this part into the "dyn_fetch" package, into the "fetch_ref_cur" procedure to which two parameters has to be sent: the process description and the function name that returns ref cursor.
Here's the changed package:
CREATE OR REPLACE PACKAGE dyn_fetch IS
-- SAME AS BEFORE, PLUS:
PROCEDURE fetch_ref_cur (
p_function_ref_cur VARCHAR2,
p_process_def VARCHAR2);
END;
/
CREATE OR REPLACE PACKAGE BODY dyn_fetch IS
-- SAME AS BEFORE, PLUS:
PROCEDURE fetch_ref_cur (
p_function_ref_cur VARCHAR2,
p_process_def VARCHAR2)
IS
l_statement VARCHAR2 (32000);
BEGIN
l_statement :=
' DECLARE' ||
' TYPE record_t IS RECORD (' ||
record_def || ');' ||
' l_record record_t;' ||
' l_ref_cur dyn_fetch.ref_cur_t;' ||
' BEGIN' ||
' l_ref_cur := ' ||
p_function_ref_cur || ';' ||
' LOOP ' ||
' FETCH l_ref_cur INTO l_record;' ||
' EXIT WHEN l_ref_cur%NOTFOUND;' ||
p_process_def ||
' END LOOP ;' ||
' CLOSE l_ref_cur;' ||
' END;';
EXECUTE IMMEDIATE l_statement;
END;
END;
/
And here's the changed procedure "test_fetch_ref_cur":
CREATE OR REPLACE PROCEDURE test_fetch_ref_cur (p_query VARCHAR2 :=
NULL) IS
FUNCTION process_def RETURN VARCHAR2 IS
-- SAME AS BEFORE
END;
BEGIN
test.set_query (p_query);
dyn_fetch.describe_columns;
dyn_fetch.fetch_ref_cur (
p_function_ref_cur => 'test.ref_cur',
p_process_def => process_def);
END;
/
Labels
- 1Z0-001 (1)
- 1Z0-007 (1)
- 1Z0-047 (1)
- 1Z0-051 (1)
- 1Z0-101 (1)
- 1Z0-131 (1)
- 1Z0-132 (1)
- Application Server (5)
- Certification (2)
- Database design (1)
- DB Administration (8)
- Deprecated (1)
- Download (3)
- Examples (29)
- Export Utility (1)
- FAQ (11)
- Features (7)
- Forms (8)
- FRM-Errors (1)
- HOW-To? (7)
- JInitiator (2)
- Migration (2)
- MS-SQL vs Oracle (1)
- OCP (1)
- ORA-Errors (14)
- Oracle Packages (1)
- Oracle Universal Installer (1)
- Oracle XE (1)
- OS Level (2)
- PL-SQL (20)
- Query (17)
- Reports (3)
- Scripts (4)
- Security (2)
- SQL (53)
- SQL *Plus (5)
- SQL Developer (1)
- Synonyms (2)
- Test yourself (2)
- Theoretical (15)
- Tips (41)
- Troubleshooting (7)
- Tuning (6)
- Tutorial (32)
- Utilities (5)
- XML (3)