Beruflich Dokumente
Kultur Dokumente
Oracle PL/SQL usage and 11g new features for Developers for DBAs
Row-by-Row Processing
DO NOT USE
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;
--- 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;
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
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
-- 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.
Lookup Queries
DO 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
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;
USE
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 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.
USE
DECLARE
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;
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_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.
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;
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;
/
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.
USE
You should avoid overusing DUAL table access. Accessing DUAL from PL/SQL causes context switching, which hurts performance.
USE
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.
USE
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.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.
USE
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;
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.
USE
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;
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;
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
USE
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
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.
Excessive Parsing
DO NOT USE
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;
...
END LOOP;
COMMIT;
END;
/
DECLARE
...
BEGIN
FOR c1_rec IN c1
LOOP
-- Query customer details
Do not use literals , Use Bind variables in SQL statements. Using literals causes excessive hard parsing stresses the library cache, thereby reducing the
applications scalability and concurrency.
Excessive Commits
DO NOT USE
USE
DECLARE
DECLARE
BEGIN
commit_number pls_integer;
BEGIN
FOR c1_rec IN c1
LOOP
-- Query customer details
commit_number := 0;
FOR c1_rec IN c1
LOOP
-- Query customer details
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.
USE
....
....
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 .
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
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;
/
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.
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.
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.
DBA_HIST_ACTIVE_SESS_HISTORY
DBA_HIST_SQLSTAT
DBA_HIST_SQLTEXT
DBA_HIST_SQL_PLAN
DBA_HIST_SYSSTAT
DBA_HIST_WAITSTAT