Top Banner
Agenda Oracle PL/SQL usage and 11g new features for Developers for DBAs Row by row processing Nested row by row processing Lookup queries Result Cache for lookup queries Excessive access to DUAL Populating Master-Detail Rows Unnecessary Function Execution Excessive Parsing Excessive Commits SIMPLE_INTEGER Datatype Continue Statement Sequences in PL/SQL expressions Dynamic SQL Enhancements Native Compilation Automatic Subprogram Inlining READ ONLY Tables INVISIBLE indexes SQL_monitoring History Tables & Enterprise Manager References Y. Anıl Akduygu October - 2012
26

Oracle 11g PL/SQL notes

Nov 07, 2014

Download

Technology

anilakduygu

Oracle 11g New features for Developers
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Oracle 11g PL/SQL notes

Agenda

Oracle PL/SQL usage and 11g new features for Developers for DBAs

• Row by row processing

• Nested row by row processing

• Lookup queries

• Result Cache for lookup queries

• Excessive access to DUAL

• Populating Master-Detail Rows

• Unnecessary Function Execution

• Excessive Parsing

• Excessive CommitsExcessive Commits

• SIMPLE_INTEGER Datatype

• Continue Statement

• Sequences in PL/SQL expressions

• Dynamic SQL Enhancements

• Native Compilation

• Automatic Subprogram Inlining

• READ ONLY Tables

• INVISIBLE indexes

• SQL_monitoring

• History Tables & Enterprise Manager

• References

Y. Anıl Akduygu October - 2012

Page 2: Oracle 11g PL/SQL notes

Row-by-Row ProcessingDO NOT USEDO NOT USE

DECLARE

CURSOR c1 IS

SELECT prod_id, cust_id, time_id, amount_sold

FROM salesWHERE amount_sold > 100;

c1_rec c1%rowtype;

l_cust_first_name customers.cust_first_name%TYPE;

l_cust_lasT_name customers.cust_last_name%TYPE;

BEGIN

FOR c1_rec IN c1

LOOP

-- Query customer details

SELECT cust_first_name, cust_last_name

INTO l_cust_first_name, l_cust_last_name

FROM customers

WHERE cust_id=c1_rec.cust_id;

--

USEUSE

--

-- Insert in to target table

--

INSERT INTO top_sales_customers (prod_id,

cust_id,

time_id,

cust_first_name,

cust_last_name,

amount_sold)

SELECT s.prod_id,

s.cust_id,

s.time_id,

c.cust_first_name,

c.cust_last_name,--

-- Insert in to target table

--

INSERT INTO top_sales_customers (

prod_id, cust_id, time_id, cust_first_name, cust_last_name,amount_sold

)

VALUES

(

c1_rec.prod_id,

c1_rec.cust_id,

c1_rec.time_id,

l_cust_first_name,

l_cust_last_name,

c1_rec.amount_sold

);

END LOOP;

COMMIT;

END;

c.cust_last_name,

s.amount_sold

FROM sales s, customers c

WHERE s.cust_id = c.cust_id AND s.amount_sold > 100;

SQL statements are called from PL/SQLin a loop, so the execution will switch back and forth between the PL/SQL engine and the SQL engine.

This switch between two environments is known as a context switch. Context switches increase elapsed time of your programs

Page 3: Oracle 11g PL/SQL notes

Nested Row-by-Row ProcessingDO NOT USEDO NOT USE

DECLARE

CURSOR c1 AS

SELECT n1 FROM t1;

CURSOR c2 (p_n1) AS

SELECT n1, n2 FROM t2 WHERE n1=p_n1;

CURSOR c3 (p_n1, p_n2) AS

SELECT text FROM t3 WHERE n1=p_n1 AND n2=p_n2;

BEGIN

FOR c1_rec IN c1

LOOP

FOR c2_rec IN c2 (c1_rec.n1)

LOOP

FOR c3_rec IN c3(c2_rec.n1, c2_rec.n2)

LOOP

USEUSE

MERGE INTO fact1 USING

(SELECT DISTINCT c3.n1,c3.n2

FROM t1, t2, t3

WHERE t1.n1 = t2.n1

AND t2.n1 = t3.n1

AND t2.n2 = t3.n2) t

ON (fact1.n1=t.n1 AND fact1.n2=t.n2)

WHEN matched THEN

UPDATE SET .. WHEN NOT matched THEN

INSERT .. ;

COMMIT;

;

LOOP

-- execute some sql here;

UPDATE … SET ..where n1=c3_rec.n1 AND n2=c3_rec.n2;

EXCEPTION

WHEN no_data_found THEN

INSERT into… END;

END LOOP;

END LOOP;

END LOOP;

COMMIT;

END;

/

Do not write code with deeply nested cursors in PL/SQL language. Review it to see if you can write such code in SQL instead.

Page 4: Oracle 11g PL/SQL notes

Lookup QueriesDO NOT USEDO NOT USE

DECLARE

CURSOR c1

IS

SELECT prod_id,

cust_id,

time_id,

amount_sold

FROM sales

WHERE amount_sold > 100;

l_cust_first_name customers.cust_first_name%TYPE;

l_cust_last_name customers.cust_last_name%TYPE;

l_Country_id countries.country_id%TYPE;

l_country_name countries.country_name%TYPE;

BEGIN

FOR c1_rec IN c1

LOOP

-- Query customer details

SELECT cust_first_name, cust_last_name, country_id

INTO l_cust_first_name, l_cust_last_name, l_country_id

FROM customers

WHERE cust_id = c1_rec.cust_id;

-- Query to get country_name

SELECT country_name

INTO l_country_name

USEUSE

DECLARE

CURSOR c1

IS

SELECT prod_id,cust_id,time_id,

amount_sold

FROM sales

WHERE amount_sold > 100;

l_country_names country_names_type;

l_Country_id countries.country_id%TYPE;

l_country_name countries.country_name%TYPE;

l_cust_first_name customers.cust_first_name%TYPE;

l_cust_lasT_name customers.cust_last_name%TYPE;

TYPE country_names_type

IS

TABLE OF VARCHAR2 (40)

INDEX BY PLS_INTEGER;

l_country_names country_names_type;

BEGIN

FOR c1_rec IN c1

LOOP

-- Query customer details

SELECT cust_first_name, cust_last_name, country_id

INTO l_cust_first_name, l_cust_last_name, l_country_id

FROM customersINTO l_country_name

FROM countries

WHERE country_id = l_country_id;

--

-- Insert in to target table

--

INSERT INTO top_sales_customers (prod_id,

cust_id,

time_id,

cust_first_name,

cust_last_name,

amount_sold,

country_name)

VALUES (c1_rec.prod_id,

c1_rec.cust_id,

c1_rec.time_id,

l_cust_first_name,

l_cust_last_name,

c1_rec.amount_sold,

l_country_name);

END LOOP;

COMMIT;

END;

• /

FROM customers

WHERE cust_id = c1_rec.cust_id;

-- Check array first before executing a SQL statement

IF (l_country_names.EXISTS (l_country_id))

THEN

l_country_name := l_country_names (l_country_id);

ELSE

SELECT country_name

INTO l_country_name

FROM countries

WHERE country_id = l_country_id;

-- Store in the array for further reuse

l_country_names (l_country_id) := l_country_name;

END IF;

-- Insert in to target table

INSERT INTO top_sales_customers

(prod_id,cust_id,time_id,cust_first_name,

cust_last_name,amount_sold,country_name)

VALUES

(c1_rec.prod_id,c1_rec.cust_id,c1_rec.time_id,l_cust_first_name,

l_cust_last_name,c1_rec.amount_sold,l_country_name);

END LOOP;

COMMIT;

END;

/

You can define an associative array to cache the results of the lookup query and reuse the array in later executions, thus effectively reducing the

executions of the lookup query.

Page 5: Oracle 11g PL/SQL notes

Result Cache for Lookup QueriesDO NOT USEDO NOT USE

DECLARE

CURSOR c1

IS

SELECT prod_id,

cust_id,

time_id,

amount_sold

FROM sales

WHERE amount_sold > 100;

l_cust_first_name customers.cust_first_name%TYPE;

l_cust_last_name customers.cust_last_name%TYPE;

l_Country_id countries.country_id%TYPE;

l_country_name countries.country_name%TYPE;

BEGIN

FOR c1_rec IN c1

LOOP

-- Query customer details

SELECT cust_first_name, cust_last_name, country_id

INTO l_cust_first_name, l_cust_last_name, l_country_id

FROM customers

WHERE cust_id = c1_rec.cust_id;

-- Query to get country_name

SELECT country_name

INTO l_country_name

USEUSE

DECLARE

CURSOR c1

IS

SELECT prod_id,

cust_id,

time_id,

amount_sold

FROM sales

WHERE amount_sold > 100;

l_cust_first_name customers.cust_first_name%TYPE;

l_cust_last_name customers.cust_last_name%TYPE;

l_Country_id countries.country_id%TYPE;

l_country_name countries.country_name%TYPE;

BEGIN

FOR c1_rec IN c1

LOOP

-- Query customer details

SELECT cust_first_name, cust_last_name, country_id

INTO l_cust_first_name, l_cust_last_name, l_country_id

FROM customers

WHERE cust_id = c1_rec.cust_id;

-- Query to get country_name

SELECT /*+ RESULT_CACHE */ country_name

INTO l_country_nameINTO l_country_name

FROM countries

WHERE country_id = l_country_id;

--

-- Insert in to target table

--

INSERT INTO top_sales_customers (prod_id,

cust_id,

time_id,

cust_first_name,

cust_last_name,

amount_sold,

country_name)

VALUES (c1_rec.prod_id,

c1_rec.cust_id,

c1_rec.time_id,

l_cust_first_name,

l_cust_last_name,

c1_rec.amount_sold,

l_country_name);

END LOOP;

COMMIT;

END;

/

INTO l_country_name

FROM countries

WHERE country_id = l_country_id;

--

-- Insert in to target table

--

INSERT INTO top_sales_customers (prod_id,

cust_id,

time_id,

cust_first_name,

cust_last_name,

amount_sold,

country_name)

VALUES (c1_rec.prod_id,

c1_rec.cust_id,

c1_rec.time_id,

l_cust_first_name,

l_cust_last_name,

c1_rec.amount_sold,

l_country_name);

END LOOP;

COMMIT;

END;

/

The result cache is new to Oracle 11g and provides enhanced query performance for SQL and PL/SQL applications by caching the results of SQL queries

into memory . A result cache shareable and is stored in SGA memory.

Page 6: Oracle 11g PL/SQL notes

Excessive Access to DUALDO NOT USEDO NOT USE

DECLARE

l_epoch INTEGER;

BEGIN

SELECT ( (SYSDATE

- TO_DATE ('01-JAN-1970 00:00:00', 'DD-MON-YYYY HH24:MI:SS'))

* 24

* 60

* 60)

INTO l_epoch

FROM DUAL;

DBMS_OUTPUT.put_line (l_epoch);

END;

/

USEUSE

DECLARE

l_epoch INTEGER;

BEGIN

l_epoch :=

(SYSDATE - TO_DATE ('01-JAN-1970 00:00:00', 'DD-MON-YYYY HH24:MI:SS'))

* 24

* 60

* 60;

DBMS_OUTPUT.put_line (l_epoch);

END;

/

/

You should avoid overusing DUAL table access. Accessing DUAL from PL/SQL causes context switching, which hurts performance.

Page 7: Oracle 11g PL/SQL notes

Excessive Access to DUALDO NOT USEDO NOT USE

DECLARE

l_cust_id NUMBER;

BEGIN

FOR c1 IN (SELECT cust_first_name, cust_last_name

FROM customers

WHERE cust_marital_status != 'married')

LOOP

SELECT cust_hist_id_seq.NEXTVAL INTO l_cust_id FROM DUAL;

INSERT INTO customers_hist (cust_hist_id, first_name, last_name)

VALUES (l_cust_id, c1.cust_first_name, c1.cust_last_name);

END LOOP;

END;

/

USEUSE

INSERT INTO customers_hist

SELECT cust_hist_id_seq.NEXTVAL, cust_first_name, cust_last_name

FROM customers

WHERE cust_marital_status != 'married';

/

You should avoid overusing DUAL table access. Accessing DUAL from PL/SQL causes context switching, which hurts performance.

Page 8: Oracle 11g PL/SQL notes

Populating Master-Detail RowsDO NOT USEDO NOT USE

DECLARE

l_cust_id NUMBER;

BEGIN

FOR c1 IN (SELECT cust_first_name, cust_last_name

FROM customers

WHERE cust_marital_status != 'married')

LOOP

SELECT cust_hist_id_seq.NEXTVAL INTO l_cust_id FROM DUAL;

INSERT INTO customers_hist (cust_hist_id, first_name, last_name)

VALUES (l_cust_id, c1.cust_first_name, c1.cust_last_name);

END LOOP;

END;

/

USEUSE

INSERT INTO customers (cust_id, ...)

VALUES (cust_id_seq.nextval,...)

RETURNING cust_id into l_cust_id;

INSERT INTO customer_transactions (cust_id, ...)

VALUES (l_cust_id,...)

...

/

You can retrieve the key value from a newly-inserted master row by using the DML RETURNING clause. Then you can use that key value while inserting in

to the detail table.

Page 9: Oracle 11g PL/SQL notes

Unnecessary Function ExecutionDO NOT USEDO NOT USE

CREATE TABLE log_table (message_seq NUMBER, MESSAGE VARCHAR2 (512));

CREATE SEQUENCE message_id_seq;

DECLARE

l_debug BOOLEAN := FALSE;

r1 INTEGER;

FUNCTION log_entry (v_message IN VARCHAR2, v_debug IN BOOLEAN)

RETURN NUMBER

IS

BEGIN

IF (v_debug)

THEN

INSERT INTO log_table (message_seq, MESSAGE)

VALUES (message_id_seq.NEXTVAL, v_message);

END IF;

RETURN 0;

END;

BEGIN

FOR c1 IN (SELECT s.prod_id,

s.cust_id,

USEUSE

DECLARE

l_debug BOOLEAN := FALSE;

r1 INTEGER;

FUNCTION log_entry (v_message IN VARCHAR2, v_debug IN BOOLEAN)

RETURN NUMBER

IS

BEGIN

IF (v_debug)

THEN

INSERT INTO log_table (message_seq, MESSAGE)

VALUES (message_id_seq.NEXTVAL, v_message);

END IF;

RETURN 0;

END;

BEGIN

FOR c1 IN (SELECT s.prod_id,

s.cust_id,

s.time_id,

c.cust_first_name,

c.cust_last_name,s.cust_id,

s.time_id,

c.cust_first_name,

c.cust_last_name,

s.amount_sold

FROM sales s, customers c

WHERE s.cust_id = c.cust_id AND s.amount_sold > 100)

LOOP

IF c1.cust_first_name IS NOT NULL

THEN

r1 := log_entry ('first_name is not null ', l_debug);

END IF;

IF c1.cust_last_name IS NOT NULL

THEN

r1 := log_entry ('Last_name is not null ', l_debug);

END IF;

END LOOP;

END;

/

c.cust_last_name,

s.amount_sold

FROM sales s, customers c

WHERE s.cust_id = c.cust_id AND s.amount_sold > 100)

LOOP

$IF $$debug_on

$THEN

IF c1.cust_first_name IS NOT NULL

THEN

r1 := log_entry ('first_name is not null ', l_debug);

END IF;

IF c1.cust_last_name IS NOT NULL

THEN

r1 := log_entry ('Last_name is not null ', l_debug);

END IF;

$END

NULL;

END LOOP;

END;

/

Executing a function call usually means that a different part of the instruction set must be loaded into the CPU. By avoiding unnecessary function

execution, you avoid unneeded flushing and refilling of the instruction pipeline, thus minimizing demands upon your CPU.

Page 10: Oracle 11g PL/SQL notes

Unnecessary Function ExecutionDO NOT USEDO NOT USE

CREATE OR REPLACE FUNCTION calculate_epoch (d IN DATE)

RETURN NUMBER

DETERMINISTIC

IS

l_epoch NUMBER;

BEGIN

l_epoch :=

(d - TO_DATE ('01-JAN-1970 00:00:00', 'DD-MON-YYYY HH24:MI:SS'))

* 24

* 60

* 60;

RETURN l_epoch;

END calculate_epoch;

/

SELECT

MAX (calculate_epoch (s.time_id)) epoch

USEUSE

CREATE INDEX compute_epoch_fbi ON sales

(calculate_epoch(time_id))

PARALLEL (DEGREE 4);

SELECT

MAX (calculate_epoch (s.time_id)) epoch

FROM sales s

WHERE s.amount_sold > 100

AND calculate_epoch (s.time_id) BETWEEN 1000000000 AND 1100000000;

MAX (calculate_epoch (s.time_id)) epoch

FROM sales s

WHERE s.amount_sold > 100

AND calculate_epoch (s.time_id) BETWEEN 1000000000 AND 1100000000;

Executing a function call usually means that a different part of the instruction set must be loaded into the CPU. By avoiding unnecessary function

execution, you avoid unneeded flushing and refilling of the instruction pipeline, thus minimizing demands upon your CPU.

Page 11: Oracle 11g PL/SQL notes

Unnecessary Function ExecutionDO NOT USEDO NOT USE

CREATE OR REPLACE FUNCTION calculate_epoch (d IN DATE)

RETURN NUMBER

DETERMINISTIC

IS

l_epoch NUMBER;

BEGIN

l_epoch :=

(d - TO_DATE ('01-JAN-1970 00:00:00', 'DD-MON-YYYY HH24:MI:SS'))

* 24

* 60

* 60;

RETURN l_epoch;

END calculate_epoch;

/

SELECT

MAX (calculate_epoch (s.time_id)) epoch

USEUSE

• CREATE OR REPLACE FUNCTION calculate_epoch (d IN date)

• RETURN NUMBER DETERMINISTIC RESULT_CACHE IS

• l_epoch number;

• BEGIN

• l_epoch := (d - TO_DATE('01-JAN-1970 00:00:00', 'DD-MON-YYYY HH24:MI:SS'))

• * 24 *60 *60 ;

• RETURN l_epoch;

• END calculate_epoch;

• /

• SELECT

• MAX (calculate_epoch (s.time_id)) epoch

• FROM sales s

• WHERE s.amount_sold > 100

• AND calculate_epoch (s.time_id) BETWEEN 1000000000 AND 1100000000;

MAX (calculate_epoch (s.time_id)) epoch

FROM sales s

WHERE s.amount_sold > 100

AND calculate_epoch (s.time_id) BETWEEN 1000000000 AND 1100000000;

The result cache is new to Oracle 11g and provides enhanced query performance for SQL and PL/SQL applications by caching the results of SQL queries

into memory . A result cache shareable and is stored in SGA memory

Page 12: Oracle 11g PL/SQL notes

Database Link CallsDO NOT USEDO NOT USE

DECLARE

V_customer_name VARCHAR2(32);

BEGIN

...

FOR c1 IN (SELECT …)

LOOP

...

SELECT customer_name

INTO v_customer_name

FROM customers@remotedb

WHERE account_id = c1.account_id;

...

END

/

USEUSE

CREATE MATERIALIZED VIEW customers_snapshot

...

AS

SELECT customer_name .. FROM customers@remotedb;

DECLARE

V_customer_name VARCHAR2(32);

BEGIN

...

FOR c1 IN (SELECT …)

LOOP

.../ ...

SELECT customer_name

INTO v_customer_name

FROM customers_snapshot

WHERE account_id = c1.account_id;

...

END

/

Excessive database link-based calls can affect application performance. Accessing a remote table or modifying a remote table over a database link within a

database link.

Excessive database link-based calls can affect application performance. Accessing a remote table or modifying a remote table over a database link within a

loop is not a scalable approach. For each access to a remote table, several SQL*Net packets are exchanged between the databases involved in the

database link.

Page 13: Oracle 11g PL/SQL notes

Excessive ParsingDO NOT USEDO NOT USE

DECLARE

...

BEGIN

FOR c1_rec IN c1

LOOP

-- Query customer details

EXECUTE IMMEDIATE

'SELECT cust_first_name, cust_last_name, country_id

FROM customers

WHERE cust_id= '|| c1_rec.cust_id INTO l_cust_first_name,

l_cust_last_name,

l_country_id;

...

USEUSE

DECLARE

...

BEGIN

FOR c1_rec IN c1

LOOP

-- Query customer details

SELECT cust_first_name, cust_last_name, country_id into

l_cust_first_name,

l_cust_last_name,

l_country_id

WHERE cust_id=c1_rec.cust_id;...

END LOOP;

COMMIT;

END;

/

WHERE cust_id=c1_rec.cust_id;

...

END LOOP;

COMMIT;

END;

/

Do not use literals , Use Bind variables in SQL statements. Using literals causes excessive hard parsing stresses the library cache, thereby reducing the

application’s scalability and concurrency.

Page 14: Oracle 11g PL/SQL notes

Excessive CommitsDO NOT USEDO NOT USE

DECLARE

BEGIN

FOR c1_rec IN c1

LOOP

-- Query customer details

SELECT cust_first_name, cust_last_name, country_id into

l_cust_first_name,

l_cust_last_name,

l_country_id

WHERE cust_id=c1_rec.cust_id;

USEUSE

DECLARE

commit_number pls_integer;

BEGIN

commit_number := 0;

FOR c1_rec IN c1

LOOP

-- Query customer details

SELECT cust_first_name, cust_last_name, country_id into

l_cust_first_name,

l_cust_last_name,

l_country_id

WHERE cust_id=c1_rec.cust_id;WHERE cust_id=c1_rec.cust_id;

INSERT ...

UPDATE ...

Commit;

END LOOP;

END;

/

WHERE cust_id=c1_rec.cust_id;

INSERT ...

UPDATE ...

if commit_number > 10000

Then

commit;

commit_number := 0;

end if;

commit_number := commit_number + 1;

END LOOP;

Commit;

END;

/

Frequent commits generate more redo, require Log Writer to flush the contents of log buffer to log file frequently, can lead to data integrity issues, and

consume more resources.

Page 15: Oracle 11g PL/SQL notes

Type Usage in Spec DefinetionsDO NOT USEDO NOT USE

CREATE OR REPLACE PACKAGE check_bilgi

IS

...

FUNCTION checkirtibatbilgi (

pcif IN t_mus_irtibat.client_no%TYPE,

pirtbilgi IN t_mus_irtibat.irtbilgi%TYPE,

pbilgitip IN t_mus_irtibat.bilgitip%TYPE,

pserino IN t_mus_irtibat.musserino%TYPE DEFAULT NULL

)

RETURN BOOLEAN;

....

USEUSE

CREATE OR REPLACE PACKAGE check_bilgi

IS

...

FUNCTION checkirtibatbilgi (

pcif IN NUMBER,

pirtbilgi IN VARCHAR2,

pbilgitip IN VARCHAR2,

pserino IN NUMBER DEFAULT NULL

)

RETURN BOOLEAN;

....

END;

/

END;

/

If you are using packages in distributed environment ; do not use Type in Packege Spec definition. By this way, you can reduce database dependencies .

Page 16: Oracle 11g PL/SQL notes

PL/SQL New Features

SIMPLE_INTEGER Datatype

The SIMPLE_INTEGER datatype is a subtype of the PLS_INTEGER datatype and can dramatical increase the speed of integer arithmetic

CREATE OR REPLACE PROCEDURE simple_integer_test_proc AS

l_start NUMBER;

l_loops NUMBER := 10000000;

l_pls_integer PLS_INTEGER := 0;

l_pls_integer_incr PLS_INTEGER := 1;

l_simple_integer SIMPLE_INTEGER := 0;

l_simple_integer_incr SIMPLE_INTEGER := 1;

BEGINBEGIN

l_start := DBMS_UTILITY.get_time;

FOR i IN 1 .. l_loops LOOP

l_pls_integer := l_pls_integer + l_pls_integer_incr;

END LOOP;

DBMS_OUTPUT.put_line('PLS_INTEGER: ' || (DBMS_UTILITY.get_time - l_start) || ' hsecs');

l_start := DBMS_UTILITY.get_time;

FOR i IN 1 .. l_loops LOOP

l_simple_integer := l_simple_integer + l_simple_integer_incr;

END LOOP;

DBMS_OUTPUT.put_line('SIMPLE_INTEGER: ' || (DBMS_UTILITY.get_time - l_start) || ' hsecs');

END simple_integer_test_proc;

/

Page 17: Oracle 11g PL/SQL notes

The CONTINUE statement jumps out of the current loop interation and starts the next one. It can be used on its own, or as part of CONTINUE WHEN statement

DECLARE

l_number NUMBER := 0;

BEGIN

FOR i IN 1 .. 100 LOOP

CONTINUE WHEN MOD(i,2) = 0;

-- Do something here!

l_number := l_number + 1;

END LOOP;

PL/SQL New Features

Continue Statement

DBMS_OUTPUT.put_line('CONTINUE WHEN : ' || l_number);

l_number := 0;

FOR i IN 1 .. 100 LOOP

IF MOD(i,2) = 0 THEN

CONTINUE;

END IF;

-- Do something here!

l_number := l_number + 1;

END LOOP;

DBMS_OUTPUT.put_line('IF .. CONTINUE: ' || l_number);

END;

/

Page 18: Oracle 11g PL/SQL notes

The NEXTVAL and CURRVAL sequence pseudocolumns can now be accessed in PL/SQL expressions as well as queries.

CREATE SEQUENCE test1_seq START WITH 1000000;

DECLARE

l_start NUMBER;

l_loops NUMBER := 100000;

l_value NUMBER;

BEGIN

FOR i IN 1 .. l_loops LOOP

l_value := test1_seq.NEXTVAL;

END LOOP;

FOR i IN 1 .. l_loops LOOP

PL/SQL New FeaturesSequences in PL/SQL expressions

FOR i IN 1 .. l_loops LOOP

SELECT test1_seq.NEXTVAL

INTO l_value

FROM dual;

END LOOP;

FOR i IN 1 .. l_loops LOOP

l_value := test1_seq.NEXTVAL;

END LOOP;

FOR i IN 1 .. l_loops LOOP

SELECT test1_seq.CURRVAL

INTO l_value

FROM dual;

END LOOP;

l_start := DBMS_UTILITY.get_time;

FOR i IN 1 .. l_loops LOOP

l_value := test1_seq.CURRVAL;

END LOOP;

END;

Page 19: Oracle 11g PL/SQL notes

� Native dynamic SQL and the DBMS_SQL package now support dynamic SQL statements larger than 32 KB.

� The EXECUTE IMMEDIATE statement, OPEN-FOR statement and DBMS_SQL.PARSE procedure all accept SQL statements in the form of

CLOBs.

� The DBMS_SQL.TO_REFCURSOR function converts a DBMS_SQL cursor ID into a REF CURSOR.

DECLARE

l_ref_cursor SYS_REFCURSOR;

l_cursor NUMBER;

l_count NUMBER := 0;

BEGIN

PL/SQL New FeaturesDynamic SQL Enhancements

BEGIN

OPEN l_ref_cursor FOR 'SELECT * FROM emp';

l_cursor := DBMS_SQL.to_cursor_number(l_ref_cursor);

WHILE DBMS_SQL.fetch_rows(l_cursor) > 0 LOOP

l_count := l_count + 1;

END LOOP;

DBMS_OUTPUT.put_line('Employee Count: ' || l_count);

DBMS_SQL.close_cursor(l_cursor);

END;

/

Page 20: Oracle 11g PL/SQL notes

Automatic subprogram inlining replace the subprogram calls with a copy of the code in the subprogram at compile time.

Reduce the overheads associated with calling subprograms

ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL=2;

DECLARE

l_loops NUMBER := 10000000;

l_start NUMBER;

l_return NUMBER;

FUNCTION add_numbers (p_1 IN NUMBER,

PL/SQL New FeaturesAutomatic Subprogram Inlining

FUNCTION add_numbers (p_1 IN NUMBER,

p_2 IN NUMBER)

RETURN NUMBER AS

BEGIN

RETURN p_1 + p_2;

END add_numbers;

BEGIN

FOR i IN 1 .. l_loops LOOP

PRAGMA INLINE (add_numbers, 'YES');

l_return := add_numbers(1, i);

END LOOP;

END;

/

Page 21: Oracle 11g PL/SQL notes

In Oracle 11g, PL/SQL native compilation requires no C compiler, no DBA intervention and is fully supported in a RAC environment.

By setting the PLSQL_CODE_TYPE to a value of NATIVE, rather than the default value of INTERPRETED, code is compiled directly to machine code

and stored in the SYSTEM tablespace.

When the code is called, it is loaded into shared memory, making it accessible for all sessions in that instance. The %_PLSQL_OBJECT_SETTINGS

views include the current PLSQL_CODE_TYPE setting for each PL/SQL object.

PL/SQL New FeaturesNative Compilation

Determining Whether to Use PL/SQL Native Compilation ( from Oracle® Database PL/SQL Language Reference)

While you are debugging program units and recompiling them frequently, interpreted mode has these advantages:

• You can use PL/SQL debugging tools on program units compiled for interpreted mode (but not for those compiled for native mode).

• Compiling for interpreted mode is faster than compiling for native mode.

After the debugging phase of development, in determining whether to compile a PL/SQL unit for native mode, consider:

• PL/SQL native compilation provides the greatest performance gains for computation-intensive procedural operations. Examples are data

warehouse applications and applications with extensive server-side transformations of data for display.

• PL/SQL native compilation provides the least performance gains for PL/SQL subprograms that spend most of their time running SQL.

• When many program units (typically over 15,000) are compiled for native execution, and are simultaneously active, the large amount of

shared memory required might affect system performance.

Page 22: Oracle 11g PL/SQL notes

• In previous Oracle releases, tables could be made to appear read-only to other users by only granting the SELECT object privilege to them,

but the tables remained read-write for the owner.

• Oracle 11g allows tables to be marked as read-only using the ALTER TABLE command.

ALTER TABLE table_name READ ONLY;

New Featues for Developers & DBAsREAD ONLY Tables

ALTER TABLE table_name READ ONLY;

ALTER TABLE table_name READ WRITE;

Page 23: Oracle 11g PL/SQL notes

• An invisible index is invisible to the optimizer as default.

• Using this feature we can test a new index without effecting the execution plans of the existing sql statements or we can test the effect of

dropping an index without dropping it.

• They are ignored by the optimizer unless the OPTIMIZER_USE_INVISIBLE_INDEXES parameter is set to TRUE at the instance or session level.

CREATE INDEX index_name ON table_name(column_name) INVISIBLE;

New Featues for Developers & DBAsINVISIBLE indexes

ALTER INDEX index_name INVISIBLE;

ALTER INDEX index_name VISIBLE;

Page 24: Oracle 11g PL/SQL notes

Oracle 11g automatically monitors SQL statements if they are run in parallel, or consume 5 or more seconds of CPU or I/O in a single execution.

This allows resource intensive SQL to be monitored as it is executing, as well as giving access to detailed information about queries once they are

complete.

New Featues for Developers & DBAsSQL_monitoring

Page 25: Oracle 11g PL/SQL notes

With history tables DBA can query historical performance data to solve problems . Enterprise Manager can help DBAs to probe problems.

Some important history tables

• DBA_HIST_ACTIVE_SESS_HISTORY

• DBA_HIST_SQLSTAT

• DBA_HIST_SQLTEXT

• DBA_HIST_SQL_PLAN

• DBA_HIST_SYSSTAT

• DBA_HIST_WAITSTAT

New Features for Developers & DBAsHistory Tables & Enterprise Manager

Page 26: Oracle 11g PL/SQL notes

http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/toc.htm

http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/tuning.htm#CHDJJAGH

PL/SQL New FeaturesReferences