SQLPLUS> select * from emp; no rows selected SQLPLUS> exec INS_EMP; PL/SQL procedure successfully completed.. SQLPLUS> select * from emp; ## Add commit to stored procedure SQLPLUS> cre
Trang 1EMP_NAME EMP_DEPT
SQLPLUS> rollback;
Rollback complete.
SQLPLUS> select * from emp;
no rows selected
This example uses an anonymous block of code, rather than a stored procedure If you were to put this statement in a stored procedure, after executing the stored procedure, if you did not have the commits in the stored procedure, you could still roll back after the execution of the procedure
SQLPLUS> create procedure INS_EMP
as
begin
insert into emp values('Mandy',10);
insert into emp values('Emily',20);
savepoint before_delete;
delete from emp where emp_dept=20;
end;
/
Procedure created.
SQLPLUS> select * from emp;
no rows selected
SQLPLUS> exec INS_EMP;
PL/SQL procedure successfully completed.
SQLPLUS> select * from emp;
SQLPLUS> rollback to before_delete;
Rollback complete.
SQLPLUS> select * from emp;
SQLPLUS> commit;
Commit complete.
SQLPLUS> rollback;
Trang 2Rollback complete.
## Rollback ineffective because commit already done.
SQLPLUS> select * from emp;
## Add commit to stored procedure
SQLPLUS> create or replace procedure INS_EMP
as
begin
insert into emp values('Mandy',10);
insert into emp values('Emily',20);
savepoint before_delete;
delete from emp where emp_dept=20;
commit;
end;
/
Procedure created
SQLPLUS> exec ins_emp;
PL/SQL procedure successfully completed.
SQLPLUS> select * from emp;
## commit part of the stored procedure so rollback
## to a savepoint will error out
SQLPLUS> rollback to before_delete;
rollback to before_delete
*
ERROR at line 1:
ORA-01086: savepoint 'BEFORE_DELETE' never established
As you can see from the examples, in the same session without a
commit, rollbacks are possible to the beginning of the statement or to the
savepoints
Defining Commits
With the transaction savepoints in place, you now need to confirm the
changes and commit them The transaction size is important, as noted
earlier You do not want commits every record; even every 500 can be too
small Locking is less of a concern with commit points in Oracle
Trang 3If looping through the data that is being processed can be validated, then
a bulk of the updates can be committed or rolled back as a group in the transaction The raising of errors in the procedure will also allow for rollbacks
in the error handling, as discussed later in this chapter
Commits should be put into the code as needed It should not be
expected that executing another procedure will automatically commit, or that a child procedure will commit automatically when completed When changing tables and performing DDL statements with transactions in the same session, a commit does happen before and after the structure change
So, if you did some transactions, and then did an ALTER TABLE or
CREATE INDEX, the changes would be committed
## Example loop to commit every 10000
declare
loop_num number :=0;
cursor c_products is
select item_id from products;
begin
for i in c_products
loop
update products set prod_num = prod_num + 2000 where item_id = i.item_id;
loop_num := loop_num + 1;
if mod(loop_num, 10000) = 0 THEN COMMIT;
end if;
end loop;
commit;
end;
This example can be modified to have a parameter passed in to adjust the commit value, or if it’s part of a package, it can have a global variable defined for the number of rows to commit at a time
Notice that the example loop first gathers the IDs to be updated in a cursor Next, let’s look at cursor processing in Oracle
Cursor Processing
In SQL Server, because of the locking and processing of transactions, bulk transactions are normally the way to go Looping through cursors is not normally the most efficient way to process transactions However, in
Oracle, implicit and explicit cursors are used to process transactions
Trang 4Implicit cursors are used automatically to process SELECT, UPDATE,
INSERT, and DELETE statements If you want to perform some other action with each row that is being processed, you will need to define an explicit
cursor Explicit cursors can be very useful in handling transactions that
require additional work for the data or for handling the commit point size
NOTE
SELECT INTO, which retrieves one row of
data, also uses an implicit cursor If there is
more than one record returned with the
SELECT INTO, an error is raised for handling
of TOO_MANY_ROWS or NO_DATA_FOUND, as
discussed in the “Error Handling” section later
in this chapter
The Oracle cursor works in a similar manner to a temporary table in SQL Server The cursor pulls out the data set that is to be worked with and uses
that in the rest of the code It’s true that SQL Server also has cursors, which
can be declared and opened, and the next record can be fetched and then
closed But behind the scenes, SQL Server is handling this in a temporary
table With Oracle’s version, we skip to the temporary table And keep in
mind that Oracle may already be using implicit cursors
With a cursor, several attributes are useful in processing the rows:
%NOTFOUND, %FOUND, %ROWCOUNT, and %ISOPEN The %NOTFOUND
attribute is good for error handling of the cursor to check if data is even
returned in the SELECT operation The cursor could be open for processing
as long as new values are found or while the cursor stays open and hasn’t
been explicitly closed
DECLARE
CURSOR c_emp_rec IS
select emp_id, emp_name from emp
where emp_dept = var_in_dept_id;
BEGIN
IF NOT c_emp_rec%ISOPEN
THEN
OPEN c_emp_rec;
END IF;
- Do stuff
.
END;
Trang 5BULK COLLECTor FOR loops can be used for cursor processing when you have an expected value of the set of results for the cursor or a manageable set of data
DECLARE
TYPE dept_list IS VARRAY of varchar2(50);
v_dept_list dept_list;
BEGIN
select dept_name
BULK COLLECT INTO v_dept_list
from departments;
FOR i IN 1 v_dept_list.COUNT
LOOP
Do stuff with department names END LOOP;
END;
Another cursor type is a REF CURSOR This is a cursor variable that can
be defined with different queries at runtime Instead of just declaring a cursor
as a SELECT statement, a datatype is defined as a REF CURSOR, and then can be associated with a variable The SELECT statement can even be put together in a variable and then be used with the cursor
SQLPLUS> create or replace procedure products_query (
var_prod_id product.prod_id%TYPE,
var_prod_name product.name%TYPE)
IS
prod_refcur SYS_REFCURSOR;
v_prod_id product.prod_id%TYPE;
v_prod_name product.name%TYPE;
BEGIN
v_stmt_sql := 'SELECT prod_id, name from product where ' ||
'prod_id = :productid and prod = :prodname';
OPEN prod_refcur FOR v_stmt_sql USING var_prod_id, var_prod_name; LOOP
FETCH prod_refcur INTO v_prod_id, v_prod_name;
EXIT WHEN prod_refcur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_prod_id || ' ' || v_prod_name); END LOOP;
CLOSE prod_refcur;
END;
/
Trang 6Procedure created.
## To see the output from DBMS_OUTPUT for example purposes
SQLPLUS> set serveroutput on
SQLPLUS> exec product_query(4,'Product 2');
4 Product 2
PL/SQL procedure successfully completed.
The cursor in the example can be set to another SELECT statement as
long as the cursor was closed first Using a variable to build the SELECT
statement gives you a lot of flexibility in the queries that are in the cursor
For example, in application packages, instead of just outputting the
information, values can be updated or used for comparison
Processing with FORALL
With a PL/SQL FORALL loop, you can collect data and perform insert,
update, or delete operations
## Create sample table of months
SQLPLUS> create table forall_months (
description VARCHAR2(50));
Table created.
## Insert some data for example
SQLPLUS> INSERT INTO forall_months VALUES (1, 'JAN');
1 row created.
SQLPLUS> INSERT INTO forall_months VALUES (2, 'FEB');
1 row created.
SQLPLUS> INSERT INTO forall_months VALUES (3, 'MAR');
1 row created.
.
SQLPLUS> COMMIT;
Commit complete.
## Create procedure that uses FORALL loop to collect
## the data and update it.
SQLPLUS> create or replace procedure update_with_year
AS
TYPE t_forall_months_tab IS TABLE OF forall_months%ROWTYPE;
l_tab t_forall_months_tab;
BEGIN
SELECT *
BULK COLLECT INTO l_tab
FROM forall_months;
Trang 7FOR indx IN l_tab.first l_tab.last LOOP
l_tab(indx).description := l_tab(indx).description||
' 2010 Information';
END LOOP;
FORALL indx IN l_tab.first l_tab.last
UPDATE forall_months
SET description = l_tab(indx).description
WHERE id = l_tab(indx).id;
COMMIT;
END;
/
Procedure created.
## Execute procedure and look at the data.
SQLPLUS> exec update_with_year;
PL/SQL procedure successfully completed.
SQLPLUS> SELECT * FROM forall_months;
ID DESCRIPTION -
-1 JAN 20 -10 Information
2 FEB 2010 Information
3 MAR 2010 Information
4 APR 2010 Information
5 MAY 2010 Information
6 JUN 2010 Information
7 JUL 2010 Information
8 AUG 2010 Information
9 SEP 2010 Information
10 OCT 2010 Information
11 NOV 2010 Information
12 DEC 2010 Information
12 rows selected.
Functions
A function in Oracle is the same thing as it is in SQL Server: a program to return some value In general, the functions in Oracle are scalar-valued functions They return a value to what called the function In contrast, stored procedures do not return anything Table 9-4 summarizes function types in SQL Server and Oracle
Coding functions is similar to creating procedures Functions can take input parameters and handle errors, but they always return a value Here is
Trang 8an example of a simple function that takes in some parameters and returns
a value:
SQLPLUS> create or replace function
get_customer_name(var_cust_id in number)
return varchar2
v_cust_name varchar2(40);
as
BEGIN
SELECT cust_name into v_cust_name
from customers where cust_id = var_cust_id;
return v_cust_name;
END;
/
You can write code to modify and manipulate the values as needed or
pull information from other tables
Oracle provides multiple system-defined functions for working with
values, dates, and characters Instead of the SQL Server functions of CAST
and CONVERT, Oracle has TO_ functions: TO_DATE, TO_CHAR, and TO_
NUMBER These allow for formatting and converting a datatype to another
type The following demonstrates some of the system-defined functions
SQLPLUS> select amount from sales
where sales_date >= TO_DATE('05/01/2010','MM/DD/YYYY');
SQLPLUS> select TO_CHAR(sysdate,'YYYYMMDD:HH24:MI') from dual;
20100501:21:23 ## Now a character string
## Add 2 months to a date
SQLPLUS> SQL> select add_months(sysdate,2) from dual;
ADD_MONTH
-14-JUL-10
SQL Server Functions Oracle Functions
System- and user-defined functions System- and user-defined functions
Table-valued functions Pipelined table functions
Scalar-valued functions Functions
TABLE 9-4. Function Types in SQL Server and Oracle
Trang 9## Find the first occurrence of some characters
SQLPLUS> select INSTR('Michelle','ich') from dual;
INSTR('MICHELLE','ICH')
-2
## Replace character with another
SQLPLUS> select replace('Gig Grown Gear','G','B') from dual;
REPLACE('GIGGR
-Big Brown Bear
## Replace characters and remove spaces
SQLPLUS> select replace(replace('Gig Grown Gear','G','B'),' ','') from dual;
REPLACE(REPL
-BigBrownBear
## Substitute a value for NULLs
SQLPLUS> select sales_state,NLV(amount,0) from sales;
STATE_ID AMOUNT
.
## Handling CASE
SQLPLUS> select UPPER('Michelle'), LOWER('MicHelle') from dual;
Functions can be used to do comparisons or change data As discussed
in Chapter 8, function-based indexes improve performance when using these types of functions to access tables Even user-defined functions can be used in indexes
The pipelined table functions are used to return a collection that can be queried in the same way as a table
## first create the needed types
SQLPLUS> create type emp_row_type as object (
empname varchar2(20),
empid number,
deptid number,
status varchar2(10));
/
Trang 10Type created.
create type emp_table_type as table of emp;
/
Type created.
SQLPLUS> create or replace function get_all_names (
p_empname in varchar2,
p_empid in number,
p_deptid in number,
p_status varchar2)
RETURN emp_table_type as
v_tab emp_table_type := emp_table_type();
BEGIN
for cur in (select ename,empno,deptno,job from emp2
where hiredate < sysdate - 1)
LOOP
v_tab.extend;
v_tab(v_tab.last) := emp_row_type
(cur.ename,cur.empno,cur.deptno,cur.job);
END LOOP;
return v_tab;
end;
/
Function created.
Oracle Database 11g has a result cache for functions Return values can
be cached to reduce the time needed to get the data out of the function The following shows the basic structure for defining a function to use the result
cache
create or replace function get_product (p_in in NUMBER)
return varchar2
result_cache
as
.
BEGIN
function code
END;
/
The optional clause RELIES_ON will invalidate the cache if the
dependent objects are modified Oracle Database 11g Release 2 uses
RELIES_ONby default, so it will automatically track dependencies and
invalidate the cached results when necessary This way, the cache will
continue to return the correct result set