Revised December 2021, published May 2017
Main menu – download 27 May 2017 I uploaded 1_pl_sq.zip Oracle 11g PL/SQL Tutorial
Oracle 11g PL/SQL Tutorial about testing developing DB procedures
- Three loops and three branches
- Function, procedure, package, array object
- Number, string, date, boolean
- DDL
- CRUD
- Ref Cursor pointer to cursor (ee to record set object variable) – December 2021
1. Three loops and three branches
/*
start ...path...02_02Hello_var_loop_if_exception_date.sql
O U T P U T :
~~~~~~~~ Three L O O P S ~~~~~~~~
--- loop1. LOOP,EXIT WHEN v_cntr>=2 (v_cntr=0,1)
Hello 28.05.2017
Hello 28.05.2017
---
--- loop2. FOR v_cntr IN 1..2 LOOP - IF v_cntr = 2 THEN
RAISE_APPLICATION_ERROR(-20001, v_errmsg);
- EXCEPTION - WHEN others THEN - IF sqlcode = -20001...
Hello
Hello , ***v_cntr=2 custom EXCEPTION !!!at!!! IF v_cntr=2 <-- !!
---
--- loop2a. FOR ii IN REVERSE 3..9 LOOP, mod(ii,3) != 0
ii=9
ii=6
ii=3
---
--- loop3. From WHILE v_cntr < 2 LOOP
Hello
Hello
---
---
~~~~~~~~ Three B R A N C H E S ~~~~~~~~
--- branch1. IF - ELSIF - ELSE - ENDIF
x message
---
--- branch2. searched CASE - WHEN - ELSE - END CASE
aaa
---
--- branch3. simple "CASE x" - WHEN - ELSE - END CASE (as PHP switch - case - default)
I don't know what v_msg is
*/
set linesize 200
set serveroutput oFF
set serveroutput on size 1000000
------------
DECLARE
v_cntr NUMBER := 0;
v_system_date CONSTANT DATE := sysdate;
v_errmsg VARCHAR2(255);
v_msg VARCHAR2(255) := 'Hello ';
BEGIN
msg('---');
msg('~~~~~~~~ Three L O O P S ~~~~~~~~');
msg('--- loop1. LOOP,EXIT WHEN v_cntr>=2 (v_cntr=0,1)');
LOOP
EXIT WHEN v_cntr >= 2; -- 2 loops : v_cntr = 0, 1
v_cntr := v_cntr + 1;
msg(v_msg || to_char(v_system_date, 'DD.MM.YYYY'));
END LOOP;
BEGIN
msg('---');
msg('--- loop2. FOR v_cntr IN 1..2 LOOP - IF v_cntr = 2
THEN RAISE_APPLICATION_ERROR(-20001, v_errmsg);');
msg(' - EXCEPTION - WHEN others THEN - IF sqlcode = -20001...');
FOR v_cntr IN 1..2 LOOP
IF v_cntr = 2 THEN
v_errmsg:=v_msg || ', ***v_cntr=2 custom EXCEPTION !!!at!!! IF v_cntr=2 <----------- !!!!!!!!';
RAISE_APPLICATION_ERROR(-20001, v_errmsg);
END IF;
msg(v_msg);
END LOOP;
EXCEPTION
WHEN no_data_found THEN msg(
'no_data_found EXCEPTION --- 2 From FOR v_cntr IN 1..2 LOOP');
WHEN others THEN
IF sqlcode = -20001 THEN msg(v_errmsg);
ELSE RAISE; -- go to end script err handler
END IF;
END;
--O U T P U T S :
--Hello
--Hello , ***v_cntr=2 custom EXCEPTION !!!at!!! IF v_cntr=2
msg('---');
msg('--- loop2a. FOR ii IN REVERSE 3..9 LOOP,
mod(ii,3) != 0');
DECLARE
v_increment NUMBER := 3;
BEGIN
FOR ii IN REVERSE 3..9 LOOP
IF mod(ii,v_increment) != 0 THEN CONTINUE; END IF;
msg('ii=' || ii);
END LOOP;
END;
msg('---');
msg('--- loop3. From WHILE v_cntr < 2 LOOP');
v_cntr := 0 ;
WHILE v_cntr < 2 LOOP
v_cntr := v_cntr + 1;
msg(v_msg);
END LOOP;
msg('---'); msg('---');
msg('~~~~~~~~ Three B R A N C H E S ~~~~~~~~');
msg('--- branch1. IF - ELSIF - ELSE - ENDIF');
v_msg := 'x message';
IF v_msg LIKE 'x%' THEN msg(v_msg);
ELSIF v_msg = 'aaa' THEN msg(v_msg);
ELSE msg('I don''t know what v_msg is');
END IF;
msg('---');
msg('--- branch2. searched CASE - WHEN - ELSE - END CASE');
v_msg := 'aaa';
CASE
WHEN v_msg LIKE 'x%' THEN msg(v_msg);
WHEN v_msg = 'aaa' THEN msg(v_msg);
ELSE msg('I don''t know what v_msg is');
END CASE;
msg('---');
msg('--- branch3. simple "CASE x" - WHEN - ELSE - END CASE
(as PHP switch - case - default)');
v_msg := 'x';
CASE v_msg -- same as PHP switch - case - default
WHEN 'aaa' THEN msg(v_msg);
ELSE msg('I don''t know what v_msg is');
END CASE;
END;
/
set serveroutput oFF
go top
2. Function, procedure, package, array object
/*
start ...path...\02_03fn_proc_pck_assoc_arr_object.sql
O U T P U T :
--- 1. fn return_1 SAYS: 1
DML A S S O C I A T I V E A R R A Y
--- 2. anonymus pl/sql block SAYS: DML associative_array v_array(2)=Hello Again!
--------2.1 robust loop through array
Hello World!
Hello Again!
--------2.2 simple loop through array
Hello World!
Hello Again!
--------2.3 robust loop through array
Doe
King
--------2.4 robust loop through array with delete
Hello World! Date and time: Sunday on 28 May, 2017 @ 08:12:34
--- 3.1 anonymus pl/sql block SAYS: DDL object_o_person v_person.lname=Doe2
--- 3.2 anonymus pl/sql block SAYS: DDL object_o_person v_person(1).lname=Doe3
--- 4. my_package.crerow_t; and read it
This is a message
--- 5. print business days
Tuesday 4 of July, 2017
Monday 25 of December, 2017
*/
set linesize 200
set serveroutput OFF
set serveroutput on size 1000000
------------
-- c a l l f u n c t i o n :
--BEGIN msg('---aaaaa'); END;
BEGIN msg('--- 1. fn return_1 SAYS: '||return_1); END;
/
-- c a l l p r o c e d u r e :
--BEGIN insert_a_rec(...); END;
-- DML associative_array
DECLARE
TYPE t_array IS TABLE OF VARCHAR2(30) INDEX BY BINARY_INTEGER;
v_array t_array;
v_index BINARY_INTEGER;
BEGIN
v_array(1) := 'Hello World!';
v_array(2) := 'Hello Again!';
msg('DML A S S O C I A T I V E A R R A Y');
msg('--- 2. anonymus pl/sql block SAYS: DML associative_array v_array(2)='||v_array(2));
msg('--------2.1 robust loop through array');
v_index := v_array.FIRST;
LOOP
EXIT WHEN v_index IS NULL;
msg( v_array( v_index ) );
v_index := v_array.NEXT(v_index);
END LOOP;
msg('--------2.2 simple loop through array');
FOR ii IN v_array.FIRST..v_array.lAST
LOOP
msg( v_array(ii) );
END LOOP;
END;
/
DECLARE
TYPE t_varchar2 IS TABLE OF VARCHAR2(100)
INDEX BY BINARY_INTEGER;
TYPE t_number IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;
v_first_name t_varchar2;
v_last_name t_varchar2;
v_salary t_number;
v_index BINARY_INTEGER;
BEGIN
SELECT first_name, last_name, salary
BULK COLLECT INTO v_first_name, v_last_name, v_salary
FROM employees where rownum < 3;
msg('--------2.3 robust loop through array');
v_index := v_last_name.FIRST;
LOOP
EXIT WHEN v_index IS NULL;
msg( v_last_name( v_index ) );
v_index := v_last_name.NEXT(v_index);
END LOOP;
END;
/
DECLARE
TYPE t_array IS TABLE OF VARCHAR2(30);
v_array t_array;
v_index BINARY_INTEGER;
v_date DATE := SYSDATE;
BEGIN
msg('--------2.4 robust loop through array with delete');
v_array := t_array();
v_array.extend; v_array(1) := 'Hello World!';
v_array.extend; v_array(2) := 'Hello Again!';
v_array.DELETE(2);
v_index := v_array.FIRST;
LOOP
EXIT WHEN v_index IS NULL;
--msg( v_array( v_index ) );
msg(
v_array( v_index ) ||
' Date and time: ' ||
to_char(v_date, 'Day') || ' on ' ||
to_char(v_date, 'FMDD Month, YYYY') ||
' @ ' ||
to_char(v_date, 'HH24:MI:SS')
);
v_index := v_array.NEXT(v_index);
END LOOP;
END;
/
--------
DECLARE
v_person o_person;
BEGIN
v_person := o_person('John', 'Doe', 21);
v_person := o_person('John2', 'Doe2', 22);
msg('--- 3.1 anonymus pl/sql block SAYS: DDL object_o_person v_person.lname='||v_person.lname);
END;
/
DECLARE
v_person o_person_collec := o_person_collec(); -- table
BEGIN
v_person.extend;
--row is TYPE OBJECT :
v_person(1) := o_person('John3', 'Doe3', 23);
msg('--- 3.2 anonymus pl/sql block SAYS: DDL object_o_person v_person(1).lname='||v_person(1).lname);
END;
/
BEGIN
msg('--- 4. my_package.crerow_t; and read it');
--in sql+ : exec my_package.delrow_t('xx')
my_package.crerow_t;
for rx in (select * from t where country_id = 'xx') loop
msg( rx.country_name );
end loop;
END;
/
begin
msg('--- 5. print business days ');
print_business_days(
to_date('31-DEC-2016', 'DD-MON-YYYY'),
to_date('31-DEC-2017', 'DD-MON-YYYY')
);
end;
/
set serveroutput oFF
go top
3. Number, string, date, boolean
-- ...path...03_01num_string_date_boolean.sql
-- 1. Valid numbers
DECLARE
vNum NUMBER;
BEGIN
vNum := 100;
vNum := 98989898989898;
vNum := 0.00000000000000001;
vNum := 10928383.9028282772722626262;
END;
/
-- 2. CHAR versus VARCHAR2
DECLARE
vc2_Name VARCHAR2(10) := 'Lewis';
char_Name CHAR(10) := 'Lewis';
BEGIN
IF vc2_Name = char_Name THEN
dbms_output.put_line('VARCHAR2, CHAR Variables Match');
ELSE dbms_output.put_line(
'VARCHAR2, CHAR Variables Do Not Match');
END IF;
END;
/
-- 3. String to number conversion
DECLARE
v_string_var VARCHAR2(10) := '15';
v_number_var NUMBER;
BEGIN
v_number_var := TO_NUMBER(v_string_var);
END;
-- 4. Date conversion
DECLARE
v_string VARCHAR2(30) := '10/30/1998 12:34:03 PM';
v_date DATE;
BEGIN
v_date := to_date(v_string, 'MM/DD/YYYY HH:MI:SS AM');
v_date := to_date(v_string, 'DD.MM.YYYY HH24:MI:SS');
END;
/
-- 5. Timestamp conversion
DECLARE
v_string VARCHAR2(30) := '10/30/1998 12:34:03.987654 PM';
v_date timestamp;
BEGIN
v_date := to_timestamp(v_string, 'MM/DD/YYYY HH:MI:SS.FF AM');
END;
-- 6. Date to char conversion
DECLARE
v_string VARCHAR2(30) := '10/30/1998 12:34:03 PM';
v_date DATE;
BEGIN
v_date := to_date(v_string, 'MM/DD/YYYY HH:MI:SS AM');
dbms_output.put_line( to_char(v_date, 'FMDD Month, YYYY') );
END;
-- 7.1 BOOLEAN good example of direct usage
DECLARE
v_var1 NUMBER := 0;
v_var2 NUMBER := 1;
BEGIN
IF v_var1 = v_var2 THEN NULL; END IF;
END;
-- 7.2 BOOLEAN good example for variable reuse
DECLARE
v_boolean BOOLEAN;
v_var1 NUMBER := 0;
v_var2 NUMBER := 1;
BEGIN
v_boolean := v_var1 = v_var2;
IF v_boolean THEN NULL; END IF;
END;
-- 7.3 BOOLEAN Poor Example
DECLARE
v_boolean BOOLEAN;
v_var1 NUMBER := 0;
v_var2 NUMBER := 1;
BEGIN
IF v_var1 = v_var2 THEN v_boolean := TRUE;
ELSE v_boolean := FALSE;
END IF;
IF v_boolean THEN NULL; END IF;
END;
/
go top
4. DDL
/*
HR@ora7 27.05.2017 21:21:16> start ...path...\01_02DDL.sql
O U T P U T :
~~~~~ 1. c r e LOG_ TABLE ~~~~~
~~~~~ 2. c r e tbl t2 ~~~~~
~~~~~ 3. c r e tbl emp_names_nds ~~~~~
~~~~~ 1. c r e p r o c e d u r e m s g ~~~~~
~~~~~ 2. c r e p r o c e d u r e l o g i t ~~~~~
~~~~~ 3. c r e p r o c PRINT_ BUSINESS_ DAYS ~~~~~
~~~~~ 4. cre fn return_1 ~~~~~
~~~~~ 5. cre R O W TYPE o_ person AS OBJECT ~~~~~
~~~~~ 6. cre T B L TYPE o_ person_ collec AS TABLE OF ~~
~~~~~ 6.1 cre pck my_ package ~~~~~
~~~~~ 6.2 cre pck body my_ package ~~~~~
~~~~~ 7.1 cre pck t2_ dyn_ api ~~~~~
~~~~~ 7.2 cre pck body t2_ dyn_ api ~~~~~
*/
drop TABLE LOG_TABLE;
prompt ~~~~~ 1. c r e LOG_ TABLE ~~~~~
CREATE TABLE LOG_TABLE
(
DATUM DATE,
MESSAGE VARCHAR2(255 BYTE)
) ;
--insert into log_table (date_and_time, message)
-- VALUES (sysdate, 'Hello World!');
prompt ~~~~~ 2. c r e tbl t2 ~~~~~
drop TABLE t2 ;
BEGIN
EXECUTE IMMEDIATE
'CREATE TABLE t2 (
col1 VARCHAR2(25)
, col2 VARCHAR2(25)
, col3 VARCHAR2(25)
)';
END;
/
prompt ~~~~~ 3. c r e tbl emp_names_nds ~~~~~
drop TABLE emp_names_nds;
CREATE TABLE emp_names_nds (
last_name VARCHAR2(25), first_name VARCHAR2(20) );
prompt ~~~~~ 1. c r e p r o c e d u r e m s g ~~~~~
CREATE OR REPLACE PROCEDURE msg(
p_msg IN VARCHAR2 DEFAULT 'NO MESSAGE')
AS
BEGIN
dbms_output.put_line(p_msg);
END;
/
--show error
prompt ~~~~~ 2. c r e p r o c e d u r e l o g i t ~~~~~
create or replace PROCEDURE logit(
v_message IN VARCHAR2 DEFAULT 'Hello World!',
v_output_target IN VARCHAR2 DEFAULT 'T')
AS
-- v_output target may be T for table or
-- D for dbms_output
PRAGMA AUTONOMOUS_TRANSACTION;
v_date DATE := SYSDATE;
PROCEDURE do_output(
v_message IN VARCHAR2,
v_date IN DATE )
AS
BEGIN
DBMS_OUTPUT.put_line(
v_message ||
' On date ' ||
to_char(v_date, 'Day') || ' on ' ||
to_char(v_date, 'FMDD Month, YYYY') ||
' @ ' ||
to_char(v_date, 'HH24:MI:SS')
);
END;
PROCEDURE do_insert(
v_message IN VARCHAR2,
v_date IN DATE )
AS
BEGIN
insert into log_table
(datum, message)
VALUES (v_date, v_message);
END;
-------------
BEGIN
CASE
WHEN v_output_target = 'T'
THEN
do_insert(v_message, v_date);
WHEN v_output_target = 'D'
THEN
do_output(v_message, v_date);
WHEN v_output_target = 'TD'
OR v_output_target = 'DT'
THEN
do_insert(v_message, v_date);
do_output(v_message, v_date);
ELSE
logit('ERROR v_output_target: ' ||
v_output_target ||
' not found.', 'T' );
END CASE;
COMMIT;
END logit;
/
prompt ~~~~~ 3. c r e p r o c PRINT_ BUSINESS_ DAYS ~~~~~
create or replace PROCEDURE PRINT_BUSINESS_DAYS
(
P_START_DATE IN DATE
, P_END_DATE IN DATE
)
AS
TYPE t_holidays IS TABLE OF VARCHAR2(100)
INDEX BY VARCHAR2(30);
v_dates t_holidays;
v_loop_increment NUMBER := 0;
BEGIN
v_dates('01-JAN') := 'Mew Years';
v_dates('04-JUL') := 'Independence Day';
v_dates('25-DEC') := 'Christmas Day';
LOOP
EXIT WHEN p_start_date + v_loop_increment > p_end_date;
/*
IF to_number(to_char(p_start_date
+ v_loop_increment, 'd'))
IN (2,3,4,5,6)
THEN
dbms_output.put_line(to_char(p_start_date
+ v_loop_increment, 'FMDay DD "of" Month, YYYY'));
END IF;
*/
-- or :
IF v_dates.EXISTS(
to_char(p_start_date + v_loop_increment, 'DD-MON'))
THEN
IF to_number(to_char(p_start_date
+ v_loop_increment, 'd'))
IN (2,3,4,5,6)
THEN
dbms_output.put_line(to_char(p_start_date
+ v_loop_increment, 'FMDay DD "of" Month, YYYY'));
END IF;
END IF;
v_loop_increment := v_loop_increment + 1;
END LOOP;
END PRINT_BUSINESS_DAYS;
/
prompt ~~~~~ 4. cre fn return_1 ~~~~~
CREATE OR REPLACE FUNCTION return_1
RETURN NUMBER
AS
BEGIN
RETURN 1;
END;
/
--show error
prompt ~~~~~ 5. cre R O W TYPE o_ person AS OBJECT ~~~~~
drop TYPE o_person_collec ;
CREATE OR REPLACE TYPE o_person AS OBJECT (
fname VARCHAR2(30),
lname VARCHAR2(30),
age NUMBER
);
/
--show error
prompt ~~~~~ 6. cre T B L TYPE o_ person_ collec AS TABLE OF ~~
CREATE OR REPLACE TYPE o_person_collec --table
AS TABLE OF o_person; --row is TYPE OBJECT
/
--show error
prompt ~~~~~ 6.1 cre pck my_ package ~~~~~
CREATE OR REPLACE PACKAGE my_package
AS
FUNCTION return_1 RETURN NUMBER;
PROCEDURE crerow_t;
PROCEDURE delrow_t(p_id in varchar2);
END;
/
prompt ~~~~~ 6.2 cre pck body my_ package ~~~~~
CREATE OR REPLACE PACKAGE BODY my_package
AS
FUNCTION return_1 RETURN NUMBER AS
BEGIN
RETURN 1;
END;
PROCEDURE crerow_t AS
BEGIN
INSERT INTO t (country_id, country_name)
VALUES ('xx', 'This is a message');
END;
PROCEDURE delrow_t(p_id in varchar2) AS
BEGIN
delete t where country_id = p_id; --'xx'
END;
END;
/
prompt ~~~~~ 7.1 cre pck t2_ dyn_ api ~~~~~
create or replace PACKAGE t2_dyn_api IS
PROCEDURE insert_row(
p_col1 IN t2.col1%TYPE,
p_col2 IN t2.col2%TYPE,
p_col3 IN t2.col3%TYPE
);
PROCEDURE update_row(
p_col1 IN t2.col1%TYPE,
p_col2 IN t2.col2%TYPE,
p_col3 IN t2.col3%TYPE
);
PROCEDURE delete_row(
p_col1 IN t2.col1%TYPE,
p_col2 IN t2.col2%TYPE,
p_col3 IN t2.col3%TYPE
);
END;
/
prompt ~~~~~ 7.2 cre pck body t2_ dyn_ api ~~~~~
create or replace PACKAGE BODY t2_dyn_api IS
PROCEDURE insert_row(
p_col1 IN t2.col1%TYPE,
p_col2 IN t2.col2%TYPE,
p_col3 IN t2.col3%TYPE
)
AS
v_dml_string CLOB;
-- DBMS_SQL variables
v_cursor_id NUMBER;
v_rows_fetched NUMBER;
BEGIN
v_cursor_id := DBMS_SQL.open_cursor;
v_dml_string := 'INSERT INTO t2 (col1, col2, col3) ';
v_dml_string := v_dml_string || 'VALUES (:col1
, :col2, :col3) ';
-- Display the string
logit( v_dml_string, 'D');
DBMS_SQL.PARSE(v_cursor_id, v_dml_string
, DBMS_SQL.NATIVE);
DBMS_SQL.bind_variable( v_cursor_id, 'col1', p_col1);
DBMS_SQL.bind_variable( v_cursor_id, 'col2', p_col2);
DBMS_SQL.bind_variable( v_cursor_id, 'col3', p_col3);
v_rows_fetched := DBMS_SQL.EXECUTE(v_cursor_id);
DBMS_SQL.CLOSE_CURSOR(v_cursor_id);
COMMIT;
logit( 'Rows Fetched: ' || to_char(v_rows_fetched), 'D');
END;
PROCEDURE update_row(
p_col1 IN t2.col1%TYPE,
p_col2 IN t2.col2%TYPE,
p_col3 IN t2.col3%TYPE
)
AS
v_dml_string CLOB;
-- DBMS_SQL variables
v_cursor_id NUMBER;
v_rows_fetched NUMBER;
BEGIN
v_cursor_id := DBMS_SQL.open_cursor;
v_dml_string := 'UPDATE t2 ';
v_dml_string := v_dml_string ||
'SET col1 = :col1,
col2 = :col2,
col3 = :col3 ';
-- Display the string
logit( v_dml_string, 'D');
DBMS_SQL.PARSE(v_cursor_id, v_dml_string
, DBMS_SQL.NATIVE);
DBMS_SQL.bind_variable( v_cursor_id, 'col1', p_col1);
DBMS_SQL.bind_variable( v_cursor_id, 'col2', p_col2);
DBMS_SQL.bind_variable( v_cursor_id, 'col3', p_col3);
v_rows_fetched := DBMS_SQL.EXECUTE(v_cursor_id);
DBMS_SQL.CLOSE_CURSOR(v_cursor_id);
COMMIT;
logit( 'Rows Fetched: ' || to_char(v_rows_fetched), 'D');
END;
PROCEDURE delete_row(
p_col1 IN t2.col1%TYPE,
p_col2 IN t2.col2%TYPE,
p_col3 IN t2.col3%TYPE
)
AS
v_dml_string CLOB;
-- DBMS_SQL variables
v_cursor_id NUMBER;
v_rows_fetched NUMBER;
BEGIN
v_cursor_id := DBMS_SQL.open_cursor;
v_dml_string := 'DELETE FROM t2 ';
v_dml_string := v_dml_string ||
'WHERE col1 = :col1 AND
col2 = :col2 AND
col3 = :col3 ';
-- Display the string
logit( v_dml_string, 'D');
DBMS_SQL.PARSE(v_cursor_id, v_dml_string
, DBMS_SQL.NATIVE);
DBMS_SQL.bind_variable( v_cursor_id, 'col1', p_col1);
DBMS_SQL.bind_variable( v_cursor_id, 'col2', p_col2);
DBMS_SQL.bind_variable( v_cursor_id, 'col3', p_col3);
v_rows_fetched := DBMS_SQL.EXECUTE(v_cursor_id);
DBMS_SQL.CLOSE_CURSOR(v_cursor_id);
COMMIT;
logit( 'Rows Fetched: ' || to_char(v_rows_fetched), 'D');
END;
END t2_dyn_api;
/
/*
-- ORA-01031: insufficient privileges
CREATE OR REPLACE PROCEDURE CREATE_TABLE
(
P_TABLE_NAME IN VARCHAR2
, P_COLUMNS IN DBMS_SQL.varchar2a
) AS
v_ddl_string CLOB;
v_index PLS_INTEGER;
-- DBMS_SQL variables
v_cursor_id NUMBER;
v_rows_fetched NUMBER;
BEGIN
IF p_table_name IS NULL OR p_columns.COUNT = 0
THEN RETURN; END IF;
v_ddl_string := 'CREATE TABLE ';
v_ddl_string := v_ddl_string || p_table_name || '( ';
v_index := p_columns.FIRST;
LOOP
EXIT WHEN v_index IS NULL;
IF v_index != p_columns.FIRST
THEN
v_ddl_string := v_ddl_string || ', ';
END IF;
v_ddl_string := v_ddl_string || p_columns(v_index);
v_index := P_columns.NEXT(v_index);
END LOOP;
v_ddl_string := v_ddl_string || ')';
-- Display the string
logit( v_ddl_string, 'D');
-- Create the table
v_cursor_id := DBMS_SQL.open_cursor;
DBMS_SQL.PARSE(v_cursor_id, v_ddl_string, DBMS_SQL.NATIVE);
v_rows_fetched := DBMS_SQL.EXECUTE(v_cursor_id);
DBMS_SQL.CLOSE_CURSOR(v_cursor_id);
END CREATE_TABLE;
/
DECLARE
v_table_name VARCHAR2(30) := 't3';
v_columns DBMS_SQL.varchar2a;
BEGIN
v_columns(1) := 'COL1 VARCHAR2(10)';
v_columns(5) := 'COL2 VARCHAR2(2)';
v_columns(10) := 'COL3 NUMBER';
-- ORA-01031: insufficient privileges
CREATE_TABLE(v_table_name, v_columns);
END;
/
*/
go top
5. CRUD
/*
start ...path...\02_01CRUd_rowtype.sql
O U T P U T :
--- 1. CRUD R employee_id= 115
r_person.salary=3246,86
--- 2. CRUD U employee_id =115
r_person.salary*,95=3084,52
r_person.LAST_NAME=Khoo
--- 3. CRUD D (if exists) employee_id=999
--- 4. CRUD C employee_id=999
r_person.salary=3084,52
r_person.LAST_NAME=Doe
*/
set linesize 200
set serveroutput oFF
set serveroutput on size 1000000
------------
/*
-- rowtype cursor
DECLARE
CURSOR c1 IS
SELECT first_name, last_name, salary
FROM employees;
r_person c1%ROWTYPE;
BEGIN
r_person.first_name := 'John';
r_person.last_name := 'Doe';
r_person.salary := 2200.00;
END;
-- rowtype record
DECLARE
r_person employees%ROWTYPE;
BEGIN
r_person.first_name := 'John';
r_person.last_name := 'Doe';
r_person.salary := 2200.00;
END;
*/
BEGIN
EXECUTE IMMEDIATE 'BEGIN logit(:mybindvar, :displaymode);
END;' USING 'This is my dynamic message.', 'D';
END;
delete t2;
BEGIN
execute immediate 'insert into t2 (col1, col2)
values (:col1val, :col2val)' using 'A', 'B';
logit('Created Rows: ' || SQL%ROWCOUNT, 'D' );
execute immediate 'update t2 set col1 = :newcol1
where col2 = :oldcol2' using 'C', 'B';
logit('Updated Rows: ' || SQL%ROWCOUNT, 'D' );
execute immediate
'delete from t2 where col2 = :oldcol2' using 'B';
logit('Deleted Rows: ' || SQL%ROWCOUNT, 'D' );
END;
select * from emp_names_nds;
delete t2;
BEGIN
t2_dyn_api.insert_row('A', 'B', 1);
t2_dyn_api.insert_row('D', 'E', 2);
t2_dyn_api.insert_row('G', 'H', 3);
END;
select * from t2;
BEGIN t2_dyn_api.update_row('X', 'B', 1); END;
select * from t2;
BEGIN t2_dyn_api.delete_row('X', 'B', 1); END;
select * from t2;
DECLARE v_output VARCHAR2(1);
BEGIN
execute immediate 'insert into t2 (col1, col2)
values (:col1val, :col2val) RETURNING col1 INTO :colret'
using 'A', 'B', OUT v_output;
logit('v_output: ' || v_output, 'D' );
END;
DECLARE
v_output VARCHAR2(1);
BEGIN
execute immediate
'insert into t2 (col1, col2)
values (:col1val, :col2val)
RETURNING col1 INTO :colret'
using 'A', 'B'
RETURNING INTO v_output;
logit('v_output: ' || v_output, 'D' );
END;
DECLARE
v_dml_sel VARCHAR2(8000) := '
SELECT last_name, first_name FROM employees
WHERE rownum < :rowsproc';
TYPE r_emp IS RECORD (
last_name VARCHAR2(25),
first_name VARCHAR2(20) );
TYPE v_emp_tbltyp IS TABLE OF r_emp;
v_emp_tbl v_emp_tbltyp;
BEGIN
EXECUTE IMMEDIATE v_dml_sel BULK COLLECT INTO v_emp_tbl
USING 10;
FORALL ii IN v_emp_tbl.FIRST..v_emp_tbl.LAST
EXECUTE IMMEDIATE 'INSERT INTO emp_names_nds (last_name, first_name) VALUES (:lastname, :firstname)'
USING v_emp_tbl(ii).last_name, v_emp_tbl(ii).first_name;
END;
select * from emp_names_nds;
--delete emp_names_nds; -- 99 deleted
-- measure bind performance
declare
v_char_null varchar2(10);
v_num_null number;
v_not_null number := 0;
v_value number;
v_loop_cnt PLS_INTEGER := 10000;
v_start_time timestamp(9);
v_end_time timestamp(9);
v_rand number := abs(dbms_random.random);
v_sql_stmt VARCHAR2(1000);
begin
v_loop_cnt := v_loop_cnt + v_rand;
dbms_output.put_line('Random: ' || to_char(v_rand) );
v_start_time := systimestamp;
FOR i IN v_rand..v_loop_cnt
LOOP
v_sql_stmt := 'SELECT NVL(to_number(''''), to_number(''' || i || ''')) FROM DUAL';
EXECUTE IMMEDIATE v_sql_stmt INTO v_value;
END LOOP;
v_end_time := systimestamp;
dbms_output.put_line( 'No Binds, Conversion ' ||
to_char(v_end_time - v_start_time) );
v_start_time := systimestamp;
FOR i IN v_rand..v_loop_cnt
LOOP
v_sql_stmt := 'SELECT NVL(null, ' || i || ') FROM DUAL';
EXECUTE IMMEDIATE v_sql_stmt INTO v_value;
END LOOP;
v_end_time := systimestamp;
dbms_output.put_line( 'No Binds, No Conversion ' ||
to_char(v_end_time - v_start_time) );
v_start_time := systimestamp;
v_sql_stmt := 'SELECT NVL(:v_num_null, :v_not_null) FROM DUAL' ;
FOR i IN v_rand..v_loop_cnt
LOOP
v_not_null := i;
EXECUTE IMMEDIATE v_sql_stmt INTO v_value USING IN v_num_null, IN v_not_null;
END LOOP;
v_end_time := systimestamp;
dbms_output.put_line( 'Binds, No Conversion ' ||
to_char(v_end_time - v_start_time) );
v_start_time := systimestamp;
FOR i IN v_rand..v_loop_cnt
LOOP
v_not_null := i;
SELECT NVL(v_num_null, v_not_null) INTO v_value FROM DUAL;
END LOOP;
v_end_time := systimestamp;
dbms_output.put_line( 'Not Dynamic ' ||
to_char(v_end_time - v_start_time) );
end;
DECLARE
cursor c_person(c_id in number) is
SELECT * FROM employees WHERE employee_id = c_id;
r_person employees%ROWTYPE;
v_koef NUMBER;
v_upd_employee_id NUMBER;
v_cre_employee_id NUMBER;
BEGIN
v_koef := 0.95;
v_upd_employee_id := 115;
v_cre_employee_id := 999;
-- 1. r e a d
for rx in c_person(v_upd_employee_id) loop
r_person := rx ;
end loop ;
dbms_output.put_line(
'--- 1. CRUD R employee_id= '
||v_upd_employee_id||chr(10)
||'r_person.salary='||r_person.salary);
-- 2. u p d a t e
r_person.salary := r_person.salary * v_koef ;
UPDATE employees SET salary = r_person.salary
WHERE employee_id = r_person.employee_id;
for rx in c_person(v_upd_employee_id) loop
r_person := rx ;
end loop ;
dbms_output.put_line(
'--- 2. CRUD U employee_id ='
||v_upd_employee_id||chr(10)
||'r_person.salary*'||v_koef||'='
||r_person.salary
||chr(10)
||'r_person.LAST_NAME='||r_person.LAST_NAME
);
-- 3. d e l e t e
BEGIN DELETE employees WHERE employee_id = v_cre_employee_id;
EXCEPTION WHEN others THEN null; END;
dbms_output.put_line(
'--- 3. CRUD D (if exists) employee_id='||v_cre_employee_id);
-- 4. c r e a t e
r_person.employee_id := v_cre_employee_id;
r_person.last_name := 'Doe';
r_person.first_name := 'John';
r_person.email := '[email protected]';
INSERT INTO employees VALUES r_person;
for rx in c_person(v_cre_employee_id) loop
r_person := rx ;
end loop ;
dbms_output.put_line(
'--- 4. CRUD C employee_id='||v_cre_employee_id
||chr(10)
||'r_person.salary='||r_person.salary||chr(10)
||'r_person.LAST_NAME='||r_person.LAST_NAME
);
BEGIN
--logit;
BEGIN
logit('Inserted new employee_id='||v_cre_employee_id);
DECLARE
v_a_different_msg VARCHAR2(100);
BEGIN
rollback; -- Notice rollback
v_a_different_msg := 'Inserted new employee_id='||v_cre_employee_id;
logit( v_a_different_msg );
END;
END;
END;
---------------
END;
/ DECLARE
v_dml_sel VARCHAR2(8000) :=
'SELECT * FROM employees WHERE rownum < :rnum1';
TYPE v_emp_tbltyp IS TABLE OF employees%ROWTYPE;
v_emp_tbl v_emp_tbltyp;
BEGIN
EXECUTE IMMEDIATE
v_dml_sel BULK COLLECT INTO v_emp_tbl USING 3; --rnum1
FOR ii IN 1..v_emp_tbl.LAST
LOOP msg('On Loop: '||ii||', '||v_emp_tbl(ii).last_name);
END LOOP;
END;
DECLARE
v_dml_sel VARCHAR2(8000) :=
'SELECT first_name, last_name, email, phone_number, hire_date
FROM employees WHERE employee_id = :p_employee_id';
r_emp employees%ROWTYPE;
BEGIN
r_emp.employee_id := 101;
execute immediate v_dml_sel
into r_emp.last_name,
r_emp.first_name,
r_emp.email,
r_emp.phone_number,
r_emp.hire_date
using r_emp.employee_id;
logit(r_emp.last_name||', email='||r_emp.email, 'D');
END;
DECLARE
v_dml_sel VARCHAR2(8000) :=
'SELECT first_name, last_name, email, phone_number, hire_date
FROM employees WHERE employee_id = :p_employee_id';
--r_emp_refcurs_id NUMBER;
r_emp_refcurs sys_refcursor;
r_emp employees%ROWTYPE;
--v_rows_fetched NUMBER;
BEGIN
--r_emp_refcurs_id := DBMS_SQL.open_cursor;
OPEN r_emp_refcurs FOR v_dml_sel USING 101;
-- see (1)
LOOP
FETCH r_emp_refcurs INTO
r_emp.first_name,
r_emp.last_name,
r_emp.email,
r_emp.phone_number,
r_emp.hire_date;
EXIT WHEN r_emp_refcurs%NOTFOUND;
-- see (2)
END LOOP;
--DBMS_SQL.close_cursor(r_emp_refcurs_id);
CLOSE r_emp_refcurs;
logit(r_emp.last_name||', email='||r_emp.email, 'D');
END;
-- see (1)
/*
DBMS_SQL.parse(r_emp_refcurs_id, v_dml_sel, DBMS_SQL.native );
DBMS_SQL.define_column( r_emp_refcurs_id, 1, r_emp.first_name, 20);
DBMS_SQL.define_column( r_emp_refcurs_id, 2, r_emp.last_name, 25);
DBMS_SQL.define_column( r_emp_refcurs_id, 3, r_emp.email, 20);
DBMS_SQL.define_column( r_emp_refcurs_id, 4, r_emp.phone_number, 25);
DBMS_SQL.define_column( r_emp_refcurs_id, 5, r_emp.hire_date);
DBMS_SQL.bind_variable( r_emp_refcurs_id, 'p_employee_id', 101);
v_rows_fetched := DBMS_SQL.execute(r_emp_refcurs_id);
*/
-- see (2)
/*
IF DBMS_SQL.FETCH_ROWS(r_emp_refcurs_id)> 0 THEN
DBMS_SQL.COLUMN_VALUE( r_emp_refcurs_id, 1, r_emp.first_name);
DBMS_SQL.COLUMN_VALUE( r_emp_refcurs_id, 2, r_emp.last_name);
DBMS_SQL.COLUMN_VALUE( r_emp_refcurs_id, 3, r_emp.email);
DBMS_SQL.COLUMN_VALUE( r_emp_refcurs_id, 4, r_emp.phone_number);
DBMS_SQL.COLUMN_VALUE( r_emp_refcurs_id, 5, r_emp.hire_date);
ELSE
EXIT;
END IF;
*/
set serveroutput oFF
go top
6. Ref Cursor pointer to cursor (ee to record set object variable)
CREATE OR REPLACE PACKAGE PCK_EMPLOYEES IS
--Returned cursor variable of type SYS_REFCURSOR :
-- 1. can be opened for any query 2. it is like view with parameters id, rc...
PROCEDURE getrc_itms_by_mast_id(id IN NUMBER, rc IN OUT SYS_REFCURSOR) ;
--also works FUNCTION getrc_itms_by_mast_id (p_sifra IN NUMBER) RETURN SYS_REFCURSOR ;
PROCEDURE tbl_displ (p_rows IN SYS_REFCURSOR);
END PCK_EMPLOYEES ;
/
sho err
CREATE OR REPLACE PACKAGE BODY PCK_EMPLOYEES IS
PROCEDURE getrc_itms_by_mast_id (id IN NUMBER, rc IN OUT SYS_REFCURSOR) IS
BEGIN
OPEN rc FOR
select * from EMPLOYEES emp where emp.DEPARTMENT_ID = id
order by emp.DEPARTMENT_ID, emp.EMPLOYEE_ID
;
END getrc_itms_by_mast_id;
PROCEDURE tbl_displ(p_rows IN SYS_REFCURSOR) IS
v_row EMPLOYEES%ROWTYPE ;
BEGIN
dbms_output.put_line('-- ********************** O U T P U T :' );
-- process each row
LOOP
FETCH p_rows INTO v_row ;
EXIT WHEN p_rows%notfound;
dbms_output.put_line('id_dep=' || v_row.DEPARTMENT_ID ||', id_emp=' || v_row.EMPLOYEE_ID);
END LOOP;
CLOSE p_rows;
EXCEPTION
WHEN OTHERS THEN
IF p_rows%ISOPEN THEN CLOSE p_rows; END IF;
RAISE;
END tbl_displ;
END PCK_EMPLOYEES;
/
sho err
--CALL CLS "PCK_EMPLOYEES", METHODS getrc_itms_by_mast_id, tbl_displ
SET SERVEROUTPUT OFF
SET SERVEROUTPUT ON SIZE 1000000 FORMAT WRAPPED
DECLARE
itms_by_mast_id_rc SYS_REFCURSOR ;
BEGIN
PCK_EMPLOYEES.getrc_itms_by_mast_id(30, itms_by_mast_id_rc); --1281, 1282
--itms_by_mast_id_rc := PCK_EMPLOYEES.getrc_itms_by_mast_id (1282); --1281, 1282
PCK_EMPLOYEES.tbl_displ(itms_by_mast_id_rc);
END;
/
-- ********************** O U T P U T :
id_dep=30, id_emp=114
id_dep=30, id_emp=115
id_dep=30, id_emp=116
id_dep=30, id_emp=117
id_dep=30, id_emp=118
id_dep=30, id_emp=119
--NATIVE DYNAMIC SQL – NDS : EXECUTE IMMEDIATE statement example:
CREATE FUNCTION F_row_cnt(tablica_p VARCHAR2) RETURN NUMBER IS
naredba VARCHAR2 (1000);
broj_redaka NUMBER;
BEGIN
naredba := 'SELECT COUNT (*)' || ' FROM ' || tablica_p;
EXECUTE IMMEDIATE naredba INTO broj_redaka;
RETURN broj_redaka;
END;
/
sho err
SET SERVEROUTPUT OFF
SET SERVEROUTPUT ON SIZE 1000000 FORMAT WRAPPED
exec dbms_output.put_line('-- ********************** O U T P U T :' );
exec dbms_output.put_line('row_cnt emps=' || F_row_cnt('employees'));
-- ********************** O U T P U T :
row_cnt emps=107
SET SERVEROUTPUT OFF
SET SERVEROUTPUT ON SIZE 1000000 FORMAT WRAPPED
-- create dynamic cursor varijable
DECLARE
rcdyn SYS_REFCURSOR;
stmt_str VARCHAR2(200);
LAST_NAME VARCHAR2(100);
SALARY NUMBER;
BEGIN
stmt_str := 'SELECT LAST_NAME, SALARY' || ' FROM employees' || ' WHERE JOB_ID = :1' ;
-- create dynamic cursor varijable :
dbms_output.put_line('-- ********************** O U T P U T :' );
OPEN rcdyn FOR stmt_str USING 'SA_MAN';
LOOP
FETCH rcdyn INTO LAST_NAME, SALARY;
EXIT WHEN rcdyn%NOTFOUND;
--procesiranje podataka
dbms_output.put_line('LAST_NAME=' || LAST_NAME ||', SALARY=' || SALARY);
END LOOP;
CLOSE rcdyn;
END;
/
-- ********************** O U T P U T :
LAST_NAME=Russell, SALARY=14000
LAST_NAME=Partners, SALARY=13500
LAST_NAME=Errazuriz, SALARY=12000
LAST_NAME=Cambrault, SALARY=11000
LAST_NAME=Zlotkey, SALARY=10500