Beruflich Dokumente
Kultur Dokumente
PLSQL_CCFLAGS
PLSQL_CODE_TYPE
PLSQL_DEBUG Stored with the units metadata, PLSQL compiler settings and optimization
PLSQL_OPTIMIZE_LEVEL level and can viewed in ALL_PLSQL_OBJECTS_SETTINGS view. Cal
PLSQL_WARNINGS also be altered using ALTER cmd
NLS_LENGTH_SEMANTICS
Use:
Avoid:
2. Takes lot of time issuing DDL in PLSQL like Create table try avoiding it.
3. Minimize data type conversion.
At runtime, PLSQL converts b/w different data types automatically.
Example:- PL_INTEGER variable to a NUMBER variable conversion. Because their
internal representation are different. So, whenever it is possible choose data types
carefully to minimize implicit conversions.
Note: In table if data type NUMBER and convert to PLS_INTERGER in the code and maintain same till
the end. This actually improves performance. Because it used more efficient hardware arithmetic and it
requires less space than INTEGER and NUMBER. PLS_INTERGER and BINARY_INTEGER are more
identical.
4. Avoid subtypes like: INTERGER, NATURAL, NATURALn, POSITIVE, POSITIVEn, AND SIGN
type in performance critical code.
Explain Plan
EXPLAIN PLAN parses a query and records the "plan" that Oracle devises to execute it. By examining
this plan, you can find out if Oracle is picking the right indexes and joining your tables in the most efficient
manner. There are a few different ways to utilize Explain Plan. We will focus on using it
through SQL*Plus since most Oracle programmers have access to SQL*Plus.
The first thing you will need to do is make sure you have a table called PLAN_TABLE available in your
schema. The following script will create it for you if you don't have it already:
@?/rdbms/admin/utlxplan.sql
or
EXPLAIN PLAN SET STATEMENT_ID = statement_id FOR your-sql-statement;
After running EXPLAIN PLAN, Oracle populates the PLAN_TABLE table with data that needs to be formatted
to presented to the user in a more readable format. Several scripts exist for this, however, one of the easiest
methods available is to cast dbms_xplan.display to a table and select from it (see examples below).
Some Examples
SQL> EXPLAIN PLAN FOR select * from dept where deptno = 40;
Explained.
SQL> set linesize 132
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------
-
Plan hash value: 2852011669
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 20 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 20 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
2 - access("DEPTNO"=40)
14 rows selected.
The DBMS_XPLAN.DISPLAY function can accept 3 optional parameters:
------------------------------------------------
| Id | Operation | Name |
------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | NESTED LOOPS | |
| 2 | NESTED LOOPS | |
12 rows selected.
Using SQL*Plus Autotrace
SQL*Plus also offers an AUTOTRACE facility that will display the query plan and execution statistics as each
query executes. Example:
Execution Plan
----------------------------------------------------------
Plan hash value: 2852011669
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 20 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 20 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPTNO"=40)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
2 consistent gets
0 physical reads
0 redo size
443 bytes sent via SQL*Net to client
374 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
Oracle Function-Based Indexes
Traditionally, performing a function on an indexed column in the where clause of a query guaranteed an
index would not be used. Oracle 8i introduced Function-Based Indexes to counter this problem. Rather
than indexing a column, you index the function on that column, storing the product of the function, not the
original column data. When a query is passed to the server that could benefit from that index, the query is
rewritten to allow the index to be used. The following code samples give an example of the use of
Function-Based Indexes.
SET AUTOTRACE ON
SELECT *
FROM user_data
WHERE UPPER(first_name) = 'JOHN2';
Execution Plan
----------------------------------------------------------
Plan hash value: 2489064024
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
SET AUTOTRACE ON
SELECT *
FROM user_data
WHERE UPPER(first_name) = 'JOHN2';
Execution Plan
----------------------------------------------------------
Plan hash value: 2489064024
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 20 | 540 | 5 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| USER_DATA | 20 | 540 | 5 (0)| 00:00:01 |
-------------------------------------------------------------------------------
SELECT *
FROM user_data
WHERE UPPER(first_name) = 'JOHN2';
Execution Plan
----------------------------------------------------------
----------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------
Concatenated Columns
This method works for concatenated indexes also.
SET AUTOTRACE ON
SELECT *
FROM user_data
Execution Plan
----------------------------------------------------------
Plan hash value: 1309354431
----------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------
Remember, function-based indexes require more effort to maintain than regular indexes, so having
concatenated indexes in this manner may increase the incidence of index maintenance compared to a
function-based index on a single column.
The database can use the preceding index when processing queries such as Example 3-6 (partial
sample output included).
A function-based index is also useful for indexing only specific rows in a table.
For example, the cust_valid column in the sh.customerstable has either I or A as a value. To index only
the A rows, you could write a function that returns a null value for any rows other than the Arows. You
could create the index as follows:
The optimizer can use an index range scan on a function-based index for queries with expressions
in WHERE clause. The range scan access path is especially beneficial when the predicate
(WHERE clause) has low selectivity. In Example 3-6 the optimizer can use an index range scan if an
index is built on the expression 12*salary*commission_pct.
A virtual column is useful for speeding access to data derived from expressions. For example, you could
define virtual column annual_salas 12*salary*commission_pct and create a function-based index
on annual_sal.
The optimizer performs expression matching by parsing the expression in a SQL statement and then
comparing the expression trees of the statement and the function-based index. This comparison is case-
insensitive and ignores blank spaces.
NOCOPY Hint to Improve Performance of OUT and IN OUT Parameters in PL/SQL Code
Background
Oracle has two methods of passing passing OUT and IN OUT parameters in PL/SQL code:
Pass By Value : The default action is to create a temporary buffer (formal parameter), copy the
data from the parameter variable (actual parameter) to that buffer and work on the temporary
buffer during the lifetime of the procedure. On successful completion of the procedure, the
contents of the temporary buffer are copied back into the parameter variable. In the event of an
exception occurring, the copy back operation does not happen.
Pass By Reference : Using the NOCOPY hint tells the compiler to use pass by reference, so no
temporary buffer is needed and no copy forward and copy back operations happen. Instead, any
modification to the parameter values are written directly to the parameter variable (actual
parameter).
Under normal circumstances you probably wouldn't notice the difference between the two methods, but
once you start to pass large or complex data types (LOBs, XMLTYPEs, collections etc.) the difference
between the two methods can become quite considerable. The presence of the temporary buffer means
pass by value requires twice the memory for every OUT and IN OUT parameter, which can be a problem
when using large parameters. In addition, the time it takes to copy the data to the temporary buffer and
back to the parameter variable can be quite considerable.
The following tests compare the elapsed time and memory consumption of a single call to test procedures
passing a large collection as OUT and IN OUT parameters.
Issues
There are a number of issues associated with using the NOCOPY hint that you should be aware of before
adding it to all your OUT and IN OUT parameters.
NOCOPY is a hint. There are a number of circumstances where the compiler can ignore the hint,
as described here.
If you are testing the contents of the parameter as a measure of successful completion of a
procedure, adding NOCOPY may give unexpected results. For example, suppose I pass the
value of NULL and assume if the parameter returns with a NOT NULL value the procedure has
worked. This will work without NOCOPY, since the copy back operation will not happen in the
event of an exception being raised. If I add NOCOPY, all changes are instantly written to the
actual parameter, so exceptions will not prevent a NOT NULL value being returned. This may
seem like a problem, but in my opinion if this affects you it is an indication of bad coding practice
on your part. Failure should be indicated by raising an exception, or at worst using a status flag,
rather than testing for values.
Parameter Aliasing. If you use a single variable as an actual parameter for
multiple OUT and/or IN OUT parameters in a procedure, using a mix of pass by value and pass
by reference, you may get unexpected results. This is because the final copy back from the pass
by value parameters will wipe out any changes to the pass by reference parameters. This
situation can be compounded further if the actual parameter is a global variable that can be
referenced directly from within the procedure. Although the manual describes possible issues,
once again it is an indication that you are writing terrible code, rather than a limitation of pass by
reference. You can read more about parameter aliasing here.
Short-Circuit Evaluation in PL/SQL
As soon as the final outcome of a boolean expression can be determined, PL/SQL stops evaluating the
expression. This is known as short-circuit evaluation and it can be used to improve the performance of
some boolean expressions in your PL/SQL.
Short-Circuit Evaluation of OR
Short-Circuit Evaluation of AND
Short-Circuit Evaluation of OR
If left side of an OR expression is TRUE, the whole expression is TRUE. We know this because,
So placing the least expensive tests to the left of boolean expressions can potentially improve
performance as the right hand side of the expression may not need to be evaluated.
Imagine we have a function that returns a boolean value. The amount of processing in the function is
significant, making it take a long time to complete. The following function fakes this by calling
the DBMS_LOCK.SLEEP procedure.
CONN / AS SYSDBA
GRANT EXECUTE ON DBMS_LOCK TO test;
CONN test/test
CREATE OR REPLACE FUNCTION slow_function (p_number IN NUMBER)
RETURN BOOLEAN AS
BEGIN
-- Mimic a slow function.
DBMS_LOCK.sleep(0.5);
RETURN TRUE;
END;
/
SHOW ERRORS
Depending on the boolean expression used, we may be able to avoid calling the function altogether,
giving out code a significant performance improvement.
SET SERVEROUTPUT ON
DECLARE
l_loops NUMBER := 10;
l_start NUMBER;
l_boolean BOOLEAN := TRUE;
BEGIN
-- Time normal OR.
l_start := DBMS_UTILITY.get_time;
FOR i IN 1 .. l_loops LOOP
IF slow_function(i) OR l_boolean THEN
-- Do nothing.
NULL;
END IF;
END LOOP;
DBMS_OUTPUT.put_line('Normal OR : ' || (DBMS_UTILITY.get_time - l_start));
-- Time short-circuit OR.
l_start := DBMS_UTILITY.get_time;
FOR i IN 1 .. l_loops LOOP
IF l_boolean OR slow_function(i) THEN
-- Do nothing.
NULL;
END IF;
END LOOP;
DBMS_OUTPUT.put_line('Short circuit OR : ' || (DBMS_UTILITY.get_time - l_start));
END;
/
Normal OR : 498
Short circuit OR : 0
PL/SQL procedure successfully completed.
SQL>
As expected, if the call to the slow function is placed on the right-hand side of the expression, it is not
executed, so the code is much quicker.
If the left side of an AND expression is FALSE, the whole expression is FALSE. We know this because,
FALSE AND FALSE = FALSE
FALSE AND TRUE = FALSE
FALSE AND NULL = FALSE
Once again, placing the least expensive tests to the left of boolean expressions can potentially improve
performance as the right hand side of the expression may not need to be evaluated. We can demonstrate
this using the slow function again.
SET SERVEROUTPUT ON
DECLARE
l_loops NUMBER := 10;
l_start NUMBER;
l_boolean BOOLEAN := FALSE;
BEGIN
l_start := DBMS_UTILITY.get_time;
l_start := DBMS_UTILITY.get_time;
END;/
SQL>
As expected, if the call to the slow function is placed on the right-hand side of the expression, it is not
executed, so the code is much quicker.
The RETURNING INTO clause allows us to return column values for rows affected by DML statements.
The following test table is used to demonstrate this clause.
DROP TABLE t1;
DROP SEQUENCE t1_seq;
CREATE TABLE t1 (
id NUMBER(10),
description VARCHAR2(50),
CONSTRAINT t1_pk PRIMARY KEY (id)
);
COMMIT;
When we insert data using a sequence to generate our primary key value, we can return the primary key
value as follows.
SET SERVEROUTPUT ON
DECLARE
l_id t1.id%TYPE;
BEGIN
INSERT INTO t1 VALUES (t1_seq.nextval, 'FOUR')
RETURNING id INTO l_id;
COMMIT;
DBMS_OUTPUT.put_line('ID=' || l_id);
END;/
ID=4
PL/SQL procedure successfully completed.
SQL>
SET SERVEROUTPUT ON
DECLARE
l_id t1.id%TYPE;
BEGIN
UPDATE t1
SET description = description
WHERE description = 'FOUR'
RETURNING id INTO l_id;
DELETE FROM t1
WHERE description = 'FOUR'
RETURNING id INTO l_id;
COMMIT;
END;
/
UPDATE ID=4
DELETE ID=4
SQL>
When DML affects multiple rows we can still use the RETURNING INTO, but now we must return the
values into a collection using the BULK COLLECT clause.
SET SERVEROUTPUT ON
DECLARE
TYPE t_tab IS TABLE OF t1.id%TYPE;
l_tab t_tab;
BEGIN
UPDATE t1
SET description = description
RETURNING id BULK COLLECT INTO l_tab;
COMMIT;
END;
/
UPDATE ID=1
UPDATE ID=2
UPDATE ID=3
SQL>
We can also use the RETURNING INTO clause in combination with bulk binds.
SET SERVEROUTPUT ON
DECLARE
TYPE t_desc_tab IS TABLE OF t1.description%TYPE;
TYPE t_tab IS TABLE OF t1%ROWTYPE;
l_desc_tab t_desc_tab := t_desc_tab('FIVE', 'SIX', 'SEVEN');
l_tab t_tab;
BEGIN
COMMIT;
END;
/
INSERT ID=5 DESC=FIVE
INSERT ID=6 DESC=SIX
INSERT ID=7 DESC=SEVEN
SQL>
SET SERVEROUTPUT ON
DECLARE
TYPE t_tab IS TABLE OF t1.id%TYPE;
l_tab t_tab;
BEGIN
EXECUTE IMMEDIATE 'UPDATE t1
SET description = description
RETURNING id INTO :l_tab'
RETURNING BULK COLLECT INTO l_tab;
COMMIT;
END;
/
UPDATE ID=1
UPDATE ID=2
UPDATE ID=3
SQL>