Matt Owen Cuesta College. Writing maintainable code Data structures Custom exceptions Design patterns and frameworks Unit Testing Test.

Slides:



Advertisements
Similar presentations
BD05/06 PL/SQL  Introduction  Structure of a block  Variables and types  Accessing the database  Control flow  Cursors  Exceptions  Procedures.
Advertisements

Oracle PL/SQL IV Exceptions Packages.
AN INTRODUCTION TO PL/SQL Mehdi Azarmi 1. Introduction PL/SQL is Oracle's procedural language extension to SQL, the non-procedural relational database.
PL/SQL. Introduction to PL/SQL PL/SQL is the procedure extension to Oracle SQL. It is used to access an Oracle database from various environments (e.g.
PL/SQL (Procedural Language extensions to SQL) Prepared by: Manoj Kathpalia Edited by: M V Ramakrishna.
Lecture-5 Though SQL is the natural language of the DBA, it suffers from various inherent disadvantages, when used as a conventional programming language.
Cursors How to step through the returned set of rows.
Chapter 4B: More Advanced PL/SQL Programming
Introduction to PL/SQL
Introduction to PL/SQL Lecture 0 – Self Study Akhtar Ali.
PL/SQL Bulk Collections in Oracle 9i and 10g Kent Crotty Burleson Consulting October 13, 2006.
Introduction to PL/SQL. Procedural Language extension for SQL Oracle Proprietary 3GL Capabilities Integration of SQL Portable within Oracle data bases.
Session Title: Using SQL and PL/SQL for Queries and Reporting Presented By: Stephen Frederic Institution: IHL September 16, 2013.
Bordoloi and Bock CURSORS. Bordoloi and Bock CURSOR MANIPULATION To process an SQL statement, ORACLE needs to create an area of memory known as the context.
PL / SQL P rocedural L anguage / S tructured Q uery L anguage Chapter 7 in Lab Reference.
Bordoloi and Bock EXCEPTIONS. Bordoloi and Bock Errors Two types of errors can be found in a program: compilation errors and runtime errors. There is.
Cursor and Exception Handling By Nidhi Bhatnagar.
1 Introduction to PL/SQL. 2  Procedural programming language  Uses detailed instructions  Processes statements sequentially  Combines SQL commands.
Oracle10g Developer: PL/SQL Programming1 Objectives Manipulating data with cursors Managing errors with exception handlers Addressing exception-handling.
SAGE Computing Services Customised Oracle Training Workshops and Consulting Are you making the most of PL/SQL? Hints and tricks and things you may have.
My experience building a custom ETL system Problems, solutions and Oracle quirks or How scary Oracle can look for a Java developer.
Lecture 4 PL/SQL language. PL/SQL – procedural SQL Allows combining procedural and SQL code PL/SQL code is compiled, including SQL commands PL/SQL code.
Stored Procedures, Transactions, and Error-Handling
Banner and the SQL Select Statement: Part Three (Joins) Mark Holliday Department of Mathematics and Computer Science Western Carolina University 4 November.
Programmer’s Report Engine Chris Leuer – Lead Software Engineer California Community College Solution Center SunGard Higher Education.
Oracle PL/SQL Practices. Critical elements of PL/SQL Best Practices Build your development toolbox Unit test PL/SQL programs Optimize SQL in PL/SQL programs.
1 All Powder Board and Ski Oracle 9i Workbook Chapter 7: Integrity and Transactions Jerry Post Copyright © 2003.
Overview · What is PL/SQL · Advantages of PL/SQL · Basic Structure of a PL/SQL Block · Procedure · Function · Anonymous Block · Types of Block · Declaring.
PL/SQL A BRIEF OVERVIEW DAVID WILSON. PL/SQL User’s Guide and Reference PL/SQL User’s Guide and Reference.
PL SQL Block Structures. What is PL SQL A good way to get acquainted with PL/SQL is to look at a sample program. PL/SQL combines the data manipulating.
Session Title: Using SQL and PL/SQL for Queries and Reporting Presented By: Stephen Frederic Institution: IHL September 16, 2014.
CS178 Database Management PL/SQL session 8 References: ORACLE 9i PROGRAMMING A Primer Rajshekhar Sunderraman.
CIS4368: Advanced DatabaseSlide # 1 PL/SQL Dr. Peeter KirsSpring, 2003 PL/SQL.
Trapping Oracle Server Exceptions. 2 home back first prev next last What Will I Learn? Describe and provide an example of an error defined by the Oracle.
Database Application Development using PL/SQL Programming.
Guide to Oracle 10g ITBIS373 Database Development Lecture 4a - Chapter 4: Using SQL Queries to Insert, Update, Delete, and View Data.
Creating Your Own Banner Web Pages (ID/Name Search on the Web) Robert Nitsos, Assistant Registrar, Loyola Marymount University Fr. Jim Keene, SJ, Systems.
Fall 2001Database Systems1 Triggers Assertions –Assertions describe rules that should hold for a given database. –An assertion is checked anytime a table.
PL/SQLPL/SQL Oracle11g: PL/SQL Programming Chapter 4 Cursors and Exception Handling.
Chapter Seventeen Subprogramming Objective: –Procedures –Functions –Packages.
Cursors For viewing and updating. Cursors How to step through the returned set of rows.
 In the java programming language, a keyword is one of 50 reserved words which have a predefined meaning in the language; because of this,
1 Stored Procedures in MySQL Part I. 2 Objectives SQL Vs. MySQL SP MySQL SP Parameters MySQL SP Control Structures.
Text TCS INTERNAL Oracle PL/SQL – Introduction. TCS INTERNAL PL SQL Introduction PLSQL means Procedural Language extension of SQL. PLSQL is a database.
Oracle9i Developer: PL/SQL Programming Chapter 11 Performance Tuning.
Chapter 8 Advanced SQL Pearson Education © Chapter 8 - Objectives How to use the SQL programming language How to use SQL cursors How to create stored.
Advanced SQL: Cursors & Stored Procedures Instructor: Mohamed Eltabakh 1.
Thinking in Sets and SQL Query Logical Processing.
Kingdom of Saudi Arabia Ministry of Higher Education Al-Imam Muhammad Ibn Saud Islamic University College of Computer and Information Sciences Overview.
Introduction to PL/SQL N. Dimililer. About PL/SQL –PL/SQL is an extension to SQL with design features of programming languages. –Data manipulation and.
CS422 Principles of Database Systems Oracle PL/SQL Chengyu Sun California State University, Los Angeles.
Oracle 数据库应用 -- PL/SQL 进阶 (3) & Oracle DBA 2016/5/ /5/10.
CS422 Principles of Database Systems Stored Procedures and Triggers Chengyu Sun California State University, Los Angeles.
Preface IIntroduction Course Objectives I-2 Oracle Complete Solution I-3 Course Agenda I-4 Tables Used in This Course I-5 The Order Entry Schema I-6 The.
1 Copyright © 2004, Oracle. All rights reserved. PL/SQL Programming Concepts: Review.
PLSQL Cont…. Most Common Oracle Data Types VARCHAR2 –Stores variable-length character data. –Takes a required parameter that specifies a maximum length.
Discussion 4 eecs 183 Hannah Westra.
COMP 430 Intro. to Database Systems
Pl/SQL LANGUAGE MULITPLE CHOICE QUESTION SET-3
INLS 623– Database Systems II– File Structures, Indexing, and Hashing
Active Database Concepts
Interacting with the Oracle Server
SQL Stored Triggers Presented by: Dr. Samir Tartir
Oracle11g: PL/SQL Programming Chapter 4 Cursors and Exception Handling.
UNIT - V STORED PROCEDURE.
SQL PL/SQL Presented by: Dr. Samir Tartir
Error Handling Summary of the next few pages: Error Handling Cursors.
Stored Procedure used in PosgreSQL
Chapter 4: Introduction to PL/SQL
Chapter 2 Handling Data in PL/SQL Blocks Oracle9i Developer:
Presentation transcript:

Matt Owen Cuesta College

Writing maintainable code Data structures Custom exceptions Design patterns and frameworks Unit Testing Test Driven Development Common performance issues

Maintainable Code Has Naming Conventions Has Descriptive Variables Has No Magic Numbers Has Abstraction Has Useful Comments Is easy to read and comprehend Bad Code Has No Consistency Has Poorly Named Variables Has Magic Numbers Has Redundant Code Has Useless Comments

DECLARE CURSOR cur IS SELECT spriden_first_name, spriden_last_name, spriden_mi FROM spriden, spbpers WHERE spriden_change_ind IS NULL AND spriden_pidm = spbpers_pidm AND spbpers_sex = 'M' AND(months_between(NVL(spbpers_dead_date,sysdate),spbpers_birth_date)/12) < 18; BEGIN FOR i IN cur LOOP IF i.spriden_mi IS NOT NULL THEN dbms_output.put_line(i.spriden_pidm || ' ' || i.spriden_first_name || ' ' || SUBSTR(i.spriden_mi, 1, 1) || '. ' || i.spriden_last_name || ' is younger than 18 years old.'); ELSE dbms_output.put_line(i.spriden_pidm || ' ' || i.spriden_first_name || ' ' || i.spriden_last_name || ' is younger than 18 years old.'); END IF; END LOOP; END; Magic Number Redundant Code Poor Naming

Standardized naming conventions make it easier to read code. Examples: [Root_Name]_c for cursors [Root_Name]_r for records [Root_Name]_t for tables of variables

Variable names should tell the reader what they are and what they do. 'i and cur' are not descriptive, they are iterators and cursors, but for what. male_student_r, tells you it is a male student’s record. male_student_c, tells you it is a cursor of male students.

Replace magic numbers in code with constants Improves readability by saying what it is Makes maintenance quick and easy by updating one piece of code, all references are changed This can also be done with character codes to improve readability DECLARE MAX_AGE CONSTANT PLS_INTEGER := 18; MAX_WAIT_TIME CONSTANT PLS_INTEGER := 15 * 60; /*In seconds*/ MALE CONSTANT CHAR := 'M';...

Extract logic into functions and procedures to simplify code flow Remove duplicate code FUNCTION format_name( First_Name VARCHAR2, Middle_Name VARCHAR2, Last_Name VARCHAR2) RETURN VARCHAR2 AS Full_Name VARCHAR2(4000) := First_Name; BEGIN IF Middle_Name IS NOT NULL THEN Full_Name := Full_Name || ' ' || SUBSTR(Middle_Name, 1, 1) || '.'; END IF; RETURN Full_Name || ' ' || Last_Name; END;

DECLARE MAX_AGE CONSTANT PLS_INTEGER := 18; CURSOR male_student_c IS … AND TRUNC(months_between(NVL(spbpers_dead_date, sysdate), spbpers_birth_date) / 12) < MAX_AGE; FUNCTION Format_Name(First_Name VARCHAR2, Middle_Name VARCHAR2, Last_Name VARCHAR2) RETURN VARCHAR2 AS Full_Name VARCHAR2(4000) := First_Name; BEGIN IF Middle_Name IS NOT NULL THEN Full_Name := Full_Name || ' ' || SUBSTR(Middle_Name, 1, 1) || '.'; END IF; RETURN Full_Name || ' ' || Last_Name; END; BEGIN FOR Male_Student_r IN Male_Student_c LOOP dbms_output.put_line( Format_Name(First_Name => Male_Student_r.spriden_first_name, Middle_Name => Male_Student_r.spriden_mi, Last_Name => Male_Student_r.spriden_last_name) || ' is younger than ' || MAX_AGE || ' years old.'); END LOOP; END; Removed Magic Number Extracted Code Descriptive Names

Be useful DECLARE /*Cursor containing names of all male students under MAX_AGE*/ CURSOR male_student_c IS SELECT spriden_first_name, spriden_last_name, spriden_mi FROM spriden, spbpers WHERE spriden_change_ind IS NULL AND spriden_pidm = spbpers_pidm AND SPBPERS_SEX = 'M' AND(months_between(NVL(spbpers_dead_date, sysdate), spbpers_birth_date) / 12) < MAX_AGE ;

…not redundant /* For ever record in cursor */ for i in cur loop … /* If there is no middle name */ if i.spriden_mi is not null then … /* Open the cursor */ open a_cursor; … Duh

Records Container for data No Subprograms Package or PL/SQL Block Scope Objects Container for data May Contain Subprograms Schema Scope Encapsulates date Provide abstraction 2 kinds in PL/SQL

Useful for encapsulating many related values Makes passing many values clean and concise Only usable within the PL/SQL block that defined them

DECLARE Type student IS record ( first_name spriden.spriden_first_name%type, middle_name spriden.spriden_mi%type, last_name spriden.spriden_last_name%type, age PLS_INTEGER := 0) ; … BEGIN …

DECLARE type student IS record ( First_Name spriden.spriden_first_name%type, Middle_Name spriden.spriden_mi%type, Last_Name spriden.spriden_last_name%type, Age PLS_INTEGER := 0) ; student_r student; BEGIN student_r.First_Name := 'First'; student_r.Middle_Name := 'Middle'; student_r.Last_Name := 'Last'; dbms_output.put_line(student_r.First_Name || ' ' || student_r.Middle_Name || ' ' || student_r.Last_Name || ' is ' || student_r.Age || ' years old.') ; END;

DECLARE type student … student_r student; BEGIN SELECT spriden_first_name, spriden_last_name, spriden_mi, TRUNC(months_between(NVL(spbpers_dead_date,sysdate),spbpers_birth_date)/12) age INTO student_r FROM spriden, spbpers WHERE spriden_change_ind IS NULL AND spriden_pidm = spbpers_pidm AND spriden_pidm = 1234; dbms_output.put_line(student_r.first_name||' is '||student_r.age||' Years old.'); END;

DECLARE type student … student_r student; FUNCTION format_name(student_r student) RETURN VARCHAR2 AS Full_Name VARCHAR2(4000) := student_r.First_Name; BEGIN IF student_r.Middle_Name IS NOT NULL THEN Full_Name := Full_Name || ' ' || SUBSTR(student_r.Middle_Name, 1, 1) || '.'; END IF; RETURN Full_Name || ' ' || student_r.Last_Name; END; BEGIN SELECT … INTO student_r FROM spriden, spbpers WHERE … dbms_output.put_line(Format_Name(student_r)||' is '||student_r.Age||' years old.'); END;

Useful for encapsulating many related values Makes passing many values clean and concise May contain logic to operate on the stored data Useable anywhere in the database Can't define types off of table columns

CREATE OR REPLACE TYPE student AUTHID CURRENT_USER AS OBJECT ( first_name VARCHAR2(256), middle_name VARCHAR2(256), last_name VARCHAR2(256), birth_date DATE, dead_date DATE, MEMBER FUNCTION format_name RETURN VARCHAR2, MEMBER FUNCTION get_age RETURN pls_integer, CONSTRUCTOR FUNCTION student( self IN OUT nocopy student, pidm NUMBER) RETURN SELF AS RESULT);

CREATE OR REPLACE TYPE BODY student AS MEMBER FUNCTION format_name RETURN VARCHAR2 IS full_name VARCHAR2(4000) := first_name; BEGIN IF middle_name IS NOT NULL THEN full_name := full_name || ' ' || SUBSTRC(middle_name, 1, 1) || '.'; END IF; RETURN full_name || ' ' || last_name; END; MEMBER FUNCTION get_age RETURN pls_integer IS BEGIN RETURN TRUNC(months_between( NVL(dead_date, sysdate), birth_date) / 12) ; END; CONSTRUCTOR FUNCTION student( self IN OUT nocopy student, pidm NUMBER) RETURN SELF AS RESULT AS BEGIN SELECT spriden_first_name, spriden_mi, spriden_last_name, spbpers_birth_date, spbpers_dead_date INTO self.first_name, self.middle_name, self.last_name, self.birth_date, self.dead_date FROM spriden, spbpers WHERE spriden_change_ind IS NULL AND spriden_pidm = spbpers_pidm AND spriden_pidm = pidm; RETURN; END;

DECLARE student_r student; BEGIN student_r := student(pidm => 1234) ; dbms_output.put_line(student_r.format_name || ' is ' || student_r.get_age || ' Years old.') ; END;

DECLARE student_r student; BEGIN SELECT student(pidm => spriden_pidm) INTO student_r FROM spriden WHERE spriden_change_ind IS NULL AND spriden_pidm = 1234; dbms_output.put_line(student_r.format_name || ' is ' || student_r.get_age || ' Years old.') ; END;

Notifying the calling code something exceptional happened Errors aren’t hidden in a return value Allows calling code to handle the exceptional event as it sees fit Very useful for Packages

CREATE OR REPLACE PACKAGE gyk_ AUTHID DEFINER AS EXP_UNKNOWN_ EXCEPTION; DEFAULT_FROM_ADDRESS CONSTANT VARCHAR2(20 CHAR) := SMTP_SERVER CONSTANT VARCHAR2(512) := ' '; PROCEDURE set_subject(subject VARCHAR2); PROCEDURE set_body( _body VARCHAR2); PROCEDURE add_to( VARCHAR2 DEFAULT NULL, pidm spriden.Spriden_pidm%type DEFAULT NULL, id spriden.spriden_id%type DEFAULT NULL, user_name VARCHAR2 DEFAULT NULL); PROCEDURE send_ ;... END gyk_ ;

FUNCTION get_ _address( VARCHAR2 DEFAULT NULL, pidm spriden.Spriden_pidm%type DEFAULT NULL, id spriden.spriden_id%type DEFAULT NULL, user_name VARCHAR2 DEFAULT NULL) RETURN GOREMAL.GOREMAL_ _ADDRESS%type AS _address GOREMAL.GOREMAL_ _ADDRESS%type; BEGIN... [Logic to get address based off of given information]... IF _address IS NULL THEN RAISE EXP_UNKNOWN_ ; END IF; RETURN _address; END; PROCEDURE add_to( VARCHAR2 DEFAULT NULL, pidm spriden.Spriden_pidm%type DEFAULT NULL, id spriden.spriden_id%type DEFAULT NULL, user_name VARCHAR2 DEFAULT NULL) AS _address GOREMAL.GOREMAL_ _ADDRESS%type; BEGIN /* The exception raised by get_ _address is not caught in this block */ _address := get_ _address( => , pidm => pidm, id => id, user_name => user_name); add_ (to_addresses, _address); END;

DECLARE DEFAULT_ CONSTANT VARCHAR2(20):= BEGIN /* May raise an exception if there is no address associated with the current user in banner ex BANINST1 has no address */ GYK_ .ADD_TO(user_name => USER); EXCEPTION WHEN GYK_ .EXP_UNKNOWN_ THEN /* If no address can be found, send to a default address */ GYK_ .ADD_TO( => DEFAULT_ ); END;

Poor reusability Forces the failure to have only one outcome IF _address IS NULL THEN _address := DEFAULT_ ; END IF; RETURN _address;

DECLARE TYPE student_t IS TABLE OF student; student_indx PLS_INTEGER; failed_student_indx PLS_INTEGER; students student_t := student_t(student(7366), student (1234)); failed_students student_t := student_t(); BEGIN student_indx := students.first; LOOP exit when student_indx is null; BEGIN GYK_ .ADD_TO( pidm=>students(student_indx).pidm); EXCEPTION WHEN GYK_ .EXP_UNKNOWN_ THEN failed_students.extend(1); failed_students(failed_students.COUNT):= students(student_indx); END; student_indx := students.next(student_indx); END LOOP; failed_student_indx := failed_students.first; LOOP exit when student_indx is null; /* Act on failed_students; */ failed_student_indx := failed_students.next(failed_student_indx); END LOOP; END; Waits until all students are processed then processes the errors

Only raise an exception when it can be handled by PL/SQL Avoid using exceptions when writing PL/SQL that will be called from the SQL engine CREATE OR REPLACE FUNCTION RAISE_EXCEPTION (PIDM IN SPRIDEN.SPRIDEN_PIDM%TYPE) RETURN VARCHAR2 AUTHID definer AS no_middle_name exception; BEGIN if pidm = 7366 then raise no_middle_name; else return null; end if; END RAISE_EXCEPTION; select * from spriden where RAISE_EXCEPTION(spriden_pidm) is null … ORA-06510: PL/SQL: unhandled user-defined exception

Tests that check the functionality of a single unit of code Should be automatable Must be consistent Allows the quick isolation of bugs in regression testing

CREATE OR REPLACE FUNCTION FORMAT_NAME ( FIRST_NAME IN VARCHAR2, MIDDLE_NAME IN VARCHAR2, LAST_NAME IN VARCHAR2 ) RETURN VARCHAR2 AUTHID DEFINER AS Full_Name VARCHAR2(4000) := NLS_INITCAP(First_Name); BEGIN IF Middle_Name IS NOT NULL THEN Full_Name := Full_Name || ' ' || Upper(SUBSTR(Middle_Name, 1, 1)) || '.'; END IF; RETURN Full_Name || ' ' || NLS_INITCAP(Last_Name); END FORMAT_NAME;

Write a unit test Write code to pass unit test Repeat

Fully tested code Easy regression testing Magic button that tells you everything is correct

Inefficient data types Not using indexes Truncating indexed date columns Implicit data type conversion on indexed columns Functions Cursor loops Concatenation instead of Tuples

The NUMBER data type is a 176 bit number capable of ranges between ± 10E125 with a precision of 1E-130 PLS_INTEGER is a 32 bit integer capable of ranges between -2^31 to 2^31 Operations on PLS_INTEGERS are about 3 times faster than on NUMBER and uses 20% the space If you only need integers, use PLS_INTEGER

DECLARE /*Bubble Sort*/ start_time PLS_INTEGER := dbms_utility.get_time; TYPE PLS_INTEGER_t IS TABLE OF [PLS_INTEGER or NUMBER]; collection PLS_INTEGER_t ; swap PLS_INTEGER; made_swap BOOLEAN := false; BEGIN SELECT RANDOM_VALUES_NUMBER BULK COLLECT INTO collection FROM RANDOM_VALUES; LOOP made_swap := false; FOR collection_indx IN 2.. collection.last LOOP IF collection(collection_indx - 1) > collection(collection_indx) THEN swap := collection(collection_indx) ; collection(collection_indx) := collection(collection_indx - 1) ; collection(collection_indx - 1) := swap; made_swap := true; END IF; END LOOP; EXIT WHEN made_swap = false; END LOOP; DBMS_OUTPUT.PUT_LINE((dbms_utility.get_time - start_time)* 10 || ' ms'); END; PLS_INTEGER: 2850 ms NUMBER: 3830 ms

Indexes help speed up data retrieval Only if the SQL Engine can use them Only if the index is the exact same as the predicate in the where block

SQLPlanTime SELECT * FROM SORTEST WHERE TRUNC(SORTEST_TEST_DATE) = TO_DATE(' ','mm-dd-yyyy') SELECT STATEMENT TABLE ACCESS (FULL) Filter Predicates TRUNC(INTERNAL_FUNCTION( SORTEST_TEST_DATE))=TO_DATE(...) 866 ms SELECT * FROM SORTEST WHERE SORTEST_TEST_DATE >= TO_DATE(' ','mm-dd-yyyy') AND SORTEST_TEST_DATE < TO_DATE(' ','mm-dd-yyyy'); SELECT STATEMENT TABLE ACCESS (BY INDEX ROWID) INDEX (RANGE SCAN) Access Predicates AND SORTEST_TEST_DATE >=TO_DATE(…) SORTEST_TEST_DATE <TO_DATE(…) 5 ms

SQLPlanTime SELECT * FROM SSBSECT WHERE SSBSECT_TERM_CODE = ; SELECT STATEMENT TABLE ACCESS (FULL) Filter Predicates TO_NUMBER(SSBSECT_TERM_CODE)= ms SELECT * FROM SSBSECT WHERE SSBSECT_TERM_CODE = '201203'; SELECT STATEMENT TABLE ACCESS (BY INDEX ROWID) INDEX (SCAN SKIP) Access Predicates SSBSECT_TERM_CODE = '201203' Filter Predicates SSBSECT_TERM_CODE = '201203' 7 ms

SQLPlanTime select * from SPBPERS where SUBSTR(SPBPERS_SSN,6,4)='1234' SELECT STATEMENT TABLE ACCESS (FULL) Filter Predicates SUBSTR(SPBPERS_SSN,6,4) = '1234' 360ms create INDEX indx ON spbpers (substr(spbpers_ssn,6,4)); select * from SPBPERS where SUBSTR(SPBPERS_SSN,6,4)='1234' SELECT STATEMENT TABLE ACCESS (BY INDEX ROWID) INDEX (SCAN SKIP) Access Predicates SUBSTR(SPBPERS_SSN,6,4)='1234' 60 ms

Runs in 10,070 ms, should take 10,000 ms Each iteration has overhead ~70μs per Runs in 11,200 ms Doesn't actually do any work 11 seconds of overhead ~43μs per FOR spriden_r IN (SELECT * FROM spriden WHERE rownum <= 1000) LOOP dbms_lock.sleep(0.01); /* 10ms */ END LOOP; FOR spriden_r IN (SELECT * FROM spriden) /*~250k records*/ LOOP NULL; END LOOP;

What's going on? Context switching A cursor is created in the SQL Engine for the select statement Each iteration of the loop in the PL/SQL Engine has to request the next record of the cursor from the SQL Engine Each request takes about 50 μs, across hundreds of thousands, or millions of records, it adds up

Fetch more than one record at a time Reduces the fetches, reduces the overhead Fetch SizeOverhead Reduction 10% 580% 1090% 10099% %

Why not all of them at once? Memory use increases by the inverse factor Fetch SizeOverhead ReductionMemory Growth 10%1x 580%5x 1090%10x 10099%100x %1,000x

How to store the records Declare a new Type to be a table of either a Rowtype, a Record, or an Object DECLARE TYPE spriden_t IS TABLE OF spriden%rowtype INDEX BY PLS_INTEGER; TYPE student_t IS TABLE OF student INDEX BY PLS_INTEGER; students_table student_t; spriden_table spriden_t; BEGIN … END;

DECLARE FETCH_LIMIT CONSTANT PLS_INTEGER := 50; CURSOR spriden_c IS SELECT * FROM spriden; TYPE spriden_t IS TABLE OF spriden%rowtype INDEX BY PLS_INTEGER; student_collection spriden_t; BEGIN OPEN spriden_c; LOOP FETCH spriden_c BULK COLLECT INTO student_collection LIMIT FETCH_LIMIT ; FOR student_indx IN 1.. student_collection.COUNT LOOP NULL; /* Do Work */ END LOOP; EXIT WHEN student_collection.COUNT < FETCH_LIMIT ; END LOOP; CLOSE spriden_c; END;

select * from table1 where CHARS || NUMS in (select CHARS || NUMS from table2 where NUMS = 50); select * from table1 where (CHARS, NUMS) in (select CHARS, NUMS from table2 where NUMS = 50); Explain Plan | Operation | Rows | Bytes | | SELECT STATEMENT | 1029K| 48M| | HASH JOIN | 1029K| 48M| | VIEW | 970 | | | HASH UNIQUE | 970 | | | INDEX RANGE SCAN| 970 | | | TABLE ACCESS FULL | 106K| 1761K| Explain Plan | Operation | Rows | Bytes | | SELECT STATEMENT | 970 | | | HASH JOIN SEMI | 970 | | | INDEX RANGE SCAN| 994 | | | INDEX RANGE SCAN| 970 | | ConcatenationTuple

There is an index for Chars, and Nums There is no index for their concatenation It must look at every row and concatenate Nums to Chars, then compare