Book: Oracle PL/SQL Programming - Oreilly Chapter 01: Introduction to PL/SQL: 1.

1 What is PL/SQL Procedure maintain_company( action_in IN Varchar2, id_in IN NUMBER, name_in IN VARCHAR2 := NULL) IS BEGIN .... EXCEPTION END; 1.2: The concept of programming in Oracle Applications: 1.3: The origin of PL/SQL: 1.3.1: Improved Application Portability with PL/SQL. 1.3.2: Improved Execution Authority and Transaction Integrity with PL/SQL: *** Transaction Integrity: Commit and Rollback. *** No direct access to update tables, only access to tables is thru procedures. This will reduce user induced errors and Ensure Security. 1.4: PL/SQL Versions ... Integration with SQL: * PL/SQL Originally designed to provide procedural extensions to the SQL language. * From with-in PL/SQL you can execute any DML statement(SELECT, UPDATE, INSERT and DELETE). * Cannot execute a DDL statement such as CREATE TABLE. * from with-in the native PL/SQL language, can make full use of all SQL operators, predicates and functions. Outside the SQL statement, we can use functions like TO_CHAR, LIKE etc.. * Can commit and rollback transactions and create SAVEPOINTS. * Can make use of PL/SQL constructs in the SQL statements: Select employee.last_name INTO new_hire_name FROM EMPLOYEE WHERE hire_date = last_hire_date Distributed SQL: UPDATE employee@NEW_YORK SET salary = (SELECT MAX(salary) FROM employee@TORONTO) NEW_YORK and TORONTO are database links to database tables in different database instances. Expanded set of datatypes for variables and constants: * can declare local variables and constants and then use those identifiers in the PLSQL program. can declare variables and constants to be a datatype known to RDBMS such as VARCHAR2 or NUMBER. * PL/SQL specific data structures: **BOOLEAN: TRUE, FALSE or NULL **BINARY_INTEGER: similar to NUMBER, BINARY_INTEGER datatype represents values as signed binary integers of virtually any size. As signed binary is the internal format for numeric values, you can perform calculations with this datatype

that do not require any conversions. **PL/SQL record: contains one or more fields and is similar to a row in a database table. Can assign values to variables and SELECT information from the database into these variables. ROWID, RAW, LONG RAW and MLSLABEL Also defines a set of subtypes, which define constraints to the base type: NATURAL: All integers greater than zero, a subtype of BINARY_INTEGER. REAL: A Subtype of numbers. Programmer-defined records: *can create records using the %ROWTYPE attribute, these records only reflect the structure of a table or structure. *can create records with what ever structure we decide upon, completely independent of any table or cursor. [PLSQL 2.0] *programmer defined record may have another record as a field in it's record, thereby allowing nested records. example: DECLARE /* Create a record to hold 4 quarters of sales data */ TYPE sales_quarters_rectype IS RECORD ( q1_sales NUMBER, q2_sales NUMBER, q3_sales NUMBER, q4_sales NUMBER ); /* Create a record to hold sales information for a customer. Notice the nested record. */ TYPE customer_sales_rectype IS RECORD ( customer_id NUMBER (5), customer_name, total_sales NUMBER (15, 2) ); sales_by_quarter sales_quarters_rectype; /* Create a record for use in this program */ customer_sales_rec customer_sales_rectype; BEGIN ... PLSQL Tables: PLSQL table is a memory resident object that gives array like access to rows of information. similar to but not same as a database table. Currently a PLSQL table may contain only one column [with datatype of our choice] and one primary key with mandatory type of BINARY_INTEGER. Example: DECLARE /* Table of strings to hold company names */ TYPE company_names_tabtype IS TABLE OF VARCHAR2(100) INDEX BY BINARY_INTEGER; company_names company_names_tabtype; BEGIN ... Built-In functions: 1. Character functions. 2. Date functions. 3. Numeric functions. 4. Conversion functions. Built-In packages: DBMS_OUTPUT, DBMS_PIPE, DBMS_LOCK, Control Structures: Conditional Control: via IF Statements. Iterative Control: loops [FOR, WHILE and simple loops] Sequential control: GOTO and NULL statements. PLSQL does not support a CASE structure. ***************************************************************************************************** ********* 26 March 2008 ***************************************************************************************************** ********* IF-ELSIF-ELSE-END IF WHILE still_searching LOOP FOR month_index IN 1 .. 12 LOOP calculate_profits(month_index); END LOOP; ENDLOOP; GOTO transfers control from one executable statement to any other statement in the current program body. Specify NULL statement to do nothing. IF rooms_available = 0 THEN GOTO no_rooms; ELSE reserve_a_rooms; END IF; <<no_rooms>> NULL; Cursor-based access to the database DECLARE CURSOR extinction_cur IS SELECT species_name, last_sighting FROM rainforest WHERE year = 1994 AND number_species_left = 0; extinction_rec extinction_cur%ROWTYPE;

/* Execute statements based on record contents.expedition_leader VARCHAR2(100). BEGIN select social_security# INTO soc_sec_number FROM employee WHERE last_name = 'FEUERSTEIN'. an exception is raised. END IF. END. Exception handling provides an event driven model. Two types of blocks: Named and Unnamed. EXCEPTION WHEN NO_DATA_FOUND THEN INSERT INTO EMPLOYEE (last_name. hire_date.10 Modular construction Code is divided into blocks. ELSIF extinction_rec.4.4. END. DECLARE soc_sec_number NUMBER. /* Close the cursor */ CLOSE extinction_cur. /* Fetch the next record. 'STEVEN' .3.9 Error Handling When an error occurrs.last_sighting = 'BRAZIL' THEN expedition_leader := 'RAMOS'. Function: Function is a block that returns a value. social_security#.3. department_id) VALUES ('FEUERSTEIN'. first_name. END IF. 1. Named: Procedure and Function. Procedure: Sequence of executable statements that performs a particular action. */ IF extinction_rec. 10). The normal processing in the program halts and control is transferred to a seperate exception handling block. PROCEDURE display_emp_status(status_code_in IN VARCHAR2) IS /* display a message based on the status code supplied as a parameter */ BEGIN IF status_code_in = 'O' . */ FETCH extinction_cur INTO extinction_rec.last_sighting = 'BACKYARD' THEN expedition_leader := 'VIJAY'. SYSDATE. 1. BEGIN /* only open the cursor if it's not open */ IF NOT extinction_cur%ISOPEN THEN OPEN extinction_cur. 123456789.

commission) DESC.parse(cursor_id. */ DBMS_SQL. commission_in IN NUMBER) RETURN NUMBER /* Calculate return total compensation. they cannot be embedded in a SQL statement.4.4. */ cursor_id := DBMS_SQL.PUT_LINE('Status is Open'). SET and VALUES. START WITH. which persist for the entire duration of the user session.4.11 Stored Procedures.THEN DBMS_OUTPUT. SQL statement is dynamic when it's not parsed and bound at compile time. add 0 to salary if the commission is null */ IS BEGIN RETURN salary_in + nvl(commission_in.1 Stored functions in SQL Can call stored functions (written in PLSQL) from with in a SQL statement. data structures and values. total_compensation(salary. caches compiled PL/SQL programs and supplies those objects to the PLSQL runtime engine when needed by the oracle session. 1. FUNCTION total_compensation (salary_in IN NUMBER. Can call stored functions any where an expression is allowed. 'DROP ' || object_type_in || ' ' || object_name_in.4 PL/SQL Release 2. PROCEDURE drop_object (object_type_in IN VARCHAR2. /* Parse and execute the drop command which is formed through concatenation of the arguments. END. ELSE DBMS_OUTPUT. object_name_in IN VARCHAR2) IS cursor_id INTEGER.1 1.4. 1. 0). Just use one of your own functions as you would a built in SQL function.4. Stored procedures are in and of themselves PL/SQL statements. functions and packages: Oracle database repository not only for the data. 1. Stored packages offer improved performance. Shared Global Area (SGA). GROUP BY. DBMS_SQL.NATIVE).3. WHERE. such as TO_DATE. HAVING.OPEN_CURSOR. END. but also the program code.4. Instead the statement itself is passed through runtime and then is passed to the SQL engine for processing. BEGIN /* Open a cursor which will handle the dynamic SQL statement The function returns the pointer to that cursor. select last_name. . ORDER BY.2 Support for DDL and dynamic SQL DBMS_SQL package: allows you to execute dynamic SQL DDL and DML statements.PUT_LINE('Status is closed'). SUBSTR or LENGTH. eg: SELECT. commission) FROM employee ORDER BY total_compensation(salary. With Packages decide which code is publicly available to programmers and which code needs to be hidden. can implement global variables.

1.6. positive integer. 'employee'). SUBTYPE primary_key_type IS NATURAL now a variable of primary_key_type should be a non zero.4.4.5 PL/SQL Release 2. now we can declare a cursor variable and open it for any compatible query.1: The PL/SQL Wrapper: PL/SQL Wrapper: Standalone utility that transforms PL/SQL code to portable binary/object code.2: 1. 1. oracle uses DBMS_JOB to manage it's snapshot facility. ***************************************************************************************************** ************** 27/03/08 ***************************************************************************************************** ************** 1.4. END..5. we could only declare and manipulate static cursors. SUM(revenue) . EXCEPTION WHEN OTHERS THEN DBMS_SQL. 1.CLOSE_CURSOR(cursor_id).6: PLSQL Release 2.2: Cursor Variables: Before.3: 1. 1.4.4. drop_object('function'. 'myFunc').4.. job can be any valid PLSQL code. Can receive and pass cursor variables as arguments to modules. eg: drop_object('table'.5. improve maintainability and readability of code.4.3 Entry Level ANSI SQL92 Support DECLARE CURSOR profit_cur IS SELECT company_id. In the past we would have repeated the difference between the Sums or have user ORDER BY 2 DESC.4: Programmer defined sub-types: create your own subtypes of native datatypes.4. 1.4.4.close_cursor(cursor_id). .5.3: Job scheduling with DBMS_JOB: allows to schedule jobs with-in the db. BEGIN . cursors that are bound at design time to a specific query and a specific cursor name./* Close the Cursor */ DBMS_SQL.SUM(cost) net_profit FROM fin_performance ORDER BY net_profit DESC.1: File IO with UTL_FILE package: allows to read and write to operating system files from within PL/SQL.

7: PL/SQL Version 8. NCLOB: declares vars which hold a LOB locator pointing to a large block of single-byte or fixed width multibyte character data. LOB can store upto 4GB of raw data.4.7. cannot commit or rollback changes to a BFILE.4.7.2: Oracle AQ/ Advanced Queuing facility implements deferred execution of work.4. BLOB: declare vars which hold a LOB locator pointing to a large binary object.4.2: Cursor variables for all PLSQL environments: open.5 Advice for oracle programmers: ***************************************************************************************************** ******************* 30/03/08 . Internal LOBs(BLOBs. 2 types of LOBs: internal and external.4.6.1: Support for an object oriented model.4. CLOBs and NLOBs) are stored in the database and can participate in transactions.4. which provide additional information about the PL/SQL table: COUNT: returns the number of rows defined in the PL/SQL table.4: Object Views: Object views and conventional views can have their own triggers. rely on underlying filesystem for data integrity.7. fetch from and close cursors using standard PL/SQL syntax. 1. fixed width character data.4.6: Large Object Support: Support several variations of LOB or Large Object datatypes. LAST: returns the number of the highest row defined in the table. available in PLSQL through the DBMS_AQ built-in package. "INSTEAD_OF" triggers allow us to write PL/SQL code to support insert/ delete thru any view. binary data (images) or character text data. External lobs cannot participate in transactions.4. 1.7.4: Improved remote dependency model: 1.6.5: External Procedures: 1.7.7. foundation technology for workflow management systems. 1. 1. DELETE: deletes the row from the table.3: Variable arrays and nested tables: Collection structures.0: 1.4.1.' CLOB: declare vars which hold a LOB locator pointing to a large block of single-byte. 1. ***************************************************************************************************** ******************* 28/03/08 ***************************************************************************************************** ******************* 1.6. can store a table with-in a table using a collection. BFILE: declare variables which hold a file locator pointing to large binary object in an operating system file outside the database. 1. external LOBs (BFILEs) are large binary data stored outside the database table spaces. nested tables and variable sized arrays [VARRAYS].3: Expanded PL/SQL table capabilities: offers a set of operators or built-ins. Weak cursor type: allows you to declare a cursor without specifying it's record structure.

5.6. By passing a negative starting location. PACKAGE BODY check IS /* First version takes a DATE parameter */ . ROUND etc.6.. END.6. EXCEPTION WHEN OTHERS THEN NULL..4:Share your ideas.6. BEGIN DELETE FROM child2 WHERE .2: Built-in functions. anchoring data structure can be a column in a database table. where a substring is found.5. 1. the entire table itself. you can define more than one module with the same name. EXCEPTION WHEN OTHERS THEN NULL..6.5: Scoping with Nested blocks: have placed BEGIN and END keywords around a sequence of DELETE statements.2: Get ready to establish new habits.6: Module overloading: With-in a package and with-in the declaration section of a PL/SQL block. 1.6. INSTR: returns position in a string. END. a programmer defined record or a local PLSQL variable. 1.. SUBSTR will count from the end of a string. ignore the problem and move on to the next delete. 1. LENGTH: returns length of a string.1: Take a creative. 1..4: The CURSOR for loop: 1.. END.3: Assume PL/SQL has what you need: **use DBMS_PIPE to communicate information between different oracle sessions. even radical approach. This way if any DELETE statement fails. ex: my_company company. SUBSTR: returns a sub-portion of a string.3: Built-In Packages: eg: TO_CHAR.5. 1. we trap the PROCEDURE delete_details IS BEGIN BEGIN DELETE from child1 WHERE . 1.6: Few of PL/SQL features: 1.1: Anchored declarations: use %TYPE and %ROWTYPE declaration attributes to anchor the datatype of one variable to that of a previously existing data structure or variable. INSTR will actually scan in reverse through the string for the nth occurance of the substring.***************************************************************************************************** ******************* 1. 1.

FUNCTION value_ok(date_in IN DATE) RETURN BOOLEAN IS BEGIN RETURN date_in <= SYSDATE. 1. Work with records. 4. 2. learn about package generators. you should fetch into a record declared against that cursor with a %ROWTYPE attribute. compiled code is discarded. build get and set programs to retrieve data and change it.7. 2. changes are automatically applied to the code. when re-compiled.6. avoid writing repetitive code inside your package bodies. END.1: Write as little code as possible. hide it in package body. declare your own record TYPE. 1. 3. 2. create function to perform any repeated calculations. a user of your package can modify the behavior of programs inside the package with out having to change her code.7: Best practices for PL/SQL excellence: 1. simply call the appropriate package procedure or function or open appropriate package cursor.7. if declaring multiple variables which are related.6. Always fetch from an explicit cursor into a record declared with %ROWTYPE. 3. . this way. all SQL statements should be hidden behind a package interface. be prepared to work in and enhance multiple packages simultaeneously. whenever you read through every record fetched by a cursor. this module is considered local because it is only defined with-in the parent PL/SQL block. END. it cannot be called by any other PL/SQL blocks defined outside of that enclosing block. spend as much time as possible in package specifications. Anchor declarations of variables back to database columns and tables they represent. as opposed to individual variables. build toggles into your packages. Encapsulate access to your data structures with in packages. 5. 3. don't declare data in package specification. Use local modules to avoid redundancy and improve readability.7. 4. such as "local" debug mechanisms. if those database elements change. Use the cursor FOR loop. 1.8: Packages: 1.3: Center all development around packages: some best practices for working with packages: 1. fetching from and closing the cursor. Some Specs: 1. use %TYPE or %ROWTYPE.2: Synchronize Program and Data Structures: Write code so that it will adapt to changes in underlying data structures and relationships: 1. which you can turn easily on and off. whenever you fetch data from a cursor. /* Second version takes a NUMBER Parameter */ FUNCTION value_ok(number_in IN NUMBER) RETURN BOOLEAN IS BEGIN RETURN NUMBER > 0. the cursor FOR loop will save lots of typing over the manual approach of explicitly opening.7 Local Modules: local module is a procedure defined in the declaration of a PL/SQL block (anonymous/unnamed). 1.

4: Standardize PLSQL development emvironment: 1. each exception handler in a function would also return a value. if we only need to know if there's more than one match. 4. normally. 8. Also used as a multibyte wildcard symbol. . 1.7.6. a-z digits: 0-9 symbols: ~!@#$%&*()-_+=:. make sure that the name of the function depicts the value being returned and the name of the procedure depicts the action being performed.5: Structured code and other best practices: 1. when using the ELSIF statement. this is done implicitly by the PLSQL runtime engine.7. as in SQL.1 The PLSQL character set: Letters: A-Z. ensure that a function has a single successful RETURN as the last line of the executable section.can cause un-predictable behavior. such as :block.<>. 10. 7. simply fetch twice with an explicit cursor. _ single underscore: single byte wildcard symbol.2 Identifiers: Properties of an identifier: .this can cause compile errors. ========================================================================================== ======================================== CHAPTER 2: PL/SQL Language Fundamentals ========================================================================================== ======================================== ***************************************************************************************************** ***************************** 31/03/08 ***************************************************************************************************** ***************************** 2. do not use "SELECT COUNT(*)" from a table unless you need to know about the total number of hits. space. 3. as in SQL. don't let functions have OUT.single line comment indicator /**/ double line comment indicator 2. 6. 2. never declare the FOR loop index (either an integer or a record). remove all hardcoded values from programs. the function should only return value through the RETURN clause. 7. compile all of the package specification for your application before any of your bodies. do not use exceptions to perform branching logic. never exit from a FOR/WHILE loop with an EXIT or RETURN statement. replace them with named constants or functions defined in packages. always keep package specifications in seperate files from the package bodies. : host variable indicator. do not use names of tables or columns for variable names. statement terminator % attribute indicator (cursor attributes like %ISOPEN and indirect declaration attributes like %ROWTYPE). IN OUT parameters.?/ Whitespace: tab. exceptions should describe error situations only..item in Oracle Forms ** exponentiation operator <> and != not equals || concatenation operator << and >> label delimiters <= and >= relational operators := assignment operator => association operator for named notation -. 9. 5. make sure that the conditions are mutually exclusive. carriage return PLSQL is case-insensntive.

3. This literal has a datatype of CHAR (Fixed length string).00 to be a real number. Never place single quotes around boolean literals.2.6. compile error generated when trying to declare a variable named END. FALSE or NULL ***************************************************************************************************** *************************** 02/APR/2008 ***************************************************************************************************** *************************** No way to indicate a true date literal. '31-JAN-2008' or NULL BOOLEAN: TRUE. .2. even though the fractional component is 0 and the number is actually an integer. PLSQL is case sensitive with in string literals.3 Boolean literal: TRUE and FALSE.1 Embedding single quotes inside a string: Inside a literal two single quotes = 1 single quote. PLSQL considers 154. PL/SQL and SQL convert such a string automatically into a date. 2.1 Reserved words: PLSQL has reserved words. '' empty string literal and is a NULL string. 'NLS_LANG = ''ENGLISH''' = NLS_LANG = 'ENGLISH'\ '''''' = '' double quote character doesn't have any significance inside a string literal.2 White spaces and keywords: 2.2: Numeric Literal: Can be integers or real numbers(a number that contains a fractional component). must start with a letter can include $ _ and # sign cannot contain spaces 2. NUMBER: 415. 2.3.05E19 or 12e-5 are valid. Date only has an internal representation. '31-JAN-08' is a string literal.3 Literals: is a value which is not represented by an identifier.3. simply a value. should not/cannot be redefined by programmers for own uses. One reserved word is END. 2. 3. identifier is a reserved word if it has a special meaning in PLSQL.upto 30 characters in length. 21. 2. NULL STRING: 'Rahul'. by calling TO_DATE function. String literal can be composed of 0 or more characters from the PL/SQL character set.

packages group together multiple procedures and functions.1: Sections of PL/SQL block: Each PL/SQL block has upto 4 sections: Header: Relevant for named blocks only.7: Block Structure: core concepts: Modularization: The PL/SQL block is the basic unit of work from which modules such as procedures and functions are built.5: Comments: 2. we can group together declarations of variables and executable statements that belong together. . RESTRICT_REFERENCES: Tells the compiler the purity level (freedom from side effects) of a packaged program. PLSQL offers the following Pragmas: EXCEPTION_INIT: tells the compiler to associate a particular error number with an identifier we have declared as an exception in the program.6: The PRAGMA keyword: The PRAGMA keyword is used to signify that the remainder of the PLSQL statement is a pragma. PRAGMA EXCEPTION_INIT (no_such_sequence. Header determines the way a named block or program must be called.7.4: Semicolon Delimiter: 2. In the block. very similar to the tuning hints we can embed in a SQL statement inside block comments.1: Single Line Comment Syntax -2. SERIALLY_REUSABLE: Tells the PLSQL runtime engine that package-level data should not persist between references to that data. A pragma is a special instruction to the compiler.. BEGIN . can create anonymous blocks and named blocks which are functions and procedures. -2289). 2. or directive. they do not execute at runtime.5.2. Also called a pseudo-instruction. Syntax: PRAGMA <instruction> example: DECLARE no_such_sequence EXCEPTION.2: Multiline comment syntax: /**/ 2.ability to modularize is important. It simply passes information to the compiler. Scope: The block provides a scope or context for logically related objects. AUTONOMOUS_TRANSACTION: Tells the PLSQL runtime engine that the transaction is independent of the parent transaction. 2. a pragma doesn't change the meaning of a program. END. Pragmas are processed at compile time.. to the compiler.5.

2: Formatting SQL statements: We can place blank lines inside a SQL statement when we are coding that SQL from with-in a PLSQL block. can define variables. END. BEGIN year_total := 0.7.4: Formatting PLSQL blocks: .2: Scope of a block: In the declaration section of a block. The block is the scope of any objects declared in that block. Exception Section: The section that handles exceptions to normal processing (warnings and errors). any element declared in the outer block is global to all the blocks nested within it. that cursor is automatically closed at the end of the block. BEGIN month_total := year_total/12.3: Formatting Control: 3. 2. if a cursor is opened in a block. Execution Section: The part of the PL/SQL block containing the executable statements.7. gives us finer control over exception handling. ========================================================================================== ===================================== Chapter 3: Effective Coding Style ========================================================================================== ===================================== 3. these structures no longer exist. modules and other structures. These declarations are local to that block.3: Nested blocks: PROCEDURE calc_totals IS year_total NUMBER. 2. Block Header IS Declaration Section BEGIN Execution Section EXCEPTION Exception Section END. /* Nested anonymous block */ DECLARE month_total NUMBER. 3. the code is executed by the PLSQL runtime engine.1: Fundamentals of effective layout: 3. one reason to create a block is to take advantage of the exception section that comes with that block. cursors and sub-blocks that are referenced in the execution and exception sections.Declaration section: The part of block that declares variables. We may not embed white spaces in SQL statements when executing from the SQL*Plus command line. The block also provides scope for exceptions that are declared and raised. When the execution of that block finishes. END.

This means that the objects can be accessed by any account that has been granted EXECUTE authority on that package.5: Formatting Packages: Package has both specification and body. PACKAGE rg_select IS list_name VARCHAR2(60)... PROCEDURE init_list (item_name_in IN VARCHAR2.the public objects. Package Body: . END rg_select.company_id%TYPE) RETURN VARCHAR2 IS c_name company. WHEN OTHERS THEN RETURN NULL. PROCEDURE delete_list. EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL. BEGIN SELECT name INTO c_name FROM company WHERE company_id = company_id_in. 3. RETURN c_name.FUNCTION company_name(company_id_in IN company. fill_action_in IN VARCHAR2 := 'IMMEDIATE').6: Using comments effectively: 3. END. 3.7: Documenting the entire package: Package Specification: PACKAGE package_name /* || Author: || || Overview: || || Major Modifications: || */ IS . PROCEDURE clear_list. END package_name. The package specification contains the declarations or definitions of all those objects that are visible outside of the package -.company_name%TYPE.

# or _ named constant: special kind of variable. 4. can have $. value(s) 4. INT. SMALL_INT.2: Scalar Data types: Scalar datatype is atomic. END package_name. NATURAL.... range: -2pow31 + 1 to 2pow31 -1. datatype. .Package Variables ------------------------*/ . declarations . character. the value of a named constant must be set when declaring the named constant and may not change thereafter. not made up of other variable components.1: Binary Integer Datatypes: BINARY_INTEGER. performance improvement can be achieved by declaring variables as BINARY_INTEGER.. Named constant has a name.2: Select Readable names: 4. PROCEDURE . 2 composite types currently supported by PL/SQL are the record and table. Are represented in the Pl/SQL compiler as signed binary numbers and do not need to be converted before PL/SQL performs numeric calculations.Private modules --------------------------*/ FUNCTION . /*-------------------. boolean and date-time. INTEGER.1.2.1: Identifiers: Upto 30 chars in length. If performing intensive calculations with integer values.2.. Composite datatype has internal structure or components. Variables of type NUMBER do need to be converted.Public Modules ---------------------------*/ FUNCTION .1. Scalar datatypes fall into one of the four categories: number.PACKAGE BODY package_name IS /*-------------------. PROCEDURE .1: Choosing the right name: 4.1.. datatype and value. 4. ++++++++++++++++++++++++++++++++++++++++++++++ END OF PART 1 ++++++++++++++++++++++++++++++ ++++++++++++++ ========================================================================================== =============== Chapter 4: Variables and Program Data ========================================================================================== =============== Attributes of a variable: name. /*-------------------... Must start with a letter.1: Numeric Datatypes: 4..... POSITIVE BINARY_INTEGER: allows to store signed integers.

Examples: bean_counter NUMBER (10.2.1.. NUMBER (precision. then rounding occurs to the nearest whole number. scale larger than precision. will be rounded to 0. -6). Here precision means max number of digits allowed to the right of the decimal point. will be rounded off to . any value assigned to small_value must have 2 0s directly to the right of the decimal point. assign . . NATURAL: 0 to 2pow31 POSITIVE: 1 to 2pow31 4. then the scale determines the point at which rounding occurs to the right of the decimal point. the scale determines the point at which rounding occurs to the left of the decimal point. NUMERIC.NATURAL and POSITIVE are subtypes of BINARY_INTEGER. Cannot use constants or variables.6784 rounded to 12345. Both precision and scale values must be literals (and integers). * A subtype uses the storage format and restrictions on how the variable of this type can be used.45 to rounded million. since scale is 2 greater than precision. NUMBER. causes to the left of the decimal point.000 small_value NUMBER (3. rounded_million NUMBER (10. assign 1. If the scale is negative.678 big_whole_number NUMBER.2: Decimal Numeric Datatypes: DEC.000. scale): precision of a number is total number of digits. maximum precision is 38 digits. followed by three non-zero digits. 5).567.988 : rounded to 2. but it allows only a subset of the values allowed by the full datatype. 12345. -6 nearest million assign 56. Is scale is zero. can hold upto 10 digits. DECIMAL. contains the full range of supported values. 3 of which are on the right of the decimal point. DOUBLE_PRECISION. No scale: no rounding. REAL NUMBER datatype used to store fixed or floating point type. as precision: 38 and scale: 0.. ***************************************************************************************************** ********************************** 07/APR/2008 ***************************************************************************************************** ********************************** If scale is positive. 3). FLOAT.003566 to small_int.00357. -ve scale. Legal values of scale range from -84 to 127. When declaring var type NUMBER. can optionally specify variable's precision and scale. scale dictates the number of digits to the right or left of the decimal point at which rounding occurs. Scale || Rounding -1 nearest 10 -2 nearest 100 .

operations on PLS_INTEGER use machine arithmetic. Subtype Compatibility Corres Oracle Datatype DEC(prec. cannot stuff var's value into Oracle db with column type CHAR. scale) IBM NUMBER(prec. whole_paragraph CHAR(10000). as it's capacity only 255 chars. 4.3. fit_almost_anything NUMBER. overflow involving BINARY_INTEGER will not raise an exception. scale) ANSI NUMBER ANSI. scale) ANSI NUMBER ANSI.3.4. but are treated differently.2. scale) REAL SMALLINT NUMBER(prec. <variable_name> VARCHAR2 (<max_length>).2. Vars declared as PLS_INTEGER and BINARY_NUMBER have the same range.2.3: Character Datatypes: Store text and are manipulated by character functions.3: PLS_INTEGER Datatype: Vars declared as PLS_INTEGER store signed integers.2. so if var value's length > 255. scale) ANSI DECIMAL(prec. Use PLS_INTEGER for all integer calculations which do not fall outside it's range. 4. . even if range of legal values is much less than default. line_of_text CHAR. The NUMERIC. use the SUBSTR function. FLOAT. results in a numeric variable of upto 38 digits in precision. DECIMAL and DEC datatypes declare only fixed point numbers. even if we declare a CHAR var of 10000 chars. if you do not specify length of a string. have the same range of legal values as their base type.2: The VARCHAR and VARCHAR2 datatypes: store variable length character strings. IBM NUMBER(38) ANSI NUMBER(prec. must specify max length for string when declaring a variable length string. When a calculation involving PLS_INTEGER overflows. results in a character variable of only 1 char. 4. This is opposite to the situation of the NUMBER datatype. range 1 to 32767 bytes. always specify length when using CHAR datatype.2. PLSQL raises an exception. PLSQL will raise an exception. Character strings are free-form. line_of_text CHAR(80). scale) DOUBLE PRECISION FLOAT(binary) INT INTEGER NUMERIC(prec.don't declare all vars a NUMBER. magnitude range of this data-type is -2147483647 to 2147483647. IBM NUMBER(38) 4. IBM NUMBER ANSI NUMBER(38) ANSI. examples: yes_or_no CHAR(1) DEFAULT 'Y'. They are provided in oracle's SQL and PLSQL to provide compatibility with ANSI SQL. makes them more efficient.1: The CHAR datatype: specifies that a character has fixed length.1. DOUBLE PRECISION and REAL allow floating decimal points with binary precisions that range from 63 to 126. PLSQL declares a string of 1 byte. SQL/DS and DB2 datatypes. if we assign a string of more than one character to the above variable.2: Numeric Subtypes: Remiander of datatypes are all subtypes of NUMBER. if the result is being assigned to a NUMBER variable. PLS_INTEGER require less storage space than NUMBER values.

WHERE or CONNECT BY clause. 2. PLSQL LONG vars are free of these restrictions. END.3. line_of_text(2000). . 3.ex: DECLARE small_string VARCHAR2(4). Comparing VARCHAR2 and CHAR datatypes is a bit tricky: Ex: DECLARE company_name CHAR(30) DEFAULT 'PC HEAVEN'. The conditional test will never return TRUE since the value company_name has been padded to length of 30 with 21 spaces. because the length of a LONG column is 2 GB. just like LONG datatype. 4. Use RTRIM to CHAR values when involved in comparison or database modification. which is only 2000. END IF.3.2. Cannot insert full size PLSQL RAW data into Oracle RDBMS RAW. may not apply character funcs like SUBSTR. before storing PLSQL VARCHAR2 value into a VARCHAR2 db column. RAW var has the same length as the VARCHAR2 (32767 bytes).5: The LONG RAW Datatype: stores upto 32760 bytes. but cannot store data in LONG db column of size more than 32760 bytes into a LONG PLSQL column. PLSQL does not interpret LONG RAW. may not use LONG column in a GROUP BY. INSTR or LENGTH on the LONG column. which must also be specified when the datatype is declared. parent_company_name VARCHAR2(25) DEFAULT 'PC HEAVEN'. can always insert a PLSQL LONG var into a LONG db column. BEGIN IF company_name = parent_company_name THEN -. 4.This code will never be executed.4: The RAW Datatype: used to store binary data or other kinds of data such as digitized picture or image. Table may not contain more than 1 single LONG column. aviod the usage of VARCHAR and CHAR.3. PLSQL RAW Size: 32767.2. VARCHAR: subtype of VARCHAR2. The LONG datatype for PL/SQL variables is different from the LONG datatype for columns in the oracle server [can store upto 2 GB data]. this makes the LONG column possible repository for graphic images etc. Oracle RDBMS RAW Size: 255. there are many restrictions on how to use LONG column in SQL: 1. In Oracle RDBMS. difference between RAW and VARCHAR2: PLSQL will not try to interpret RAW data. 4. ORDER BY. can insert PLSQL VARCHAR2 values into a LONG db column without any worries of overflow. maximum length allowed for PLSQL VARCHAR2 variables is much higher than that of VARCHAR2 datatype in the Oracle RDBMS. remember that only the first 2000 can be inserted [use SUBSTR]. oracle will not perform character set conversions on RAW data when it's moved from one system to another system.3: The LONG datatype: Variable declared long can store 32760 bytes of data.2. but can insert into Oracle RDBMS LONG RAW [2 GB storage]. In oracle DB.

as it is inserted into the database. for multi-platform compatible applications. we cannot change the value of a rowid. ROWID is oracle rdbms specific. 4. ELSE DELETE FROM employee WHERE rowid = emp_rec. RDBMS generates the row id for each row. use the ROWID dtype to store rowids from the database. cannot actually specify this internal or literal value with an assignment. It is a fixed length value which uses seven bytes. minute.4. create a table with column with dtype of CHAR(1) and put values of 'Y' or 'N'. can perform arithmetic on date variables. Neither Oracle RDBMS DATE or the PLSQL Date datatypes stores times in increments of less than single seconds. END IF. RDBMS stores dates in a standard internal format. Time component is stored as the number of seconds past midnight. here. TO_CHAR: To convert a date to a character . day. hour. the time portion of the database value defaults to midnight(12:00:00 AM). rely on implicit conversion of character and numeric values to an actual date or explicit conversion with the TO_DATE function. ex: PROCEDURE remove_internal_competitors IS BEGIN FOR emp_rec IN (SELECT connections. faster even than a search by primary key. use dbms_rowid package to manipulate ROWIDS. The rowid is an internally generated and maintained binary value which ids a row of data in a table. we can store this info in NUMBER datatype. second PLSQL validates and stores dates between January 1.rowid. rowid FROM employee WHERE salary > 50000) LOOP IF emp_rec. Boolean is a Logical datatype. Can get sub-second timings using the DBMS_UTILITY package's GET_TIME function. 4. PLSQL provides a DATE datatype which corresponds directly with the RDBMS DATE. Oracle DATE stores the following information: century. can enter a data in many formats. not very useful to track real-time activities which happen in sub-second intervals.2. ROWID is a pseudocolumn that is a part of every table we create. can make use of date functions.connections IN ('President'. ROWID is not advisable. FALSE. such as subtraction of one date from other or addition/subtraction of numbers from a date. 4712 AD. access by ROWID is typically the fastest way to locate or retrieve a particular row from the database. month. It is called a pseudo column since SQL uses in places where we use columns.2. END.4: Boolean Datatype: TRUE. SYSDATE: returns current time and date. 'CEO') THEN send_holiday_greetings. 4712 BC to December 31. END LOOP. Instead. The information in the rowid provides the exact physical location of the row in the database.2.6: The ROWID datatype: In Oracle RDBMS. NULL.5: The Date-Time Datatype: RDBMS provides a true DATE datatype which stores both date and time information. enter a data without a time. Oracle RDBMS does not support Boolean Datatype. year.3.

7: LOB Datatypes: Large Object Datatypes. file locator contains directory alias and file name..6. var gives readonly.1: NCHAR: store fixed length NLS character data.2. default of 1 is used. ex: DECLARE book_one BFILE.2. length is length in bytes. External LOBS: BFILE are large binary data stored in operating system files outside the db tablespaces.2: BLOB: store large binary objects "out of line" inside the db. ***************************************************************************************************** ************************************* 08/APR/08 ***************************************************************************************************** ************************************* 8-bit ASCII character set is simply not able to represent all the available characters in langs like Japanese etc. a row of data for that .2.7.7. When declaring a BFILE var.2. 4.string or to a number. cannot commit or rollback changes to a BFILE. 4712 BC. Julian Date: number of days since the first valid date. CLOB and NCLOB are stored in the database and can participate in a transaction in the db server. If variable width character set. cannot participate in db transactions. DTypes: 4.2: NVARCHAR2: store variable length NLS character data. rely on underlying filesystem for data integrity. Two types of LOBs in Oracle: Internal and External Internal LOBS: BLOB. NLS features allow to convert between character sets.6: NLS Character Datatypes: NLS: National Language Support. 4. yes_no NCHAR. 4. byte-stream IO access to the files.2. LOB can store upto 4GB of raw data.6. when a table has a BLOB column. ex: any_name NVARCHAR2 (200).. max length is 32767 length context: if national character is a fixed-width character set. max length of NVARCHAR2 is 32767. 4.2. binary data (Images. we allocate memory to store the file locator of the BFILE. Such languages require 16 bits (2 bytes) to represent each character. then length indicates number of characters.1: BFILE: Store large binary objects upto 4 GB in files outside the db. Use Julian Dates if you need to perform calculations or display date information with a single point of reference and continuous dating. specify length when declaring NVARCHAR2.). 4. not the BFILE contents itself. January 1. need to specify length when declaring NCHAR. ex: ssn NCHAR (10). or character text data.

DBMS_LOB package can be used to manipulate LOBs in many ways. 4. title VARCHAR2(100). a row of data for that table contains a pointer or locator to the actual location of the CLOB data.7. NCLOB: store large blocks of single byte or fixed width multi-byte character data "out of line" inside the db. Oracle offers Random Access to LOB objects. NCLOB var contains locator. Hence it is not "in line" with other column values on that row. variable width character sets are not supported in NCLOBs.7. participate fully in transactions. any changes made to BLOB can be committed or rolled back along with other outstanding changes in the transaction. BLOB upto 4GB in size. CREATE OR REPLACE PROCEDURE how_big (title_in IN VARCHAR2) IS CURSOR book_cur IS SELECT contents_loc FROM favorite_books WHERE title = UPPER(title_in). ex: DECLARE famous_five_novel CLOB. when a table has a NCLOB column. locator points to single byte character data. contents_loc CLOB). . 4. Can perform SUBSTR and INSTR operations against a LOB. any changes made to NCLOB can be committed or rolled back along with other outstanding changes in the transaction. not possible with LONG data. participate fully in transactions. CLOB upto 4GB in size. BLOB var contains locator.table contains a pointer or locator to the actual location of the BLOB data.2. CLOB var contains locator. 4. participate fully in transactions.2. ex: DECLARE fam_five japanese NCLOB. LOB: 4 GB. when a table has a CLOB column. any changes made to CLOB can be committed or rolled back along with other outstanding changes in the transaction. NCLOB upto 4GB in size. ex: DECLARE my_foto BLOB. BLOB locators cannot span transactions or sessions. locator points to large binary object. Hence it is not "in line" with other column values on that row.5: LOBs and LONGs: LOBs are different and preferrable to LONGs.7.3: CLOB: store large blocks of single byte character data "out of line" inside the db. CLOB locators cannot span transactions or sessions. LONG cannot. LOB can be an attribute of an object type.2. but only sequential access to LONG objects. Hence it is not "in line" with other column values on that row. locator points to single byte/fixed width multi-byte character data.6: Working with LOBS: CREATE TABLE favorite_books (isbn VARCHAR2(50). LONG: 2 GB . NCLOB locators cannot span transactions or sessions. a row of data for that table contains a pointer or locator to the actual location of the NCLOB data.

use BFILENAME function to return a locator based on those two pieces of info. 4. not merely created another locator or pointer back to the same text. we work with a LOB locator. Here 'photos' is a db object called DIRECTORY. END. little_kahuna := DBMS_LOB.book_loc CLOB. END. need the CREATE DIRECTORY or CREATE ANY DIRECTORY privs to create a directory. DECLARE big_kahuna CLOB. little_kahuna VARCHAR2(2000).GET_LENGTH(book_loc)) || ' characters. Two different rows in a db table can have a BFILE column which point to the same file. the locator simply points to the file stored on the server. CREATE DIRECTORY photos AS 'c:\photos'. DECLARE all_of_us BFILE. BEGIN all_of_us := BFILENAME('photos'.').'). IF book_cur%NOTFOUND THEN DBMS_OUTPUT. copied the LOB value to this new row. title || '.SUBSTR(big_kahuna. A BFILE locator is composed of a directory alias and a filename. use DBMS_LOB to extract some or all of the CLOB value and place it in a VARCHAR2 variable. 'family. assigned a new LOB locator for the second edition. DML operations such as INSERT or UPDATE always affect an entire LOB. call appropriate functions in the DBMS_LOB package. contents_loc) SELECT isbn. Here. . to be able to reference this directory.jpg'). CLOSE book_cur. / INSERT INTO favorite_books (isbn. title. END IF. ELSE DBMS_OUTPUT. To change or delete a portion of a LOB.PUT_LINE(title_in || ' contains ' || TO_CHAR(DBMS_LOB.PUT_LINE('Remember you don''t like "' || INITCAP(title_in) || '". BEGIN SELECT contents_loc INTO big_kahuna WHERE title = 'WAR AND PEACE'. 1). BEGIN OPEN book_cur. must be granted the READ priv. contents_loc FROM favorite_books WHERE title = 'Oracle PL/SQL Programming'.2. Second Edition'. END.7: Working with BFILEs: When working with BFILEs in PL/SQL. cannot copy values between a character LOB and a VARCHAR2 var. 2000.2. FETCH book_cur INTO book_loc.

2. A NULL is never equal to anything else. A NULL is never not equal to anything.1: Explicit Data conversions: take place when we use a built-in conversion function to force conversion of a value from one dtype to another. max_salary := 0.. IS NULL : true when a var is NULL else FALSE IS NOT NULL: true when a var is not NULL else FALSE 4.8.8: Conversion between dtypes: 4.Never True.3: Drawbacks of implicit conversions: as far as possible avoid implicit conversions.. 4..3. my_string := 'Fun' y_string := NULL.3 NULLs in PL/SQL: Rules: 1.Will not work.This will never be true. including those opened using the UTL_FILE package.2..8.2: Implicit data conversions: when PLSQL detects that a conversion is necessary. 4. Not just BFILEs but all kinds of files. max number of BFILEs that can be opened in a session is established by the db initialization parameter SESSION_MAX_OPEN_FILES.3..This will never be true. my_string := ' '. -. my_string := NULL IF LENGTH(my_string) = 0 THEN . perform special case checking with IS NULL and IS NOT NULL operators. IF NULL = NULL .GRANT READ ON DIRECTORY photos TO SCOTT.8. it will attempt to change values as necessary to perform the operation. A NULL value cannot be found with INSTR function in a string. new_value := POWER(NULL... can override this with default value. IF max_salary = NULL . 10). use explicit functions for better understanding..Never True 2. make sure that var has been assigned to a value before use in an operation. -.. -.2: Checking for NULL values: IF hire_date >=SYSDATE OR hire_date IS NULL THEN . if my_string != y_string THEN .2. When we apply a NULL value to a function. we generally receive a NULL value in return..2. 3. it initializes all locally declared variables to NULL. This parameter defines an upper limit on the number of files opened simultaeneously in a session. -.New value is set to NULL. -.1: NULL values in comparisons: When PL/SQL executes a program. IF my_string = NULL . 4. -. 4. 4.

commission < comp_plan. REPLACE Function: returns a string in which all occurrances of a specified match string are replaced with a replacement string. 2nd arg is returned. exception to this rule is for the index VARS of FOR loops. NULL) ==> junk 'junk' || NULL ||' mail ' || NULL ==> junk mail 2.3.4. concatenation ignores NULL values and simply concatenates "around" the NULL.4 Variable Declarations: declare a var b4 referencing it. 4. 4. takes 2 args. PL/SQL treats a string of 0 length as NULL. If the replace string is NULL.commission IS NULL THEN non_sales_BONUS(:employee_id). function or package.4. CONCAT('junk'. and my_string = NULL. REPLACE does not try to match and replace any characters in the original string. . large_but_constrained_# NUMBER (20. Cannot be in future'). NVL function: used for translating a NULL value into a non NULL value. total_revenue NUMBER (15. procedure. <variable_name> <dtype> [optional default assignment] 4. 4. 'Not Applicable').3: Function results with NULL arguments: 1. un-constrained: no_limits NUMBER. new_description := NVL(old_description. 5). 2). un-constrained when they are no restrictions. long_para VARCHAR2 (2000). 3. itty_bitty_# NUMBER(1).PUT_LINE('Date Req. ELSIF :employee. END IF.target_commission THEN send_THANK_YOU(:employee_id). In both cases. enough_data BOOLEAN. IF :employee. ELSIF :employee. declarations should be made in the declaration section of the anonymous block.commission >= comp_plan. Concatenation: 2 ways to concatenate: CONCAT function and the concatenation operator [||]. are the same.DBMS_OUTPUT.1: Constrained Declarations: dtype in a declaration can be constrained or un-constrained.target_commission THEN send_WORK_HARDER(:employee_id). is constrained when we specify a number which constrains or restricts the magnitude of the value which can be assigned to that variable. if 1st arg's NULL. If the match String is NULL. END IF. my_string = ''.2: Declaration Examples: hire_date DATE. the replace removes from the original string any characters found in the match string.

2).2: Anchoring at compile time: make sure that all affected modules are re-compiled after data structure changes. PROCEDURE calc_reven IS . BEGIN .company_id%TYPE. 10). the var should simply be visible in that section.1: Benefits of anchored declarations: * Synchronization with database columns.5.This will raise exception. ex: total_sales NUMBER (20.3: Nesting usages of the %TYPE attribute: DECLARE -. 4. since NOT NULL constraint will conflict with the NULL assigned to company_name during instantiation. company_name VARCHAR2 (60) NOT NULL. total_rev_95 total_rev%TYPE.next_date CONSTANT DATE := '15-APR-96'. such as a PLSQL Variable or a column in a table. term_limit NUMBER DEFAULT 3. now in code company_name := NULL will cause a VALUE_ERROR exception.5. total_rev_94 total_rev%TYPE.5 Anchored Declarations: use the %TYPE declaration attribute to anchore dtype of one variable to a datastructure.5. default value can be literal. monthly_sales total_sales%TYPE.. 4. company_name VARCHAR2 (60) NOT NULL DEFAULT 'PCS R US'. <var_name> <type attribute>%TYPE [optional default value assignment]. 4.base variable unlimited_revenue NUMBER. 4.3: Default values: <var_name> <dtype> := <default_value>. <var_name> <dtype> DEFAULT <default_value>.5. declared var or an expression. 4. total_rev unlimited_revenue%TYPE..4. company_id company. -. national_debt NUMBER DEFAULT POWER (10. 4. order_overdue CONSTANT BOOLEAN := ship_date > ADD_MONTHS(3). * Normalization of local variables.4. 4. can also specify that var must be NOT NULL.4: NOT NULL Clause: when assigning a default value.4: Anchoring to vars in other PLSQL blocks: To anchor.

6: Programmer Defined SubTypes: a subtype of a datatype is a variation that sets the same set of rules as the original dtype.2: Examples of subtype declarations: SUBTYPE hire_date_type is DATE.5. but might allow only a subset of the dtype's values.6.1: Declaring sub-types: declare subtype in the declaration section... THEN DECLARE tot_rev_94 total_rev%TYPE. BEGIN IF .5 Anchoring to NOT NULL dtypes: When declaring a var. This NOT NULL declaration constraint is transferred to variables declared with the %TYPE attribute. . can also specify the var to be NOT NULL... 2147483647 DECLARE val_1 POSITIVE. above code throws a VALUE_ERROR exception. * constrained subtype: SUBTYPE POSITIVE IS BINARY_INTEGER RANGE 1 . total_rev unlimited_rev%TYPE. including pre-defined subtypes. This is not the case with db columns. BEGIN val_1 := -10000000..table_col%TYPE Determined from the dtype of the column in the table table_name%ROWTYPE Contains the same structure as the table cursor_name%ROWTYPE Contains the same structure as the virtual table created by the cursor PL/SQL table Contains the same structure as the PLSQL table previously declared with the TYPE statement 4. SUBTYPE <subtype_name> IS <base_type>. END IF. 4. BEGIN . END.unlimited_rev NUMBER. * un-constrained subtype: SUBTYPE FLOAT IS NUMBER provides an alias or an alternate name for the original dtype. END calc_reven.6. PLSQL Subtypes: Subtype category Description PL/SQL Datatype Pre-defined PL/SQL datatype. 4. such as POSITIVE Programmer Defined Subtype Previously created with a subtype declaration Variable_name%TYPE The subtype is inferred from the dtype of the variable table_name. 4.

7.7 Tips for creating and using Variables: 4. SUBTYPE last_name_type IS employee. BEGIN IF order_overdue and high_priority THEN ship_order('EXPRESS').7. SUBTYPE three_value_logic IS VARCHAR2 IN ('YES'.3: Avoid Recycling Variables 4.SUBTYPE soc_sec_number_type is POSITIVE.7. SUBTYPE company_key_type IS primary_key_number. 4. ELSE ship_order('GROUND').7.9: Use variavbles to hide complex logic DECLARE /* Hide Business Rules Behind Variables.first_name%TYPE. but only as a default. SUBTYPE primary_key_number IS NUMBER.6. -.Invalid.2: Name Subtypes to self-document code 4.4: Use named constants to avoid hard-coding values. END IF. 4. high_priority CONSTANT BOOLEAN DEFAULT cust_priority_type = 'HIGH'. We can always override that constraint. -.7.last_name%TYPE.Invalid.7: Use %TYPE when a variable represents a constant 4. This constraint takes effect when you declare variables based on the subtype. 4.7. TYPE room_tab is TABLE OF NUMBER(3) INDEX BY BINARY_INTEGER SUBTYPE hotel_room_type IS room_tab. ========================================================================================== ======================================== CHAPTER: 05 Conditional and Sequential control. 'NO'. */ order_overdue CONSTANT BOOLEAN DEFAULT (shipdate < ADD_MONTHS (SYSDATE + 3) OR order_date >= ADD_MONTHS(SYSDATE-2)) AND order_status = 'O'. Finally an anchored subtype does not carry over the NOT NULL constraint to the variables it defines.7. SUBTYPE first_name_type IS emp_rec. ========================================================================================== .1: Establish Clear Variable Naming Conventions 4.7.3: Emulating Constrained Subtypes: When creating subtype based on an existing variable or a db column. Nor does it transfer a default value that was included in the original declaration of the variable or column. END. that supertype inherits the length from the original dtype. 'MAYBE').7.5: Convert Variables to named constant 4.6: Remove unused variables from programs 4. SUBTYPE prefix IS CHAR(3).8: Use %TYPE to standardize non-database declarations 4.

*ELSE Clause optional.1 Conditional Control Statements: 5. ELSE continue_call. Statements in ELSE executed when the IF Condition executes to FALSE or NULL.1. IF new_caller AND caller_id IS NULL THEN confirm_caller. ELSIF new_company AND company_id IS NULL THEN confirm_company. 5.1. IF caller_type = 'VIP' THEN generate_response('EXPRESS').======================================== ***************************************************************************************************** ***************************** 09/APR/08 ***************************************************************************************************** ***************************** 5.1.1: IF THEN END IF. ELSE generate_response('NORMAL'). .2: IF THEN ELSE END IF. ELSIF new_call_topic AND call_id IS NULL THEN confirm_call_topic. 5. END IF. END IF. Ex: IF report_requested THEN print_report(report_id).3: IF THEN ELSIF THEN ELSE END IF.

. No overlapping conditions.1: The GOTO Statement: performs un-conditional branching to a named label. END IF. END IF.. ELSE IF <condition3> THEN . control is immediately shifted to the first executable statement following the label. Restrictions reg the GOTO statement: *Atleast one executable statement must follow a label. Avoiding Syntax Gotchas: Always Match-up an IF with an END IF. so it cannot take the place of one.2. ***************************************************************************************************** ******************************** 10/APR/2008 ***************************************************************************************************** ******************************** 5.2: Mutually Exclusive IF-ELSIF conditions: Make sure that IF-ELSIF conditions are mutually exclusive. If the evaluation of a condition is ver expensive [in CPU terms/memory].. ELSE . so that the condition is evaluated only when absolutely necessary. 5. The following examples show illegal uses of labels: IF status = 'COMPLETED' THEN <<all_done>> /* Illegal! */ ELSE schedule_activity.4: Nested IF Statements: IF <condition1> THEN IF <condition2> THEN . Syntax: GOTO label_name. END IF.5.1.3. Place semicolon only after the END IF keyword.... .. ELSIF keyword does not have an E. a label itself is not an executable statement.1. before IF.2: Sequential Control Statements: GOTO and NULL. 5.. can defer that processing to an inner IF statement. label definition: <<label_name>> When GOTO statement is encountered. END IF. Must have a space between an END and IF. .

<<all_done>> /* Illegal! */ END. RETURN formula_number.12 LOOP . below code is erratic: GOTO label_inside_IF. Don't jump into the middle of a loop: cannot jump into the middle of a loop with GOTO.. IF Condition: Only way to enter an IF block is through an evaluation of the IF condition as TRUE.. BEGIN <<label_inside_block>> /* Crosses block boundary */ NULL. /* Crosses IF clause Boundary! */ ELSIF status = 'OLD' THEN <<old_status>> GOTO new_status. This code produces error becuz it doesn't comply with the structure: GOTO label_inside_block. BEGIN FOR company_rec IN company_cur LOOP apply_bonuses(company_rec. IF status='NEW' THEN <<label_inside_IF>> /* Out of Scope! */ show_new.. BEGIN Statements: only way to enter a block with in a block is through the sub block's begin statement. * Target labels and scope of GOTO: target label should be in the same scope as the GOTO statement.DECLARE CURSOR company_cur IS .company_id). FUNCTION new_formula(molecule_in IN VARCHAR2) RETURN NUMBER IS BEGIN . PLSQL insists on orderly entrances and exits. <<loop_termination>> /* Illegal */ END LOOP. Scope Error: PLS-00375: illegal GOTO statement. END IF. this GOTO cannot branch to label. FOR month_num IN 1. /* Crosses IF clause Boundary! */ END IF. Scope of IF statements: GOTO should not transfer from one IF clause to another: IF status = 'NEW' THEN <<new_status>> GOTO old_status.... hence. END. END.

. EXCEPTION WHEN OTHERS THEN <<out_of_here>> /* Out of Scope */ NULL. A GOTO in an exception handler may reference a label in the same handler. /* label not visible here */ END. BEGIN GOTO case_statement. 5. */ GOTO out_of_here. Target labels and PL/SQL blocks: Target label must be in the same part of the PLSQL block as the GOTO statement. A GOTO in the execution section may not go to the exception section. Reasons for using a NULL statement: Improve readability of the program: IF status = 'GOOD' THEN process_order. BEGIN /* The label and GOTO should be in the same section. NULL statement does nothing except pass control to the next executable statement. Also cannot issue a GOTO from a local module into main body. DECLARE FUNCTION local_null IS BEGIN <<case_statement>> NULL. END LOOP. END. END IF. . /* Can't go back to a loop! */ Don't GOTO a local module. GOTO do_a_month. ELSE NULL.<<do_a_month>> schedule_activity(month_num).. END. Nullifying the effect of a raised exception: Structure and flow of the exception block: EXCEPTION WHEN <exception_name1> THEN .2: The NULL statement: Syntax: NULL. vice versa.

. PROCEDURE process_date (data_in IN order%ROWTYPE. Can implement top-down design by creating stubs. Stub: name and parameters which we need.avg = :sales. * Code that implements the modules. but does not propogate any exceptions to the enclosing blocks. * Modules which implement that system. EXCEPTION WHEN ZERO_DIVIDE THEN :sales_avg = 0.. but no internal implementation. WHEN OTHERS THEN NULL. "End processing in the current PLSQL block and move to the enclosing block.. Sample Stubs: PROCEDURE revise_timetable (year_in IN NUMBER) IS BEGIN NULL. but otherwise take no action".month1/:sales_total. data_action IN VARCHAR2) IS BEGIN . Supporting top-down design of modules: Top Down Design: * General description of the system. * Step-by-step refinements.WHEN <exception_nameN> THEN . END. use the NULL statement to make sure that a raised exception halts execution of the current PL/SQL block. END. it must have atleast one executable statement. Using NULL with GOTO to avoid additional statement execution: use a GOTO statement to quickly move to the end of a program if the state of the data indicates that no further processing is required.. RAISE FORM_TRIGGER_FAILURE. PROCEDURE company_name (company_id_in IN NUMBER) IS BEGIN NULL. PROCEDURE calc_avg_sales BEGIN :sales. WHEN OTHERS THEN . END. In order for a PL/SQL program to compile. END.

make permanent any changes made by your session to the database in the current transaction. 6. ROLLBACK TO statement allows you to undo all changes and release all locks which were issued since the savepoint identified by savepoint_name was marked. IF status != 0 THEN GOTO end_of_procedure.. syntax: COMMIT [WORK] [COMMENT text]. COMMIT WORK. .1. WORK keyword . COMMIT removes any row or table locks issued in the session. Parameterless ROLLBACK undos all outstanding changes in the transaction. with a TO clause to indicate a savepoint at which the ROLLBACK should stop. Without parameters 2. undo some or all changes made by the session to the database in the current transaction. 2 ways to use ROLLBACK statement: 1. syntax: ROLLBACK [WORK] [TO [SAVEPOINT] savepoint_name].1 Transaction Management: Transaction begins implicitly with the first SQL statement issued since the last COMMIT or ROLLBACK or with the start of a session. Examples: COMMIT..1: COMMIT: Saves all outstanding changes since the last COMMIT or ROLLBACK and releases all locks. stored in data dictionary along with transaction id.IF data_in. text can be handy for examining and resolving in-doubt transactions within a two phase commit framework.ship_date).order_date). .optional. once you commit. <<end_of_procedure>> NULL. END. IF data_in. COMMIT COMMENT 'maintaining account balance'.. cannot ROLLBACK once COMMIT happens. COMMENT text is usu employed in distributed trasactions. COMMENT specifies a comment which is then associated with the current transaction.order_date IS NOT NULL THEN status := validate_orderdate(data_in.1. ur changes will be visible to other oracle users or sessions. ========================================================================================== ================================================ Chapter 6: Database Interactions and Cursors ========================================================================================== ================================================ ***************************************************************************************************** ************************************* 12/APR/2008 ***************************************************************************************************** ************************************* 6.ship_date IS NOT NULL THEN status := validate_shipdate(data_in. IF status != 0 THEN GOTO end_of_procedure. erases any save points issued since the last COMMIT or ROLLBACK. 6. Text is a quoted literal and not more than 50 chars in length.2: ROLLBACK: Erases all outstanding changes since the last COMMIT or ROLLBACK and releases all locks.

preserves any changes and locks issued before this savepoint was declared. a data manipulation statement (update. if we issue a savepoint in a recursive program. establish an isolation level. LOCK TABLE table_reference_list IN lock_mode MODE [NOWAIT]. where table_reference_list is a list of one or more table references (identifying a table/view or a remote entity through a db link). or anonymous block within the SAVEPOINT statements are executed. all subsequent queries only see those changes which were committed before the transaction began. regardless of the procedure. Examples: ROLLBACK. can rollback to the same savepoint again. or DELETE PL/SQL implicitly generates a SAVEPOINT. Syntax: SAVEPOINT savepoint_name. by doing this you can share or deny access to the table while you perform operations against it. The savepoint to which you rollback is however not erased. defines how transactions that modify a db should be handled.$ and _. when we specify 'SERIALIZABLE'. oracle waits until the table is available. SHARE. This statement must be the first statement to be processed in a transaction and can only appear once. insert. that savepoint is "moved" from it's original position to the current point in the transaction. SHARE UPDATE. If you leave out the NOWAIT keyword. lock_mode is the mode of the lock. cannot be a literal (enclosed in quotes) or variable name. SHARE ROW EXCLUSIVE. function.emp@newyork IN SHARE UPDATE MODE.3: SAVEPOINT: Establishes a savepoint. which then allows to perform partial ROLLBACK. savepoints are scoped to PLSQL blocks. a DML which requires row level locks held by another transaction will wait until those row locks are released. This over-rides default row level locking usually applied to a table. all save points issued after the specified savepoint_name are erased. Immediately before you execute an INSERT. delete) which attempts to modify a table already modified in a uncommitted transaction will fail. a new SAVEPOINT is executed at each level of recursion. LOCK TABLE emp. In this way only the last DML statement is undone. UPDATE. LOCK TABLE emp IN ROW EXCLUSIVE MODE. EXCLUSIVE If you specify the NOWAIT keyword. When rolling back to a savepoint. erasing any changes or releasing any locks issued after that savepoint. LOCK TABLE scott. SET TRANSACTION READ WRITE. ROLLBACK TO begin_cleanup. SET TRANSACTION: allows to begin a read-only or read-write session. ROW EXCLUSIVE. if we specify READ COMMITTED. LOCK TABLE: Allows to lock an entire db table in the specified mode. ROLLBACK WORK. but we can only rollback to the most recent version of the savepoint.1. or assign the current transaction to a specified rollback segment. SET TRANSACTION ISOLATION LEVEL SERIALIZABLE|READ COMMITTED. which can be one of the following: ROW SHARE. dept IN SHARE MODE NOWAIT. 6. SAVEPOINT gives a name to and marks a point in the processing of your transaction. savepoint_name is an un-declared oracle identifier. can be upto 30 chars in length and can have the characters #. . if we re-use a savepoint name within the current transaction. This marker allows you to ROLLBACK to that point. sets the current transaction as read write. SET TRANSACTION USE ROLLBACK SEGMENT rollback_segname: This version assigns the current transaction to the specified rollback segment and establishes the transaction as read-write. defines the current transaction as read only. Comes in 4 flavors: SET TRANSACTION READ ONLY. oracle will not wait for the lock if the table has been locked by another user.Savepoint is an undeclared oracle identifier. This statement cannot be used in conjunction with SET TRANSACTION READ ONLY. locking a table will not stop users from querying or reading the table.

fetch rows from cursor: FETCH employee_cur INTO employee_rec. This work area contains info about the SQL statement and the set of data returned or affected by that statement.header_id AND ooh. cursor always refers to one SQL statement. such as an INSERT or SELECT which returns a single row.item_number FROM oe_order_headers_all ooh oe_order_lines_all ool WHERE ooh. comes in 2 flavors: implicit and explicit.. END.6. But when using dynamic SQL. 2. steps performed by PLSQL to execute a statement in our program: PARSE: parse SQL to check validity and determine execution plan (either rule-based/cost-based optimizer. SQL determined at compile time. can think of a cursor as a pointer into a table in the db. We define our own explicit cursors. Cursor Variables: Can declare a variable which references a cursor object in the db.1: Types of Cursors: Types of SQL which can be executed in PL/SQL: Static: Content of the SQL statement is determined at compile time.2: Cursor Operations: Regardless of the type of cursor.. for static SQL.2. cursor acts as a pointer to the virtual table generated by the SQL in cursor declaration.) BIND: associates values from the program to place holders in SQL statement.2. in above example. close cursor: CLOSE employee_cur. . Gen Syntax: CURSOR employee_cur IS SELECT * FROM employee. the set of rows returned by a cursor are called the active set or the result set of the cursor.2: Cursors in PLSQL: ***************************************************************************************************** ************************************ 13/APR/2008 ***************************************************************************************************** ************************************ When you execute an SQL statement from PL/SQL.header_id = ool. we need to explicitly request a binding of variable values. oracle RDBMS assigns a private work area for that statement. Can then use the cursor to fetch rows one by one. 6. once cursor is declared: OPEN employee_cur. act as references to cursor objects. variable may refer different SQL at different times. 6. Dynamic: Constructed at run-time and executed. DECLARE CURSOR order_cur IS SELECT ooh.order_number. PL/SQL declares and manages implicit cursors everytime we execute a SQL DML statement. ool. can also pass a cursor variable as a parameter to a procedure or function.order_number = '71000080'. must use explicit cursor when we need to retrieve more than one row of data using SELECT. Static cursor objects: static cursors of PL/SQL. the SQL engine performs these tasks. BEGIN . The row to which the cursor points to is called the current row. [Using DBMS_SQL package] Two types of Cursor objects: 1. PL/SQL cursor is a mechanism by which you can name that work area and manipulate information with-in it.

6. END. 6. CLOSE: closes cursor and releases all memory used by the cursor. but also perform a second fetch to check if too many rows are retrieved by the query [TOO_MANY_ROWS PL/SQL Exception]. cannot use explicit cursors for these statements.2: vulnerable to data errors: with implicit cursors.3: Explicit Cursors: have complete control over how to access info from the db. END IF.order_number = '71880056'. 6. BEGIN /* Open Cursor */ IF NOT order_cur%ISOPEN THEN OPEN order_cur.header_id = ool. oe_order_lines_all ool WHERE ooh. /* Close Cursor */ CLOSE order_cur.3.3. when working with explicit cursors. 6. PL/SQL will perform this for us.4: Declaring Cursors: Syntax: .1: less efficient than explicit cursors: implicit cursor executes as an SQL statement.2. 6. for SELECT statements which retrieve more than one row. Oracle's SQL is ANSI standard. sometimes we won't explicitly open a cursor. PLSQL employs implicit cursor for each UPDATE. 6. ool. we cannot handle different conditions.2. cursor no longer has a result set. The pointer to the active or current row is set to the first row.1: Implicit Cursors: PLSQL issues an implicit cursor when we execute a SQL statement directly from code.3.3: gives us less programmatic control: can't get inside the seperate operations of a cursor. can't apply programming constructs such as IF ELSE. PL/SQL moves the pointer forward in the result set. hence implicit query performs minimum 2 fetches and explicit query only one fetch.3. Always Use EXPLICIT CURSORS. program is protected against changes in data and will continue to fetch rows without raising exceptions. bind variables are used to determine the result set of the SQL statement. FETCH returns the next row from the cursor's result set.OPEN: when opening a cursor. to see whether a row was found or the cursor is already open.item_id FROM oe_order_headers_all ooh. fetch and close automatically. /* Fetch one or more rows from the cursor */ FETCH order_cur INTO order_rec. with explicit cursors.2.3: Implicit and Explicit Cursors: 6. ANSI dictates that single row fetch must not only fetch the first record.order_number. info about the state of the cursor is available through the examination of cursor attributes. FETCH does not do anything if there are no rows to FETCH (does not raise an error).3. DECLARE /* Declare the cursor */ CURSOR order_cur IS SELECT ooh. each time we fetch. we must use explicit cursors. EXECUTE: the statement is run with-in the SQL engine. once closed. FETCH: when performing query. such as open and close stages. oracle performs open.3. can't examine the attributes of a cursor.2: Drawbacks of implicit cursors: 6. outside of our programmatic control. DELETE or INSERT. have choice between using an implicit or explicit cursors only for a SELECT statement which retrieves one row.header_id AND ooh. to data access.

and the SELECT list of a SELECT statement. :review. then rename the loc vars.4.4: The cursor RETURN clause: RETURN clause of a cursor allows us to create a specification of the cursor [can be placed in package specification] which is seperate from the body [SELECT statement]. cursor with parameters: CURSOR company_cur (company_id_in IN NUMBER) IS SELECT company_id FROM company WHERE company_id = company_id_in. salary + projected_bonus new_salary.. Specification: CURSOR order_cur RETURN oe_order_headers_all%ROWTYPE Body: SELECT * FROM oe_order_headers_all. CURSOR order_cur RETURN oe_order_headers_all%ROWTYPE IS SELECT * FROM oe_order_headers_all.2: PLSQL variables in a cursor: can reference local PL/SQL program data [vars and constants].CURSOR cursor_name [([parameter.4.4. CURSOR emp_cur IS SELECT employee_id. -36). bind variables in WHERE. 6.3: Identifier precedence in a CURSOR: If local variables conflict with db column or table namesin the cursor. END. GROUP.. cursor without parameters: CURSOR company_cur IS SELECT company_id FROM company.1: The Cursor Name: follows identifier rules in PLSQL. 6. DECLARE projected_bonus NUMBER := 1000. cursor with return type: CURSOR emp_cur RETURN employee%ROWTYPE IS SELECT * FROM employee WHERE department_id = 10.evaluation FROM employee WHERE hiredate < ADD_MONTHS(SYSDATE. BEGIN . RETURN clause may be made up of any of the following datatype structures: . cannot assign values to it. HAVING. [parameter])]] [RETURN return_specification] IS SELECT statement. an undeclared identifier.4. 6. 6.

record defined from a database table. check %ISOPEN attribute of cursor b4 opening: IF NOT order_cur%ISOPEN THEN OPEN order_cur. 6. arguments are values to be passed. when opening a cursor. all the queries identified by the query are locked. 6. PLSQL executes the query for that cursor. then when the cursor is opened.5 Opening Cursors: Syntax: OPEN <cursor_name> [(argument [. try to open a cursor which is already opened. ELSE NULL. . can fetch into a record structure [%ROWTYPE attr or TYPE declaration] or can fetch into a list of one or more variables. if the cursor was declared with parameter list. Syntax: FETCH <cursor_name> INTO <record_or_variable_list>. it also ids the active set of data. rows from all involved tables that meet the criteria in the WHERE and join conditions. read consistency model in oracle RDBMS guanrantees that all fetches will reflect data as it existed when the cursor was opened.argument])]. Package Specification: PACKAGE order_pkg IS CURSOR order_cur IS RETURN oe_order_headers_all%ROWTYPE. record defined from a programmer-defined record. . regardless of when we perform the first FETCH. updates or deletes performed after the cursor was opened. Package Body: PACKAGE BODY order_pkg IS CURSOR order_cur IS RETURN oe_order_headers_all%ROWTYPE IS SELECT * FROM oe_order_headers_all. Can only open closed or never opened cursors.. that is performed by FETCH statement. END order_pkg.. The point of declaring/opening a cursor is to return/fetch rows of data from the cursor and manipulate the info retrieved. will get the following error: PL/SQL: cursor already open. If the SELECT statement uses a FOR UPDATE clause. Fetch into a PL/SQL record: FETCH company_cur INTO company_rec. using the %ROWTYPE attribute. does not actually retrieve the rows.6 Fetching from cursors: A cursor represents a virtual table within your PL/SQL program. END order_pkg. all data fetched through the cursor will ignore any inserts. END IF. The number of expressions in the cursor's select list must match the number of columns in the record identified by the table_name%ROWTYPE or PLSQL_Record%ROWTYPE. until we close the cursor. from the moment the cursor is opened.

6. can even fetch after the last record.2: Fetching past the last row: after opening cursor.8: Closing Cursors: Syntax: CLOSE <cursor_name>. specifies a max no of cursors a user can have open at once. fetch will not set the values of the INTO list to NULL. :dept. To reference the calculated column in our program. invoice i WHERE c. . PLSQL will not raise any exceptions. it will stay OPEN until you CLOSE it explicitly or disconnect oracle session.8..company_name%TYPE. company_rec company_rec_type%ROWTYPE. 6. 2. END. the cursor is only defined within (is local to) that block. 6.min_salary. 6. open cursor uses a certain amount of memory. same applies when we fetch into a record.total_sales > 5000 THEN .7: Column Aliases in Cursors: In an explicit cursor. hiredate.Fetch into a variable: FETCH new_balance_cur INTO new_balance_dollars.. 6.2: Closing local cursors: when we declare a cursor in a PL/SQL block (anonymous block.invoice_id AND i.8.invoice_date BETWEEN '01-JAN-1994' AND '31-DEC-1994'. Fetch into a row of a PLSQL table row. DECLARE CURSOR company_cur IS SELECT company_name. must check the value of the %NOTFOUND attr. then "maximum open cursors exceeded" error encountered. END IF. then it's scope is not limited to any PLSQL block. if exceeded. total_sales invoice. when execution of that block terminates. exact amount depends on the active set of the cursor. or function). When we close a cursor.company_id = i.1: Maximum number of cursors: db init parameter when starting a db instance: OPEN_CURSORS. close a cursor only if open.1: Matchinf Column list with INTO clause: When we fetch into a list of variables. the number of variables must match the number of expressions in the SELECT list of the cursor. use %ISOPEN to check. we disable it. IF company_rec. cursors can also cause db to issue row level locks when the FOR UPDATE clause is used in the select statement. BEGIN OPEN company_cur. 6. can fetch from it until there are no records left in the active set. procedure.6. If cursor is defined in a package. We fetch into a record with a %ROWTYPE declaration against a cursor. SUM(inv_amt) total_sales FROM company c. cannot fetch after cursor close. ). a variable and oracle forms bind variable: FETCH emp_name_cur INTO emp_name (1). no of cols in the record must match. TYPE company_rec_type IS RECORD ( company_name company. FETCH company_cur INTO company_rec.inv_amt%TYPE.6. column aliases are required for calculated columns when: 1. PLSQL will close cursor automatically w/o raising any exceptions. at this point the %NOTFOUND cursor attribute is set to true.

FETCH order_cur INTO order_rec. ELSE NULL.1: The %FOUND attribute: reports the status of your most recent FETCH against the cursor. order_rec order_cur%ROWTYPE. OPEN order_cur. WHILE order_cur%FOUND LOOP DBMS_OUTPUT.order_number || ' order_header_id: ' || order_rec. END.PUT_LINE('Order Number: ' || order_rec.Always include CLOSE statements.PUT_LINE('Just Fetched Row # ' || TO_CHAR(order_cur%ROWCOUNT)).9: Cursor Attributes: Access cursor attributes to get information about current status of the cursor or the result of the last fetch from the cursor. order_cur%FOUND / order_cur%NOTFOUND / order_cur%ROWCOUNT / order_cur%ISOPEN DECLARE Cursor order_cur IS SELECT order_number. END IF. evaluates to TRUE if a row was returned.header_id || ' Row Number: ' || order_cur%ROWCOUNT). reference to %FOUND attr results in INVALID_CURSOR exception.9. Explicit Cursors: %FOUND %NOTFOUND %ROWCOUNT %ISOPEN Example: Cursor order_cur IS SELECT order_number. LOOP FETCH order_cur INTO order_rec. BEGIN IF NOT order_cur%ISOPEN THEN OPEN order_cur. when cursor is not yet opened. order_header_id FROM oe_order_headers_all. FETCH order_cur INTO order_rec. 6. FALSE if no row was returned. END LOOP. order_header_id FROM oe_order_headers_all. END LOOP. 6. EXIT WHEN NOT order_cur%FOUND DBMS_OUTPUT. . CLOSE order_cur. CLOSE order_cur.

DECLARE CURSOR order_cur IS SELECT order_number.9. END LOOP. DBMS_OUTPUT.PUT_LINE(order_rec. will encounter error when trying to open a cursor which is already open. 6. "cursor already open".5: Implicit SQL cursor attributes: can access info about most recently executed SQL statements through SQL cursor attributes.9.header_id || ' Row Number: ' || order_cur%ROWCOUNT).3: The %ROWCOUNT attribute: returns the number of records fetched from a cursor at the time this attribute was queried. 6. END. . CLOSE order_cur.2: The %NOTFOUND attribute: opposite of %FOUND.6. DBMS_OUTPUT. LOOP FETCH order_cur INTO order_rec. SQL%FOUND. CLOSE order_cur. BEGIN IF NOT order_cur%ISOPEN THEN OPEN order_cur.header_id || ' ' || order_cur%ROWCOUNT).order_number || ' ' || order_rec. returns TRUE when cursor is unable to fetch rows as the last row has been fetched and FALSE when a row has been fetched. LOOP FETCH order_cur INTO order_rec. END IF. can use %ROWCOUNT attr to limit the number of records fetched from a cursor. EXIT WHEN order_cur%NOTFOUND.9. END LOOP. header_id FROM oe_order_headers_all. referencing the %ROWCOUNT attribute before opening a cursor will result to INVALID_CURSOR exception. 6.order_number || ' order_header_id: ' || order_rec. referencing %NOTFOUND when a cursor has not yet been opened will result in INVALID_CURSOR exception. otherwise returns FALSE. order_rec order_cur%ROWTYPE.9.4: The %ISOPEN attribute: returns TRUE when cursor is open. EXIT WHEN order_cur%NOTFOUND OR order_cur%ROWCOUNT > 10.PUT_LINE('Order Number: ' || order_rec. <cursor_name>%FOUND = NOT <cursor_name>%NOTFOUND <cursor_name>%NOTFOUND = NOT <cursor_name>%FOUND OPEN order_cur. ELSE NULL.

6.4: Cursor Parameter Modes: Only Supports the IN parameter type.2: Opening cursors with parameters: DECLARE CURSOR order_cur (order_type_in VARCHAR2) IS SELECT order_number. 6. order_header_id WHERE UPPER(order_type) = UPPER(order_type_in). never rely on %FOUND or %NOTFOUND attributes.10. then %ROWCOUNT will return NULL.9.10: Cursor Parameters: * Parameters make cursors more reusable. %ISOPEN. PLSQL raises the NO_DATA_FOUND exception. 6. UPDATE or DELETE affected atleast one record.3: Scope of Cursor Parameters: Scope of cursor param is confined to that cursor.5: Default values for parameters: ..10. * Parameter avoid scoping problems: When passing parameters instead of hard-coding values. Implicit cursor can never be open outside of the statement itself. INSERT and UPDATE.10. After statement (SELECT. SQL%ISOPEN. then PLSQL throws TOO_MANY_ROWS exception. BEGIN . Will return true if a SELECT retrieves atleast one row. OUT and IN OUT are not supported. the result set for that cursor is not tied to a specific variable in a program or block.6: Differences between implicit and explicit cursor attributes: * If RDBMS has not opened an implicit cursor for the session. make sure that you reference the attribute immediately after execution of the SQL statement. * Behavior of %NOTFOUND is opposite of %FOUND behavior for SELECT. 6.10. can define the cursor at a higher level and use it in any of the sub-blocks with variables of those local blocks.1: Generalizing cursors with parameters: DECLARE CURSOR order_cur (order_type_in VARCHAR2) IS SELECT order_number. UPDATE. END. when using SELECT. SQL%ROWCOUNT. INSERT or DELETE) has executed. 6. %FOUND and %NOTFOUND will return FALSE. order_header_id WHERE UPPER(order_type) = UPPER(order_type_in). When implicit SELECT returns more than one row. If the program has nested blocks.. the implicit cursor will have opened and closed implicitly. INVALID_CURSOR exception will not be thrown.SQL%NOTFOUND. 6. When exception is raised. 6. returns FALSE if no records were affected. control shifts to exception block. * When an implicit SELECT statement does not return any rows. cannot refer to the cursor parameter outside of the SELECT statement associated with that cursor. * If you plan to make use of an SQL cursor attr.10. * %ISOPEN will always return false before and after the execution of the SQL statement. * %FOUND attr returns TRUE if a SELECT. END. BEGIN OPEN order_cur('DP PROJECT ORDER').

hp.. END. CURSOR cust_num_cur IS SELECT hp. rows in a table are locked only if the FOR UPDATE clause references a column in that table.header_id%TYPE.FOR UPDATE statement.11: SELECT FOR UPDATE in cursors: ***************************************************************************************************** ************************************* 14/APR/08 ***************************************************************************************************** ************************************* When you issue a SELECT.party_id AND hp.party_id. hz_cust_accounts hca WHERE hp. BEGIN OPEN order_cur('Getronics Order'). order_rec order_cur%ROWTYPE. 6.party_name. No one else will be able to change any of these records until you perform a COMMIT or ROLLBACK. FETCH order_cur INTO my_order_number. my_order_number oe_order_headers_all.customer_number FROM hz_parties hp.party_id = hca. hca. RDBMS automatically obtains row level locks for all rows identified by the SELECT statement. OF list of the FOR UPDATE clause does not restrict us from changing only those columns listed. /* Both Legal */ --FETCH order_cur INTO order_rec. my_header_id. Can use the FOR UPDATE clause in a SELECT against multiple tables. Here.order_number%TYPE.DECLARE CURSOR order_cur (order_type_in VARCHAR2 := 'DP Project Order') IS SELECT order_number. CURSOR instance_cur IS SELECT * FROM csi_item_instances WHERE instance_number = '123546' FOR UPDATE OF install_location_id.customer_number. Locks are still placed on all . header_id FROM oe_order_headers_all WHERE UPPER(order_type) = UPPER(order_type_in). header_id FROM oe_order_headers_all WHERE order_number = '71800065' FOR UPDATE. my_header_id oe_order_headers_all.party_id = '268746' FOR UPDATE OF hca.. Here no rows of the hz_parties table are locked. --OPEN order_cur. CURSOR order_cur IS SELECT order_number.

Error Encountered: 'Fetch Out Of Sequence'. DELETE FROM table_name WHERE CURRENT OF cursor_name. END IF. 6. FOR UPDATE OF order_type. Now. 6.header_id. COMMIT. When a COMMIT or ROLLBACK occurs.1: Releasing Locks with COMMIT: When we open a cursor with FOR UPDATE clause. UPDATE table_name SET clauses WHERE CURRENT OF cursor_name. all rows identified by the cursor are locked. the locks are released. END LOOP.2: WHERE CURRENT OF Clause: PLSQL provides WHERE CURRENT OF clause for both the UPDATE and DELETE statements inside a cursor in order to change the most recently fetched row of data. Without NOWAIT your process will block until the table is available. BEGIN FOR order_type_rec IN order_type_cur /* Cannot make another FETCH after COMMIT */ LOOP IF order_date >= SYSDATE THEN UPDATE oe_order_headers_all SET order_type = 'DP_GETRONICS_ORDER' WHERE UPPER(order_type) = 'GETRONICS_ORDER' AND order_date >= SYSDATE AND header_id = order_type_rec.11. BEGIN . order_date FROM oe_order_headers_all WHERE UPPER(order_type) = 'GETRONICS_ORDER'.rows. DECLARE CURSOR order_type_cur IS SELECT header_id. order_number. order_type. order_number. we cannot execute another FETCH against a FOR UPDATE clause. Can append keyword NOWAIT to the FOR UPDATE clause to tell not to wait if the table has been locked by another user. The position in the cursor is lost. Update these columns in that row i just fetched. order_date FROM oe_order_headers_all WHERE UPPER(order_type) = 'GETRONICS_ORDER'. No limit to the wait time unless the table is remote. In this case control will immediately returned to the program. Use WHERE CURRENT OF Clause: Delete the record i just fetched. order_type.11. END. DECLARE CURSOR order_type_cur IS SELECT header_id. after the COMMIT or ROLLBACK occurs. so that we can proceed with other work or wait until we have a lock on the table.

FETCH my_sample_cur INTO emp_rec. reference standard attrs: %ISOPEN. END IF. %FOUND. COMMIT. CLOSE my_sample_cur. END. emp_rec employee%ROWTYPE. provides a mechanism for passing results of queries between different PLSQL programs. END.2: Similarities to Static Cursors: * The CLOSE statement: DECLARE TYPE my_sample_cur_type IS REF CURSOR. even different queries with-in a single program execution. order_rec oe_order_headers_all%ROWTYPE.12: Cursor Variables: Cursor variable can be opened for any query. END. * Can OPEN/CLOSE/FETCH from cursors.FOR order_rec in order_cur LOOP IF order_date >= SYSDATE THEN UPDATE oe_order_headers_all SET order_type = 'DP_GETRONICS_ORDER' WHERE CURRENT OF order_type_cur. END LOOP.1: Features of Cursor Variables: Cursor Vars let you: * Associate a cursor variable with different queries at different times in your program execution. FETCH order_cur_var INTO order_rec. BEGIN OPEN order_cur_var FOR Select * FROM oe_order_headers_all. * Cursor Attributes: . DECLARE TYPE order_cur_type IS REF CURSOR RETURN oe_order_headers_all%ROWTYPE. my_sample_cur my_sample_cur_type. 6. * Pass a cursor variable as an argument to a procedure or function. CLOSE order_cur_var. 6.12. BEGIN OPEN my_sample_cur FOR SELECT * FROM employee. 6. * Assign contents of one cursor variable [and it's result set] to another cursor variable. %ROWCOUNT.12. order_cur_var order_cur_type. even between client and server PLSQL programs. %NOTFOUND.

Declaration of a cursor variable does not create an object. 6.my_sample_cur%ISOPEN. BEGIN OPEN emp_cur FOR SELECT * FROM emp. * Fetching from cursor var: Syntax is same. can be used to query multiple SQL statements within scope of a single program. Strong Type Cursor: TYPE order_cur_type IS REF CURSOR RETURN oe_order_headers_all%ROWTYPE. END. not associated with any record structure. DECLARE TYPE emp_cur_type IS REF CURSOR RETURN employee%ROWTYPE. FETCH emp_cur INTO emp_rec. my_sample_cur%ROWTYPE. can only be used with SQL statement and FETCH INTO whose data structures match the specified record type. CLOSE emp_cur. emp_cur emp_cur_type. Cursor variable points to a cursor object. OPEN <cursor_name> FOR <select_statement>. emp_rec employees%ROWTYPE. Declare the actual cursor variable based on that type. my_sample_cur%FOUND. Weak Type Cursor: TYPE order_cur_type IS REF CURSOR. TYPE <cursor_type_name> IS REF CURSOR [RETURN <return_type>]. but PLSQL applies additional rules to make sure that the data structure of the cursor variable's row match that of the data structures to the right of the INTO keyword. 6.3: Declaring REF CURSOR Types and Cursor Variables: Steps to create cursor variables: Create a referenced cursor TYPE. my_sample_cur%NOTFOUND. Cursor object has the result set which is identified by the SQL statement attached to it.12.4: Opening Cursor Variables: We assign a value (cursor object) to the cursor object when we OPEN the cursor. Strong Type REF CURSORS: The structure of the select statement must match the return type specified in the TYPE declaration statement. can be used much more flexibly than strong type cursors. Weak Type REF CURSORS: can OPEN it for any query. DECLARE .12. attaches a record type to the cursor variable at the time of the cursor declaration.

2. PROCEDURE open_site_list (address_in IN VARCHAR2. site_cur_inout IN OUT building_curtype) IS home_type CONSTANT INTEGER := 1. Create Procedure.5.TYPE util_cur_type IS REF CURSOR.12. util_cur_var util_cur_type. 6. OPEN util_cur_var FOR SELECT SYSDATE FROM dual.site_type = home_type THEN OPEN site_cur_inout . Example: 1. commercial_type CONSTANT INTEGER := 2. when the FETCH is executed. it checks for datastructure compatibility. CURSOR site_type_cur IS SELECT site_type FROM property_master WHERE address = address_in.5: Fetching from Cursor Variables: FETCH <cursor variable name> INTO <record type>. mode of cursor variable param is IN OUT.2: Handling the ROWTYPE_MISMATCH exception: Before PL/SQL performs the FETCH. FETCH site_type_cur INTO site_type_rec. then OPEN FOR reuses the variable and simply points it to another SQL query.12. without skipping any rows in the result set. CLOSe site_type_cur. 6. OPEN FOR statement implicitly creates an object for the variable. FETCH <cursor variable name> INTO <variable1. variable3>. the data structure compatibility check must happen at run time.5. BEGIN OPEN util_cur_var FOR SELECT * FROM employees. Define Weak REF CURSOR: TYPE building_curtype IS REF CURSOR. If cursor variable has not yet been assigned to any cursor object. END. BEGIN OPEN site_type_cur.1: Strong and Weak REF CURSOR Types: For Strong REF CURSOR types. PLSQL engine will raise pre-defined ROWTYPE_MISMATCH exception. 6. variable2. the data structure compatibility check happens at compile time. If the query and the INTO clause do not structurally match [even after using implicit conversions].12. site_type_rec site_type_cur%ROWTYPE. can trap the ROWTYPE_MISMATCH and try to FETCH the cursor variable using another INTO clause. But for Weak REF CURSOR types. IF site_type_rec. If cursor variable already points to some object. OPEN util_cur_var FOR SELECT * FROM oe_order_headers_all.

6. * Weak REF CURSOR type var may OPEN FOR a query of any rowtype. building_curvar).12.12. Code which uses ROWTYPE_MISMATCH to read the cursor obtained from open_site_list: DECLARE building_curvar building_curtype.12. * Both variables (or params) are of weak REF CURSOR type. An OPEN statement FOR that query was executed for the cursor variable. a corresponding field in the second list has the same PLSQL datatype. * Strong REF CURSOR type may OPEN FOR a query whose rowtype matches structurally to the rowtype specified in the TYPE specification.1: Compile Time Row Matching Rules: Two cursor variables(incl procedure params) are compatible for assignment and argument passing if any of the following are true: * Both Variables (or params) are strong REF CURSOR type with the same <rowtype_name>. A cursor variable was assigned a value. 3. * For each field in one record.site_type = commercial_type THEN OPEN site_cur_inout FOR SELECT * FROM commercial_properties WHERE address LIKE '%' || address_in || '%'. open_site_list(address_string. FETCH building_curvar INTO home_rec. no matter what it referred to earlier. home_rec home_properties%ROWTYPE. * Strong REF CURSOR type may refer to a query whose rowtype matches structurally to the rowtype specified in the TYPE specification.2: Run-time row matching rules: * Weak REF CURSOR type var may refer to a query of any rowtype. END. which can be converted .6. other is of any weak REF CURSOR type. show_commercial_site(commercial_rec). * Two records (or LOVs) are considered structurally matching [with implicit conversions] if: * The no of fields is same in both the records.6: Rules for cursor variables: A cursor variable is said to "refer to a given query" if either of the following is true: 1. from another cursor variable that refers to that query. no matter what it referred to earlier. BEGIN prompt_for_address(address_string). commercial_rec commercial_properties%ROWTYPE. 6.FOR SELECT * FROM home_properties WHERE address LIKE '%' || address_in || '%'. regardless of the rowtype. show_home_site(home_rec). WHEN ROWTYPE_MISMATCH THEN FETCH building_curvar INTO commercial_rec. * One variable (parameter) is any Strong REF CURSOR type. END open_site_list. 2. ELSIF site_type_rec. 6. END IF. 6.

. BEGIN DECLARE curvar2 curvar_type. the two cursor variables become aliases for the same cursor object. assign that cursor variable to a cursor variable in another scope. emp_rec employees%ROWTYPE.6. the PLSQL block in which the variable is declared [if declared in a package. * For a cursor variable (parameter) used in a FETCH statement. the cursor object remains accessible as long as atleast one cursor variable refers to that object. the record or list of variables of the INTO clause of the FETCH PLSQL to match the first. 6. DECLARE TYPE curvar_type IS REF CURSOR. -. DECLARE TYPE curvar_type IS REF still accessible END.INVALID_CURSOR Exception END. BEGIN OPEN curvar2 FOR SELECT order_number FROM oe_order_headers_all WHERE header_id = 235457.record 2 CLOSE curvar1. -.order_number%TYPE. BEGIN OPEN curvar1 FOR SELECT * FROM employees. Cursor object: Once an OPEN FOR creates a cursor object. curvar1 := curvar2. share the reference to the cursor object. FETCH curvar1 INTO emp_rec. END. FETCH curvar2 INTO emp_rec. -. the cursor object remains accessible even if the original cursor variable has gone out of scope. action taken against the cursor object using one variable is available to and reflected by the other cursor variable. curvar2 curvar_type. curvar1 curvar_type. curvar1 curvar_type. -. it's globally accessible].6. Same rule used for static cursors.3: Cursor Variable Aliases: If we assign one cursor variable to another. the query associated with the cursor variable must structurally match with implicit conversions.record 1 FETCH curvar2 INTO emp_rec. curvar2 := curvar1.4: Scope Of a cursor object: same as that of a static cursor. create a cursor object in one PLSQL Block [scope] and assign it to another cursor variable. order_number_var oe_order_headers_all.12. 6. FETCH curvar1 INTO order_number_var.

my_order_num_cur order_cur_type.7. need to specify the mode of the param and the datatype. FETCH my_order_num_cur INTO my_order_num.header_id%TYPE .order_number%TYPE. . BEGIN OPEN local_order_num_cur FOR SELECT order_number FROM oe_order_headers_all WHERE header_id = order_header_id.12. END package. PROCEDURE get_order_num_cur ( order_header_id IN oe_order_headers_all. IN OUT: Read/Write in a program. 6.12. PACKAGE company IS TYPE curvar_type IS REF CURSOR RETURN company%ROWTYPE. my_order_num_cur). 6. CLOSE my_order_num_cur.12.2: Setting the parameter mode: IN : Can only be read by the program. END. BEGIN get_order_num_cur(435257. the only way you can reference a pre-existing REF CURSOR is by placing the TYPE statement in a package.7: Passing Cursor Variables as arguments: can pass cursor variable as an argument to a function or procedure. cannot be able to fetch from it within the server. All variables declared in the package specification act as global variables with-in a session. * Cannot use RPCs (Remote Procedure Calls) to pass a cursor from one server to another. 6.curvar_type) IS BEGIN END. OUT : Can only be written to by the program. in-equality or nullity using comparison operators. order_num_cur := local_order_num_cur.12. unless you also open it within the same call. my_order_num oe_order_headers_all. END. cannot contain FOR UPDATE.7. order_num_cur OUT order_cur_type) IS local_order_num_cur order_cur_type.8: Cursor Variable Restrictions: * Cannot be declared in a package. * The query you associate with a cursor in OPEN FOR. * cannot test for cursor variable equality.order_number%TYPE.1: Identifying REF CURSOR type DECLARE TYPE order_cur_type IS REF CURSOR RETURN oe_order_headers_all. * If we pass a cursor variable as a bind or host variable to PLSQL. PROCEDURE open_company(curvar_out OUT company. If creating a stand-alone procedure or function.6. since they do not have a persistent state.

will not be able to use REF CURSOR types to specify column types in statements to CREATE TABLEs or CREATE VIEWs.13. 6.1. /* Record for the above cursor */ task_rec task_cur%ROWTYPE. /* Variables which determine whether the function should continue to loop through cursor's records.2: Using multiple fetches more efficiently 6. more_tasks BOOLEAN := false. IF more_tasks THEN /* A record was fetched. so that no one else can modify that record while the user is at work on that un-assigned task.task_id%TYPE IS /* Cursor of all tasks. Create an anonymous block within the function so that we can trap the record_locked exception and still stay in the loop.13.1: Validating Foreign key entry with cursors: 6. FUNCTION next_task RETURN task. assigned and un-assigned */ CURSOR task_cur IS SELECT task_id FROM task WHERE task_status = 'OPEN' ORDER BY task_priority. -54). * Cursor variables cannot be used with dynamic SQL [DBMS_SQL package]. */ BEGIN /* Try to get a lock on the current Task */ SELECT task_id INTO return_value .13: Working with CURSORS: 6.* cannot assign NULLs to a cursor variable. more_tasks = task_cur%FOUND. BEGIN /* Open cursor and start lookup through it's records. */ found_unassigned_task BOOLEAN := false.1: In-efficiency of Group functions in cursors.13. if nothing's found. 6. PRAGMA EXCEPTION_INIT(record_locked. hand it to the user and obtain a lock on that record. we're done */ FETCH task_cur INTO task_rec.task_id%TYPE. */ WHILE NOT found_unassigned_task AND more_tasks LOOP /* Fetch the next record.1.2: Managing a work queue with SELECT FOR UPDATE Task: To find an unassigned task. /* An Exception for error ORA-0054 "resource busy and acquire with NOWAIT specified" */ record_locked EXCEPTION. date_entered DESC. * Database columns cannot store cursor variable values.13. /* Primary key of the un-assigned task to be returned */ return_value task.

Notice that SELECT INTO has therefore already set the function's return value. then we were able to get a lock onto this particular task. END. END IF. END. RETURN return_value. */ CLOSE task_cur.task_id FOR UPDATE OF task_id NOWAIT.1. EXCEPTION WHEN OTHERS THEN CLOSE task_cur. Notice that if an un-assigned task was NOT found. so keep going. END LOOP. Now Set the boolean to stop the loop */ found_unassigned_tasks := TRUE. The Simple Loop: PROCEDURE set_all_ranks(max_rank_in IN INTEGER) IS ranking_level NUMBER (3) := 1. .1: Examples Of Different Loops: Procedure executed in each of the following loops: set_rank(ranking_level): Accepts the maximum ranking level as an argument and sets the ranking until the level exceeds the maximum. */ NULL. ranking_level := ranking_level + 1.1: LOOP BASICS: 7. EXIT WHEN ranking_level > max_rank_in. BEGIN IF max_rank_in >= 1 THEN LOOP set_rank(ranking_level).FROM task WHERE task_id = task_rec. END LOOP. ========================================================================================== ================================================= Chapter 7: LOOPS ========================================================================================== ================================================= PLSQL provides 3 kinds of loops: * Simple or infinite loops. * The FOR loop (numeric or cursor) * The WHILE loop 7. EXCEPTION WHEN record_locked THEN /* Record was already locked. NULL. /* If we get to this line. /* Return the Task Id. I will simply return NULL per declaration default.

2: Structure for PL/SQL loops: Code outside the loop should not have to know about the inner working of the loop. END. END LOOP. 7. DECLARE pipe_status INTEGER.RECEIVE_MESSAGE('execution_trace'). body must contain atleast one executable statement. ranking_level := ranking_level + 1. only then is an EXIT or EXIT WHEN statement executed. Test for termination: takes place inside the body of a loop.2: The Simple Loop: LOOP <executable statement (s)> END LOOP. Loop Termination: When EXIT statement is executed in the body of the loop. you want the loop to execute atleast once. from one to maximum number: PROCEDURE set_all_ranks(max_rank_in IN INTEGER) IS BEGIN FOR ranking_level IN 1 . so that it can respond immediately and display information in the pipe. BEGIN LOOP pipe_status := DBMS_PIPE. If exit statement is not executed.END IF. Use this loop when: not sure how many times the loop has to execute. END. In many situations. END LOOP END.1. 7. FOR Loop: rank for the fixed range of values. Infinite Loop: keeps checking for messages from a particular pipe. then the simple loop becomes an in-finite loop. message_text VARCHAR2. BEGIN WHILE ranking_level<=max_rank_in LOOP set_rank(ranking_level). The WHILE loop: PROCEDURE set_all_ranks(max_rank_in IN INTEGER) IS ranking_level NUMBER (3) := 1.. body of simple loop will always execute atleast once. IF pipe_status = 0 . the number of times a loop must execute varies and so FOR loop cannot be used. max_rank_in LOOP set_rank(ranking_level).

boolean condition. 7. 7. balance_remaining). 7. can also terminate using a EXIT statement. Reason to use this loop: Use when you want to execute a body of code a fixed number of times. ELSE apply_balance(account_id. LOOP balance_remaining := account_balance(account_id). not recommended.2.1: Terminating a Simple Loop: EXIT and EXIT WHEN: EXIT. <highest_number> LOOP <executable_statement(s)> END LOOP.3: The Numeric FOR LOOP: ***************************************************************************************************** ************************************** 15/APR/2008 ***************************************************************************************************** ************************************** FOR <loop_index> IN [REVERSE] <lowest_number> . PLSQL checks the value of the loop index. condition . EXIT WHEN balance_remaining < 1000.THEN DBMS_PIPE. EXIT WHEN condition. END LOOP.2: Emulating a REPEAT UNTIL loop: LOOP . IF balance_remaining < 1000 THEN EXIT. body EXIT WHEN boolean_condition.. END. END IF. balance_remaining). Can use an EXIT statement only with-in a LOOP. DBMS_OUTPUT. END LOOP. . Test for termination: after each execution of the loop body. END LOOP..UNPACK_MESSAGE(message_text). apply_balance(account_id.2. If the lower bound is greater than the upper bound of the range scheme.PUT_LINE(message_text). END IF.. have atleast one statement between LOOP and END LOOP. Loop termination: terminates when the number of times specified in the range scheme has been satisfied. END LOOP. LOOP balance_remaining := account_balance(account_id). the loop never executes it's body.

. PLSQL implicitly declares it as a local variable of datatype INTEGER. * Expressions with-in the range scheme are evaluated once when the loop starts. The range is not re-evaluated during the execution of the loop. 7. 50 LOOP calc_values(loop_index*2).3. make changes within the loop to the variables which you used to determine the FOR loop range. FOR loop_counter IN REVERSE 1 . record_index is declared implicitly by PLSQL with the %ROWTYPE attribute against the cursor specified by cursor_name. 2) = 0 THEN calc_values(loop_index).. * never change values of either loop index or range boundary with-in the loop. FOR loop_index IN 1 . END LOOP..4: The Cursor FOR Loop: FOR record_index IN cursor_name LOOP executable_statements . have to write some cute code. other than one. FOR calc_index IN start_period_number . LEAST(end_period_number. END LOOP.3: Handling Non-Trivial Increments: cannot specify loop index increment. bad practice.. cannot reference outside of the loop.7.3. 10 LOOP executable statements .. Loop Termination: terminates un-conditionally when all of the records in the associated cursor have been fetched. END LOOP.2: Examples of Numeric FOR LOOPS: FOR loop_counter IN 1 ... END LOOP..... those changes will have no effect. 7. current_period) LOOP executable statements .. 7. scope of this index is the loop itself. 10 LOOP executable statements . can . * Use REVERSE keyword to make the FOR loop decrement from upper bound to lower bound. 100 LOOP IF MOD (loop_index.1: Rules for Numeric For Loops: * Don't declare loop index. END LOOP. to obtain increment index. END LOOP. * don't use EXIT statement with-in a FOR loop. or FOR loop_index IN 1 ..3. END IF.

7. 7.5: The WHILE loop: WHILE <condition> LOOP <execution_statements> END LOOP. 7. Test For Termination: Evaluation of boolean condition occurs prior to the first and each subsequent execution of the body. loop executes as long as the condition executes to TRUE. END LOOP. FALSE. . Syntax: <<loop_label>> loop label must appear just before the LOOP statement. 7.5. if the condition results in FALSE or NULL. Termination in between is not recommended. EX: <<all_emps>> FOR emp_rec IN emp_cur LOOP .1: Example of Cursor FOR Loops: DECLARE CURSOR occupancy_cur IS SELECT pet_id. 7. PLSQL performs another fetch..4.pet_id.6.room_number).1: Infinite WHILE loop: WHILE TRUE LOOP <executable_statement(s)> END LOOP. not recommended. BEGIN FOR occupancy_rec IN occupancy_cur LOOP update_bill(occupancy_rec.1: Loop Labels: Can associate a label with a loop and use that label to increase your control over loop execution. If the %NOTFOUND attribute of the cursor evaluates to TRUE. Reason For use: When not sure how many times you must execute loop body. NULL. END. then the loop terminates. Loop Termination: When boolean expression [condition] evaluates to FALSE or NULL. END LOOP.. To conditionally terminate the loop. Reason for use: Use this loop when u want to fetch and process every record in the cursor. 7. condition is boolean. room_number FROM occupancy WHERE occupancy_date = SYSDATE.2: The cursor FOR loop record: cannot reference the loop_record outside the FOR loop.6: Managing Loop Execution: 7.4. Test for termination: after each execution of the loop body. evaluates to TRUE.4. occupancy_rec.terminate with the EXIT statement.3: When to use Cursor FOR loop: Use it to scan through all the items in a set. Don't want to execute the loop atleast one time.

...1: Benefits of Loop Labels: 7. END LOOP month_loop. 12 LOOP IF year_loop. 7.6.. 12 LOOP . . END IF.2: Loop Termination using labels: EXIT loop_label. use dot notation to refer to loop related variables....6 Loop Scope: loop boundary creates a scope similar to that of a PL/SQL block. END LOOP year_loop. loop index always takes precedence over a variable of same name declared outside the scope of the loop.month_number < 6 -. <<year_loop>> WHILE year_number >= 1995 LOOP <<month_loop>> FOR month_number IN 1 .. END LOOP year_loop. Example: <<year_loop>> WHILE year_number >= 1995 LOOP . END LOOP month_loop.Both the Loops are terminated.1. EXIT year_loop WHEN termination_condition -. <<month_loop>> FOR month_number IN 1 . 12 LOOP IF calc_revenue. 7.will retrieve the value 6..1.. EXIT loop_label WHEN condition. BEGIN FOR month_number IN 1 . 12 LOOP . <<year_loop>> WHILE year = 'LEAP_YEAR' LOOP <<month_loop>> FOR month_number IN 1 .6.labels can also appear optionally after the END LOOP keyword.year_number = 1996 THEN . PROCEDURE calc_revenue (year_in IN NUMBER) IS month_number NUMBER (2) := 6. END LOOP month_loop. END LOOP year_loop.. defined outside of the loop scope..

7.3: Avoiding the phony loop: use loops only where necessary. checkout_date FROM occupancy WHERE checkout_date IS NOT NULL.THEN .2: Scope With Labels: <<year_loop>> FOR date_number IN 1994 .pet_id.checkout_date).7. 7. 7. name. -.7.. END LOOP year_loop. COMMIT..2. END. END LOOP.4: PL/SQL loops versus SQL processing. 7. 7. 7.2. number of ways to exit it.2. pet_name. The above process can be done using 2 SQL statements.4: GOTO statement: should not be used inside of a loop..date_number belongs to month_loop and year loop's date_number is qualified by --it's loop label.2: The proper way to say good bye.7. END IF. checkout_date) VALUES (checked_out_rec. END LOOP. checkout_date) SELECT pet_id. THEN .1: Naming loop indexes.6.will use the loop index. BEGIN FOR checked_out_rec IN checked_out_cur LOOP INSERT INTO occupancy_history(pet_id..pet_id. checkout_date .1: Premature FOR loop termination: not advisable. 7.2. pet_name.3: RETURN statement: never use a RETURN statement inside a loop.7. 7.. checked_out_rec. END LOOP.pet_name. 7. END IF. use meaningful and self-documenting loop indexes.7.2. checked_out_rec. END.7. 12 LOOP IF year_loop.2: EXIT and EXIT WHEN statements: Should not be used in FOR and WHILE loops. 1999 LOOP <<month_loop>> FOR date_number IN 1 ..7. Termination: only one way to enter a loop. pet_name. DELETE FROM occupancy WHERE pet_id = checked_out_rec. DECLARE CURSOR checked_out_cur IS SELECT pet_id. Tips for Pl/SQL loops: 7. calc_revenue_for_month(month_number). INSERT INTO occupation_history (pet_id.date_number = 1994 AND date_number = 1 -.

When exception occurs. BEGIN .1: Why Exception Handling: Advantages of exception handling: * Event driven handling of errors.. . * Improved reliability of error handling 8. WHEN INVALID_CURSOR THEN ... Syntax: EXCEPTION WHEN exception_name [or exception_name] THEN <executable statements> END... current PL/SQL block's execution halts and control is transferred to the seperate exception section of your program. Instead.. control is passed to the enclosing block. to handle exceptions.... provides an event driven model for processing errors. * A warning issued by the application to the user. * Clean Processing of error processing blocks. declarations . Use a cursor when we need to modify each record or selected records in a record set using different processes.. Cannot return to that block after you finish handling the exception.FROM occupancy WHERE checkout_date IS NOT NULL. DELETE FROM occupation WHERE checkout_date IS NOT NULL. Example: EXCEPTION WHEN NO_DATA_FOUND THEN . 8.2: The Exception Section: DECLARE . ========================================================================================== ================================================ Chapter 8: Exception Handlers ========================================================================================== ================================================ Errors of any kind are treated as exceptions -.. Exception handling process cleanly seperates error handling process from executable statements..... EXCEPTION .. * An error caused by a user action. execution section .situations that should not occur. Can be one of the problem: * An error generated by the system (such as "out of memory" or "duplicate value in index"). END. exception handlers .

declared in the STANDARD package of PL/SQL. PROGRAM_ERROR: PL/SQL encountered an internal error. TIMEOUT_ON_RESOURCE: Timeout has occurred in RDBMS. In most.3.. END. When it's not present.. FETCH or CLOSE a cursor before it is opened. SQLCODE is a PL/SQL built-in function. TRANSACTION_BACKED_OUT: remote part of a transaction is rolled back.2: Named Programmer Defined Exceptions: . which returns the status code of the last executed statement. Should not declare standard exceptions in our programs. NOT_LOGGED_ON: Program tried to execute a call to the database before it has logged into the db.WHEN payment_overdue THEN . the SQLCODE valud is the same as the oracle error code. referenced an un-initialized row in a PLSQL table. VALUE_ERROR: PL/SQL raises the VALUE_ERROR whenever it encounters an error having to do with the conversion. If the exception is not handled. WHEN OTHERS THEN . we will have declared our own local exception. Exception List: CURSOR_ALREADY_OPEN: Tried to OPEN a cursor that was already OPEN. SQLCODE returns 0 if the last statement executed without any errors. EXCEPTION WHEN CURSOR_ALREADY_OPEN THEN CLOSE my_cursor. You read past the EOF using the UTL_FILE package. SELECT INTO statement can return only one row. then the user is presented directly with the error code and description. 8. LOGIN_DENIED: Program tried to login to Oracle RDBMS using invalid username/password combination.. WHEN OTHERS is optional. INVALID_NUMBER: PL/SQL executes an SQL statement which cannot convert a character string successfully into a number. It will not be raised when an internal error with that name occurs. Must CLOSE cursor before trying to OPEN or ReOPEN it. INVALID_CURSOR: made a reference to a cursor that did not exist. STORAGE_ERROR: Program ran out of memory or somehow memory got corrupted. TOO_MANY_ROWS: A SELECT INTO statement returned more than one row. or invalid constraining of numeric or character data. 8. END..3. ZERO_DIVIDE: Your program tried to divide by zero. either with an explicit ROLLBACK command or as the result of some other action. but not all cases. NO_DATA_FOUND: Execute a SELECT_INTO statement that returned no rows. while waiting for resource. the exception is immediately raised to the enclosing block. DUP_VAL_ON_INDEX: INSERT or UPDATE statement tried to store duplicate values in a column or columns in a row which is restricted by a unique index. If we declared.3: Types of exceptions: Four kinds of exceptions in SQL: 8.1: Named System Exceptions: Exceptions that have been given names in PL/SQL and raised as a result of an error in PL/SQL or RDBMS processing. truncation.

WHEN negative_balance THEN . duplicate_company BEGIN ...company_id%TYPE) IS invalid_company_id EXCEPTION.Exceptions that are raised as a result of errors in ur application code.3.. RAISE no_sales_for_company. 8. negative_balance EXCEPTION.. WHEN no_sales_for_company THEN . DECLARE exception_name EXCEPTION..3. no_sales_for_company EXCEPTION.3.. EXCEPTION_INIT(exception_name.. EXCEPTION_INIT instructs the compiler to associate or initialize a programmer defined exception with an oracle error code. 8. after declaration. END. PROCEDURE calc_annual_sales (company_id_in IN company.1: The EXCEPTION_INIT pragma: use PRAGMA to associate a name with an internal code. EXCEPTION_INIT must appear in the declaration section of a block. You give these exceptions names by declaring them in the declaration section. error_code_literal). .. PL/SQL exception handling model does not make any distinction between internal errors and application specific errors. Only the most common errors are named. You then raise the exceptions explicitly in the program. the rest have numbers and can be assigned names using the PRAGMA EXCEPTION_INIT syntax. the exception name is used in the PRAGMA.. EXCEPTION WHEN invalid_company_id THEN . Syntax for declaring custom exceptions: exception_name EXCEPTION.3: Un-named System Exceptions: Exceptions that are raised as a result of an error in PL/SQL or RDBMS processing.... assign your own name to the Oracle or PL/SQL error raised in your program and then handle that error. RAISE negative_balance. BEGIN .. . RAISE invalid_company_id. but have not been given names by PL/SQL.. .

8. If they are assigned a name. PROCEDURE delete_company (company_id_in IN NUMBER) IS child_records_exist EXCEPTION EXCEPTION_INIT (child_records_exist. 8..PUT_LINE ('Delete Child records first!'). the scope of that name is the same as that of named programmer defined exceptions. error_msg_in IN VARCHAR2). including the minus. programmer provides both the error number (between -20000 and -20999) and an error message and raises the exception using RAISE_APPLICATION_ERROR.. -2292). which it is most of the time. Named System Exceptions: Exceptions are globally available because they are not declared in or confined to any particular block of code. Here..1: Scope of a programmer-defined exception: PROCEDURE check_account(company_id_in IN NUMBER) IS overdue_balance EXCEPTION.4: Determining Exception Handling Behaviour: 8. EXCEPTION WHEN child_records_exist THEN DBMS_OUTPUT. BEGIN .4.. END. each RDBMS error has a number associated with it. IF . PROCEDURE RAISE_APPLICATION_ERROR (error_number_in IN NUMBER. Named Programmer Defined Exceptions: can only be raised in the execution and exception sections of the block in which they are declared(and all nested blocks).. Scope of an Exception: Portion of code which is "covered" by that exception.. 8. Unnamed programmer defined exceptions: only defined in the clause to RAISE_APPLICATION_ERROR and then is passed to the calling program. executable statements . Unnamed system exceptions: can be handled in any PLSQL block using the WHEN OTHERS section... BEGIN DELETE FROM company WHERE company_id = company_id_in.4: Un-named programmer defined exceptions: Exceptions that are defined and raised in the server by the programmer. THEN RAISE overdue_balance END IF. That error along with that message is passed back to the client side application. can raise and handle a named system exception in any block. . * System Exceptions (both named and un-named) are raised by PL/SQL when ever a program violates a rule in RDBMS (such as "duplicate value in index") or causes a resource limit to be exceeded (ex: "maximum open cursors exceeded"). if the error code is literal. LOOP .error_code_literal: oracle error code.

RAISE overdue_balance.. BEGIN .. 8. 8.2: Raising exceptions in nested blocks: PROCEDURE check_account(company_id_in IN NUMBER) IS overdue_balance EXCEPTION..4. EXCEPTION WHEN overdue_balance THEN .. EXCEPTION WHEN STANDARD. DECLARE no_data_found EXCEPTION.. executable statements .. IF .4..... END. does not trap standard PLSQL generated [query that returns no rows]. BEGIN BEGIN .. THEN .. THEN RAISE overdue_balance END IF. LOOP . EXCEPTION WHEN no_data_found -.3: Overlapping exception names: locally declared exception will take precedence over system exception of the same name. .. END. notation distinguishes between the two and finds handler for the pre-defined exception... 8.locally declared exception.END LOOP. END. DECLARE no_data_found EXCEPTION.. EXCEPTION WHEN overdue_balance THEN ...1.. END LOOP.4: Qualifying exception names: Using the locally defined exception and the standard exception of the same name at the same time. END...1.1. executable statements . use dot notation on the predefined exception..NO_DATA_FOUND THEN .. . BEGIN .4..

.5: Referencing duplicate exceptions in nested blocks: Cannot declare a the same exception more than once in the same block.2: Propogation of an exception: When no exception handler found. raise procedure level overdue_balance exception. . END.WHEN NO_DATA_FOUND THEN . Both exceptions will be completely different.4. BEGIN .. but the exception in the nested block is not recognized in the main block [even though the names are the same]. Ways To prevent this: 1... "unhandled user-defined exception". PROCEDURE check_account (company_id_in IN NUMBER) IS overdue_balance EXCEPTION. BEGIN . Hence error is generated.1.NO_DATA_FOUND THEN .overdue_balance. PLSQL then attempts to handle the exception by raising that exception once more in the enclosed block. END. use dot notation to qualify exception names. DECLARE overdue_balance EXCEPTION. make sure that locally raised overdue_balance exception is handled by the WHEN OTHERS exception in the enclosing block. Since the nested block has it's own declaration section and has an exception declared. END. Ultimately. RAISE overdue_exception. the flow first searches for an exception handler in the nested block. RAISE overdue_exception. but can declare the same exception in nested blocks. 2. not finding one. RAISE check_account.... declaration local to the nested block takes precedence over the global exception. or EXCEPTION WHEN NO_DATA_FOUND OR STANDARD.. then it's an un-handled exception. flow goes to the enclosing block. END.... but gives a run-time error. Above Procedure compiles. with in subblock. PLSQL propogates the exception into enclosing block of that current block.. . continues to do so until there are no more enclosing blocks. if no handler is found. EXCEPTION WHEN overdue_exception THEN . 8.4. 8.

5. BEGIN SELECT SUM (sales) INTO cust_sales FROM invoice WHERE customer_id = 1001.PUT_LINE('Customer 1001 accounts for more than half of all sales!'). . DECLARE total_sales NUMBER := 0. PL/SQL raises ZERO_DIVIDE exception: DECLARE total_sales NUMBER := 0.1. END IF. EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT. IF cust_sales/total_sales > . cust_sales NUMBER..1.PUT_LINE('Divide by zero occurred'). EXCEPTION .5 Raising an Exception: ***************************************************************************************************** ************************************* 16/APR/2008 ***************************************************************************************************** ************************************* Four ways of raising exception: * Programmer raised a unnamed programmer defined exception: raised using RAISE_APPLICATION_ERROR 8. BEGIN SELECT SUM (sales) INTO cust_sales FROM invoice WHERE customer_id = 1001.8.5. sales_dominaiton EXCEPTION.5 THEN RAISE sales_domination. ELSIF cust_sales/total_sales > . use exception handler to handle these. 8. WHEN sales_domination THEN DBMS_OUTPUT. cust_sales NUMBER := 0. sales_domination EXCEPTION.5 THEN RAISE sales_domination.2: Programmer raised a named exception: explicit call to RAISE statement: Programmer raises ZERO_DIVIDE exception.1: PL/SQL runtime engine raised a named system exception: auto raised by PLSQL. END. END IF.. IF total_sales = 0 THEN RAISE ZERO_DIVIDE.

EXCEPTION WHEN NO_DATA_FOUND THEN RAISE skip_sub_block. 8.5. never in the current block. BEGIN .3: Impact of un-handled exceptions: If the program contains IN OUT or OUT params.1. Any changes made to those params during program execution are rolled back. .END.2: Re-raising an exception: Programmer re-raises the current exception. Can raise another exception from with-in an exception handler.5.block 2 skip_sub-block EXCEPTION. then control is passed to the enclosing block. then PL/SQL engine does not assign values to those params. RAISE. Have to issue an explicit ROLLBACK statement to do this.. -. . END.. 8. END. Using exceptions raised inside the exception block: DECLARE -. EXCEPTION WHEN OTHERS THEN send_error_to_pipe (SQLCODE).This statement re-raises the exception most recently occurred. RAISE.. EXCEPTION END. * Runtime engine does not ROLLBACK any changes made to the db during program execution.. 8. this exception can only be handled in the exception section of the enclosing block.4: Exception raised in an exception handler.. Not to the exception section of the current PL/SQL block.5.block 3 BEGIN . 8. EXCEPTION WHEN skip_sub_block THEN NULL.5.block 1 BEGIN DECLARE -. DECLARE -.. if any.3: Exceptions raised in a declaration: When an exception is raised in the declaration section of a PL/SQL block. END.

. WHEN OTHERS clause must be the last exception handler in the exception section.6. since only one exception can be raised at a time. the execution section is terminated.. 8. END.2: Unhandled Exceptions: Best way to handle un-handled exceptions: Make sure that the outermost PL/SQL block contains a WHEN OTHERS clause in the exception section. BEGIN UPDATE company SET .. END..4: Continuing Past Exceptions: PROCEDURE change_data IS BEGIN BEGIN DELETE FROM employees WHERE .6. EXCEPTION .6: Handling Exceptions: Once an exception is raised. PROCEDURE delete_company (company_id_in IN NUMBER) IS BEGIN DELETE FROM company WHERE company_id = company_id_in. END delete_company. EXCEPTION WHEN OTHERS THEN DECLARE error_code := SQLCODE.... EXCEPTION WHEN OTHERS THEN NULL.3: Using SQLCODE and SQLERRM in WHEN OTHERS clause: SQLCODE: returns ERROR code.. 8. ELSIF error_code = -2291 THEN . 8. Cannot return to that body of the code.. BEGIN IF error_code = -2292 THEN ..8. END IF. SQLERRM: returns ERROR message.1: Combining Multiple Exceptions in a single handler: WHEN balance_too_low OR ZERO_DIVIDE THEN Cannot use AND. ELSE .6. 8..6. error_message := SQLERRM..

1: Using RAISE_APPLICATION_ERROR: PROCEDURE RAISE_APPLICATION_ERROR (error_number IN NUMBER. END. END IF. 'Sample Exception')..2: RAISE_APPLICATION_ERROR in a database trigger: CREATE OR REPLACE TRIGGER minimum_age_check BEFORE INSERT ON employee FOR EACH ROW BEGIN IF ADD_MONTHS(:month_val.WHEN OTHERS THEN NULL.. PRAGMA EXCEPTION_INIT(no_babies_allowed. it's as though an exception has been raised with the RAISE statement. 18*12) > SYSDATE THEN RAISE_APPLICATION_ERROR (-20001. END. The builtin returns a programmer defined error number and message back to the client component of the application. When calling RAISE_APPLICATION_ERROR..7.. Error message cannot be more than 2K bytes in length. will have to issue an explicit ROLLBACK to counter this. Changes made to db will not be ROLLBACK. if it's longer the procedure will simply truncate anything beyond 2K. END. On Client Side: DECLARE no_babies_allowed EXCEPTION. . Execution of the PLSQL block halts immediately and any changes made to OUT or IN OUT arguments (if present) will be reversed. error_msg IN VARCHAR2) Example: RAISE_APPLICATION(-20001. Error number you specify must be between -20000 and -20999. EXCEPTION WHEN no_babies_allowed THEN DBMS_OUTPUT. END... BEGIN INSERT INTO company_history . Can then use EXCEPTION_INIT pragma and exception handlers to handle the error in graceful and user-friendly manner. BEGIN INSERT INTO employee .PUT_LINE(SQLERRM).7. 8. 8. -20001).7: Client Server Communication: RAISE_APPLICATION_ERROR feature provided by Oracle to communicate application specific errors from the server side (usu a database trigger) to the client side application. 8. EXCEPTION WHEN OTHERS THEN NULL. 'Atleast 18 Years Age!!!'). END.

').').9: Exception Handler as an IF statement: FUNCTION convert_date (value_in IN VARCHAR2) RETURN DATE IS DECLARE return_value DATE. FUNCTION company_name (id_in IN NUMBER. NO_DATA_FOUND could be raised in both scopes. WHEN NO_DATA_FOUND THEN DBMS_OUTPUT. access_type_in IN VARCHAR2) RETURN VARCHAR2 IS return_value VARCHAR2(60). Attempt to read past the end of an operating system file. END. we query from an implicit table and also make references to a PL/SQL table's row. EXCEPTION WHEN OTHERS . ELSIF access_type_in = 'MEMORY' THEN RETURN company_name_table(id_in).PUT_LINE('Unable to locate company in Database. 8. EXCEPTION WHEN NO_DATA_FOUND THEN RAISE bad_data_in_select. bad_data_in_select EXCEPTION.PUT_LINE('Unable to locate company in memorized table. Overlapping use of the same exception could cause some confusion. 3. how to know which one raised it? Use nested PLSQL blocks to resolve this issue. An Implicit Query returns no data. 'MM/DD/YY'). END IF. BEGIN IF access_type_in = 'DBMS' THEN BEGIN SELECT name INTO return_value FROM company WHERE company_id = id_in. BEGIN IF value_in IS NULL THEN return_value := NULL. END. 2. Suppose in a single PL/SQL block. ELSE return_value := TO_DATE(value_in. RETURN return_value. Attempt to reference a row in PLSQL table which has not yet been defined. EXCEPTION WHEN bad_data_in_select THEN DBMS_OUTPUT.8.8: NO_DATA_FOUND Multi-purpose exception: NO_DATA_FOUND raised under 3 circumstances: 1.

you establish a relationship between all those different attributes and give that relationship a name by defining that record. you abstract all the different attributes or fields of the subject of that record. Each field is defined explicitly (its name and datatype) in the TYPE statement of that record. each with it's own value. 'MM/YY'). Each field corresponds to a column or expression in the cursor's select statement.and has the same name as -.3: Benefits of using records: *** Data abstraction: Instead of working with individual attributes of an entity or an object. record as a whole does not have a value. *** Leaner / Cleaner code. 'DD-MON-YY'). <record_name>.1 Record Basics: 9. END. Cursor Based: A record based on a cursor's select statement. *** Aggregate operations: can perform operations which apply to all columns in a record. think of and manipulate that entity as a "thing in itself".2: Accessing record based data: Using the dot notation. instead each component or field has a value. . END. ========================================================================================== ================================================ Chapter 09: Records in PL/SQL ========================================================================================== ================================================ ***************************************************************************************************** ************************************* 24/JUL/2008 ***************************************************************************************************** ************************************* Records very similar in concept and structure to the rows of a database tables.1. RETURN return_value.1.1: Different types of Records: Table Based: A record based on a table's column structure.emp_full_name 9. 9.a column in a table. Composite data structure: composed of more than one element.THEN BEGIN return_value := TO_DATE(value_in. EXCEPTION BEGIN return_value := TO_DATE(value_in. Each field corresponds to -. EXCEPTION WHEN OTHERS THEN return_value = NULL.10: Raise Nothing But Exceptions. END IF. 8. 9. When you create a record. a field in a programmer based record can also be another record. END.1.<field_name> Ex: employee_rec. Programmer Defined: A record whose structure the programmer gets to define with a declaration statement.

. <record_name> <table_name>%ROWTYPE DECLARE comp_name company.6: Comparing 2 records: To compare records. . Overwriting: DECLARE CURSOR company_cur IS . rather than individual variables...4: Guidelines for using records: ** Create corresponding cursors and records: Whenever we create a cursor. Comparison: IF old_company_rec. EXCEPTION WHEN OTHERS THEN NULL.5: Referencing a Record and it's fields: 9... FETCH company_cur INTO company_rec.. BEGIN OPEN company_cur. BEGIN SELECT * FROM company INTO comp_rec WHERE company_id = 1024.2: Table Based Records: 9.. = new_company_rec. This can use up memory and consume un-necessary CPU cycles. ** Pass records as parameters.start_date = new_company_rec. 9. when AND old_company_rec.2.1. END. END IF. it's field values are saved by the PLSQL program in case of the need for a rollback.1. ** Create table based records: using %ROWTYPE attribute.1: Declaring records with the %ROWTYPE attribute.address1 THEN .name%TYPE. Always fetch into that record.1.9. instead of individual variables. also create the corresponding record [except for cursor FOR loops].. must always do so through comparison of record's individual fields. Drawback: If a record is passed as an OUT or IN OUT parameter. company_rec company_cur%ROWTYPE. comp_rec company%ROWTYPE.3 Cursor Based Records . company_rec.start_date AND old_company_rec.address1 = new_company_rec. 9. No such thing as a "read-only" PLSQL record = 'New Name'. END.

3. publicize_loss(high_losses_rec. sales s WHERE DECLARE CURSOR comp_summary_cur IS SELECT C. 9. 9. SUM(order_amount) tot_sales FROM company WHERE company_id = id_in. END. LOOP FETCH high_losses_cur INTO high_losses_rec. BEGIN OPEN high_losses_cur.company_id = s. 'YYYY') = '1994'). EXCEPTION WHEN OTHERS THEN NULL. CURSOR comp_performance_cur(id_in IN NUMBER) IS SELECT name.. each field in the record corresponds to and has the same name as the column or aliased expression in the cursor's query.3. c. EXIT WHEN high_losses_cur%NOTFOUND.A record whose structure is drawn from the select list of a cursor. BEGIN . END. high_losses_rec high_losses_cur%ROWTYPE.. .2: Setting the Record's Column Names: A cursor's query can also include calculated values or FROM company c. species_lost above_avg_loss FROM rain_forest_history WHERE species_lost > (SELECT AVG(species_lost) FROM rain_forest_history WHERE TO_CHAR(analysis_date. project_further_damage(high_losses_rec.1: Choosing columns for a cursor record.dying_country_cd). if we want to access that through a record.shrinking_plot). we must provide an alias for that calculated value. size_in_acres shrinking_plot.company_id. DECLARE CURSOR high_losses_cur IS SELECT country_code dying_country_cd.company_id comp_summary_rec comp_summary_cur%ROWTYPE. <record_name> <cursor_name>%ROWTYPE. END LOOP. Here. CLOSE high_losses_cur. c.

] TYPE company_rectype IS RECORD (comp# company. .company_id%TYPE. customer_name ). it is only needed for table and cursor records. No %ROWTYPE here.4. top_customer_rec customer_sales_rectype. name company. TYPE customer_sales_rectype IS RECORD (customer_id NUMBER(5). 9.. * Can also specify default values using DEFAULT keyword or := in record definition. total_sales NUMBER(15. 9.1: Declaring Programmer defined Record TYPEs: Syntax: TYPE <type_name> IS RECORD (<field_name1> <datatype1>.4.. prev_customer_sales_rec customer_sales_rectype. . Use this record TYPE as the basis for declarations for your own actual records having that structure. IF comp_performance_rec. can declare a field to be a cursor variable.2: Declaring the Record: <record_name> <record_type>.3.4: Programmer-Defined Records: Perform 2 steps to declare programmer defined records: ***************************************************************************************************** ************************************* 14/AUG/2008 ***************************************************************************************************** ************************************* 1. Datatype can be any of the following: Pre-defined datatype (VARCHAR2.2) ). <field_name2> <datatype2>.name%TYPE.. NUMBER etc) Programmer defined subtype Declarations using %TYPE attribute Declarations using %ROWTYPE attribute PLSQL record type PLSQL table type cannot declare a field to be an exception or a cursor [with PLSQL 2.tot_sales > 10000 THEN .. 2.comp_performance_rec comp_performance_cur%ROWTYPE. <field_namen> <datatypen> ). 9. Declare or define a record TYPE containing the structure you want in your record.

country_code := 1005.country_code. .employee_id INDEX BY BINARY_INTEGER. rain_forest_rec.* Can apply constraints like NOT NULL (here we also need to assign a default value). new_hires_tab employee_ids_tabletype ).name%TYPE. TYPE employee_ids_tabletype IS TABLE OF employee. CURSOR company_sales_cur IS SELECT name. report_rec. rain_forest_rec. SUM(order_amount) total_sales FROM company c. order o WHERE c.. paragraph_text long_line_type.company_id. company_nm company. company_name = o.species_lost := 425. DECLARE rain_forest_rec rain_forest_history%ROWTYPE. total_sales company_sales_cur. rain_forest_rec.total_sales := 0. 9.. TYPE mish_mash_rectype IS RECORD (emp_number NUMBER(10) NOT NULL := 100.analysis_date.size_in_acres := 32. 9. rain_forest_rec. INSERT INTO rain_forest_history VALUES ( rain_forest_rec. TYPE company_rectype IS RECORD (company_id company.total_sales%TYPE := 0. prefers_nonsmoking_fl BOOLEAN := FALSE.size_in_acres.report_id). * Each field name within a record must be unique.5: Assigning Values To and From Records: * Direct Field Assignment using assignment operator: top_customer_rec. new_company_rec company_rectype ). END.3: Examples of programmer defined record declarations: SUBTYPE long_line_type IS VARCHAR2.. rain_forest_rec.species_lost ).analysis_date := SYSDATE. new_hires_tab employee_ids_tabletype.4. * SELECT INTO from an implicit cursor: .output_generated := check_report_status(report_rec. rain_forest_rec. BEGIN rain_forest_rec.company_id%TYPE.

customer_name. * Aggregate assignment: DECLARE prev_rain_forest_rec rain_forest_history%ROWTYPE. SUM(sales) tot_sales FROM customer WHERE sold_on BETWEEN < ADD_MONTHS(SYSDATE...DECLARE TYPE customer_sales_rectype IS RECORD (customer_id NUMBER(5). -3). .customer_id. -3). top_customer_rec.. top_customer_rec...2) ).. * FETCH INTO from an explicit cursor: DECLARE CURSOR cust_sales_cur IS SELECT customer_id. SELECT cust_sales_rec. curr_rain_forest_rec rain_forest_history%ROWTYPE. . cursor-based and programmer defined. SUM(sales) INTO top_customer_rec. name. BEGIN SELECT customer_id. name. . curr_rain_forest_rec := prev_rain_forest_rec. BEGIN OPEN cust_sales_cur.. name. total_sales NUMBER(15.total_sales FROM customer WHERE sold_on BETWEEN < ADD_MONTHS(SYSDATE. type and structure. FETCH cust_sales_cur INTO cust_sales_rec.customer_id.tot_sales. /* or */ OPEN cust_sales_cur. . FETCH cust_sales_cur INTO cust_sales_rec.. -3). top_customer_rec customer_sales_rectype. cust_sales_rec.. 9. SUM (sales) INTO top_customer_rec FROM customer WHERE sold_on BETWEEN < ADD_MONTHS(SYSDATE. BEGIN . cust_sales_rec cust_sales_cur%ROWTYPE. customer_name customer_name%TYPE. Record defined by it's name. Two records can have the same structure but be of a different type..6: Record Types and Record Compatibility: Three types of records: Table-based.

Table structure against which all different records will be declared: CREATE TABLE cust_sales_roundup (customer_id NUMBER (5). cust_sales_rec cust_sales_cur%ROWTYPE. customer_name VARCHAR2(100). Above records have the same structure but each is of a different type. top_customer_rec. the following rules must hold: * Both cursor based records in an aggregate assignment must be based on the same cursor. 2) ). Records of different types are incompatible with each other at the record level. cust_sales_roundup_rec := top_customer_rec. * Both table based records in an aggregate assignment must be based on the same table. ** Setting records to NULL: . /*Incompatible*/ Even when records in an aggregate assignment are of the same type and structure.total_sales = v_total_sales.customer_name = v_customer_name. top_customer_rec. * cursor based record: CURSOR cust_sales_cur IS SELECT * FROM cust_sales_roundup. ** Records of the same type: can perform aggregate assignments only between records of the same type and the same source.. top_customer_rec. total_sales NUMBER (15. programmer defined record: TYPE customer_sales_rectype IS RECORD (customer_id NUMBER (5). * manual record: collection of individual variables which the programmer can treat as a group by always making changes to and referring to all variables together.6.. 9. /*Incompatible*/ cust_sales_rec := cust_sales_roundup_rec. Must execute a seperate assignment for each field in the record. 2) ). customer_name customer_name%TYPE. RECORD statement. v_total_sales NUMBER (15. v_customer_name customer_name%TYPE.customer_id = v_customer_id. 2).1: Assignment Restrictions: ** Manual Records: Cannot assign a manual record to a real record of any type and vice versa. total_sales NUMBER (15. * Both programmer defined records in an aggregate assignment must be based on the same TYPE . * table based record: cust_sales_roundup_rec cust_sales_roundup%ROWTYPE. v_customer_id NUMBER (5).

BEGIN . extension VARCHAR2(4) ). day_phn_number VARCHAR2(4). 9. . END. DECLARE TYPE first_rectype IS RECORD (var1 VARCHAR2(100)). TYPE second_rectype IS RECORD (nested_rec first_rectype := first_rec). area_code VARCHAR2(3). phn_number VARCHAR2(4). TYPE contact_set_rectype IS RECORD (day_phone# phone_rectype. The record that contains the nested record as a field is called the enclosing record. eve_exchange VARCHAR2(3). END. Initialize this new record with the previously defined record. will set each of the fields in the record to default value of NULL. eve_phone# phone_rectype.. ** Example of a nested record: DECLARE TYPE phone_rectype IS RECORD (intl_prefix VARCHAR2(2). * Create a new record type and record. cell_phone# phone_rectype ). fax_phone# phone_rectype. .. then create a second record type using the first record as it's single column. first_rec first_rectype. This is called a nested record.assignment of NULL to a record is allowed.7: NESTED Records: Can include a record as a field within another record. day_exchange VARCHAR2(3).. exchange VARCHAR2(3). eve_intl_prefix VARCHAR2(2). day_extension VARCHAR2(4). without nested records: DECLARE TYPE phone_rectype IS RECORD (day_intl_prefix VARCHAR2(2). BEGIN .. ** Record Initialization: * Can initialize a table or cursor based record at the time of declaration only with another record of same type and source... day_area_code VARCHAR2(3). PROCEDURE create_companies(prev_company_rec IN company%ROWTYPE) IS curr_company_rec company%ROWTYPE := prev_company_rec. eve_phn_number VARCHAR2(4). eve_area_code VARCHAR2(3).

cell_phn_number VARCHAR2(4). cell_extension VARCHAR2(4) ). in_an_emergency_rec := auth_rep_info_rec [Incompatible] ** DeNormalizing program data in nested records: DECLARE .eve_extension VARCHAR2(4).home_phone#.day_phone#. TYPE level2_rectype IS RECORD (paragraph level3_rectype). fax_extension VARCHAR2(4). Fully Qualified name for the textline variable: level1_rec. in_an_emergency_rec emerg_contacts_rectype. BEGIN in_an_emergency_rec.area_code := auth_rep_info_rec. cell_exchange VARCHAR2(3). TYPE level1_rectype IS RECORD (story level2_rectype). phone_rectype. level1_rec level1_rectype.story. auth_rep_info_rec contact_set_rectype. * Unnested records are difficula to maintain. possible becuz both the fax_phone# and home_phone# have the same record type. cell_intl_prefix VARCHAR2(2). fax_phone# phone_rectype.emerg1_phone# := auth_rep_info_rec. * Unnested records are difficult to read. BEGIN .home_phone#. ** Aggregate Assignments of nested records: auth_rep_info_rec. cell_phone# phone_rectype ). END.paragraph. fax_area_code VARCHAR2(3). TYPE emerg_contacts_rectype IS RECORD (emerg1_phone# phone_rectype. fax_phn_number VARCHAR2(4).. ** Dot Notation with records: DECLARE TYPE level3_rectype IS RECORD (textline VARCHAR2(1000)).area_code. auth_rep_info_rec contact_set_rectype. eve_phone# phone_rectype.. emerg2_phone# phone_rectype ). cell_area_code VARCHAR2(3). fax_exchange VARCHAR2(3).textline auth_rep_info_rec.fax_phone#. DECLARE TYPE contact_set_rectype IS RECORD (day_phone# phone_rectype.fax_phone# := auth_rep_info_rec. fax_intl_prefix VARCHAR2(2).

TYPE comp_address_rectype IS RECORD (addr1 company. cr_address_rec comp_address_rectype ). addr4 company. * Nested Records hide data complexity: PROCEDURE display_company(company_rec_in IN company_rectype) above is better than PROCEDURE display_company (company_rec_in IN company_rectype. ap_address_rec comp_address_rectype. **PL/SQL Tables . ship_address_rec PLSQL Tables and Other Collections: PROCEDURE IS TYPE string_tabletype IS TABLE OF VARCHAR2(30) INDEX BY BINARY_INTEGER. addr2 company. addr3 company.state_abbrev%TYPE. zipcode company.addr3%TYPE. recv_address_rec comp_address_rectype. company_name_table string_tabletype.zipcode%TYPE ).addr2%TYPE.addr1%TYPE. i still have to pass it as a paramter for the procedure to compile. BEGIN company_name_table (row_in) := name_in. state TYPE company_rectype IS RECORD (comp_id company. ship_address_in IN comp_address_rectype.company_id%TYPE. EXCEPTION WHEN OTHERS THEN NULL. * PLSQL table: one type of collection structure.addr4%TYPE. cr_address_in IN comp_address_rectype) here even if the company doesn't have an address. ========================================================================================== ================================================ Chapter 10: PLSQL Tables: ========================================================================================== ================================================ 10. comp_name company. city company. ap_address_in IN comp_address_rectype. recv_address_in IN comp_address_rectype. END.

* Reside in the private PLSQL area of the Oracle Server database instance, not available to client side structures at this time. cannot declare and manipulate PL/SQL tables in oracle developer/2000 environment. * Can build stored procedures and packages which use PLSQL tables. can call this stored procedures from oracle developer / 2000 env. **Nested Tables and VARRAYs: * can be used as datatypes of fields in conventional tables and attributes of objects. 10.2: Characteristics of PL/SQL tables: Definition of PLSQL table: One-dimensional, unbounded, sparse collection of homogeneous elements, indexed by integers. * One dimensional: PLSQL table can have only one column. * Unbounded or Unconstrained: No predefined limit to the number of rows in a PL/SQL table, grows dynamically as we add more rows to the table. No rows are allocated for this structure, when it is defined. * Sparse: In a PLSQL table, a row exists in the table only when a value is assigned to that row. Rows do not have to be assigned sequentially. Row 15 can have a value of 'Fox' and Row 15446 can have a value of 'Eagle'. * Homogeneous elements: Because a plsql table contains a single column, all rows must contain values of the same datatype. Can have a plsql table of records. All rows will have the same set of columns. * Indexed by integers: Indexed by BINARY_INTEGER. Range of Binary Integer: -2pow31 -1 to 2pow31 -1. Since row number does not have to be used sequentially and has an enormous range, can use this integer index in interesting ways. eg: row number of PLSQL table can be the primary key of the table. 10.3: PLSQL tables and DML statements. ** Tables are PLSQL constructs and are not a part of the SQL language and standards. * No concept of transaction integrity with PL/SQL tables. cannot commit information or roll back changes from the table. * Cannot select from a PLSQL table. Instead, we use loops to move through the table row by row. * Cannot issue DML statements like INSERT, UPDATE or DELETE. [PLSQL 2.3 offers a delete operator.] 10.4: Declaring a PLSQL table: 2 steps: * Define a particular PLSQL table structure, using the table TYPE statement. Result of this statement is a datatype which can be used in declaration statements. * Declare the actual table based on that table type. ** Defining the table TYPE: TYPE <table_type_name> IS TABLE OF <datatype> [NOT NULL] INDEX BY BINARY_INTEGER; BINARY_INTEGER allows the fastest retrieval of data. Rules for table type name are the same as any PLSQL identifier. 30 chars length, must start with a letter, can include special characters such as _ or $. Datatype can be any of the following: 1. Scalar datatype or 2. Anchored datatype. 3. Records [For PLSQL 2.3]. ** Declaring the PLSQL table: <table_name> <table_type> PACKAGE company_pkg IS TYPE primary_keys_tabtype IS TABLE OF NUMBER NOT NULL INDEX BY BINARY_INTEGER;

company_keys_tab primary_keys_tabtype; emp_keys_tab primaryh_keys_tab_type; ... END company_pkg; 10.5: Referencing and modifying PL/SQL table rows: Row in a PL/SQL table is a scalar value. Reference using following syntax: <table_name> (<primary_key_value>) primary_key_value is compatible with the BINARY_INTEGER data type. Assign values to a row using ':='. company_name_tab (15) := 'MSIT OU'; company_keys_tab (-2000) := new_company_id; header_string := 'Sales Of' || company_name_tab (15); ** Automatic conversions of row number expressions: * Store the string in row 16: requests_table(15.556) := 'Totals By Category'; * Store the string in row 255: company_keys_table('25'||'5') := 1000; * Expression will be evaluated and used as a row number: key_wordlist_table (last_row + 15) := 'Bow Wow'; ** Referencing an undefined row: A row in a PLSQL table does not exist until you assign a value to that row. PLSQL does not create the memory structure for the rows in the table until we need them. Whenever we assign a value to a row in the PLSQL table, PLSQL creates a row. PLSQL raises the NO_DATA_FOUND exception if we reference a row which does not exist. NO_DATA_FOUND is also raised when an implicit CURSOR SELECT statement does not return any rows or when you attempt to read past the end of the file with the UTL_FILE package. DECLARE new_request NUMBER; TYPE request_tabtype IS TABLE OF NUMBER INDEX BY BINARY_INTEGER; request_table request_tabtype; BEGIN new_request := request_table(15); EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END; ** Nonsequential use of PLSQL table: Can place a value in any row of the table you wish, regardless of the primary key value of the last row you filled. DECLARE TYPE test_tabtype IS TABLE OF VARCHAR2(20) INDEX BY BINARY_INTEGER; test_table test_tabtype; BEGIN test_table (1) := 'value1';

test_table (20) := 'value2'; table_table (255) := 'value3'; END; ** Passing PLSQL tables as parameters: In a single call, pass all the values in a table to the module. The send_promos procedure sends a promotional mailing to all the companies in my table. The company_overdues procedure returns a table with the names of the companies which have overdue bills. PACKAGE company_pkg IS TYPE primary_keys_tabtype IS TABLE OF company.company_id%TYPE NOT NULL INDEX BY BINARY_INTEGER; company_keys_tab primary_keys_tabtype; emp_keys_tab primary_keys_tabtype; TYPE company_names_tabtype IS TABLE OF INDEX BY BINARY_INTEGER; company_names_tab company_names_tabtype; PROCEDURE send_promos (company_table_in IN primary_keys_tabtype); FUNCTION companies_overdue (overdue_date_in IN DATE) RETURN company_names_tabtype; FUNCTION id(name IN RETURN company.company_id%TYPE; END company_pkg; * you must declare a PLSQL table based on the type before you can use any of the programs. CREATE OR REPLACE PROCEDURE send_promos_for_overdue_companies (date_in IN DATE := SYSDATE) IS v_row PLS_INTEGER; cnames company_pkg.company_name_tabtype; BEGIN cnames := company_pkg.companies_overdue(date_in); v_row := cnames.FIRST; LOOP EXIT WHEN v_row IS NULL; DBMS_OUTPUT.PUT_LINE(cnames(v_row)); v_row := cnames.NEXT (v_row); END LOOP; EXCEPTION WHEN OTHERS THEN NULL; END; Ex02: DECLARE v_row PLS_INTEGER; company_ids company_pkg.primary_keys_tabtype; date_in := SYSDATE; BEGIN

send_promos(company_ids). nth_day := nth_day + 1.FIRST.6 Filling Rows of a PLSQL table: * Direct Assignment Eg: countdown_test_list (43) := 'Internal Pressure'. nth_day BINARY_INTEGER := 1. company_names_table (last_name_row) := 'Johnstone Clingers'. 'DY') NOT IN ('SAT'. new_names name_tabtype.DELETE. company_ids(NVL(company_ids. BEGIN WHILE nth_day < ndays_in LOOP IF TO_CHAR(v_date. v_row := company_pkg. END LOOP. 0) +1) := company_pkg. * Aggregate: Eg: DECLARE TYPE name_tabtype IS TABLE OF VARCHAR2(100) INDEX BY BINARY_INTEGER. v_date := v_date + 1. v_row := company_pkg.company_names_tab(v_row)). company_pkg. bizdays_norm bizdays_tabtype. old_names name_tabtype. END IF.company_names_tab. BEGIN old_names(1) := 'Smith'.id(company_pkg. ndays_in IN NUMBER := 30) IS TYPE bizdays_tabtype IS TABLE OF DATE INDEX BY BINARY_INTEGER. DBMS_OUTPUT. 'SUN') THEN bizdays_norm (nth_day) := v_date. LOOP EXIT WHEN v_row IS NULL. 10. old_names(2) := 'Raul'. * Iterative Eg: CREATE OR REPLACE PROCEDURE show_bizdays(start_date_in IN DATE := SYSDATE.companies_overdue(date_in). END LOOP.company_names_tab.NEXT (v_row).company_names_tab. END show_bizdays. . END company_pkg. company_pkg.company_pkg. EXCEPTION WHEN OTHERS THEN NULL. EXCEPTION WHEN OTHERS THEN NULL.LAST.PUT_LINE(v_date). v_date DATE := start_date_in.company_names_tab := company_pkg.

If any one of the arguments is NULL.new_names(1) := 'Smita'. 10. new_names(2) := 'Rahul'. Info: -.PUT_LINE(old_names(1)). then total_rows := emp_pkg. One way to empty a PLSQL table of all rows is to perform an aggregate assignment with a table that is empty. Eg: . then no rows will be deleted.. -. -. company_names_tab := empty_company_names_tab.COUNT. BEGIN . -. END. -. end_index_in IN INTEGER). Eg: total_rows := emp_table. if emp_table is defined in emp_pkg. PROCEDURE DELETE. company_names_tab company_names_tabtype.Deletes this particular row in the PLSQL table.emp_table. old_names := new_names. empty_company_names_tab company_names_tabtype.Deletes rows between start index and end index in the table. row_index2]) * COUNT: Returns the number of elements currently contained in the PLSQL table..COUNT. then DELETE will not delete any rows at all. EXCEPTION WHEN OTHERS THEN NULL.If end_index is less than start_index. EXCEPTION WHEN OTHERS THEN NULL. 10.Deletes all rows in the PLSQL table.DBMS_OUTPUT. it just sets the value of the row to NULL.<operation> * An operation which takes a row index as an argument: <table_name>. But this assignment doesn't actually remove the row or make it undefined. * DELETE: Deletes one or more elements from the PLSQL table. 3.<operation>(row_index1 [. ** Built-in functions of PLSQL tables: General Syntax for calling PLSQL table built-ins: * An operation which takes no arguments: <table_name>. DECLARE TYPE company_names_tabtype IS TABLE OF company.This statement will raise a no data found exception. -. PROCEDURE DELETE (index_in IN INTEGER).name%TYPE INDEX BY BINARY_INTEGER. END. Spec: FUNCTION COUNT RETURN INTEGER.7: Clearing the PLSQL table: * Can set a single row to NULL : company_names_table (num_rows) := NULL. PROCEDURE DELETE (start_index_in IN INTEGER. Spec: 1.8: PLSQL table enhancements: * Records are supported as elements of PLSQL tables. -. 2.

Eg: IF names_tab.DELETE (15). BEGIN myvar := table_in(index_in). * EXISTS: Returns FALSE if a reference to an element at the specified index would raise the NO_DATA_FOUND exception. Info: If the PLSQL table does not contain any elements. Spec: FUNCTION NEXT (index_in IN INTEGER) RETURN INTEGER.. Eg: next_row := names_tab.FIRST.DELETE.Deletes all rows in names_tab. Spec: FUNCTION PRIOR (index_in IN INTEGER) RETURN INTEGER. RETURN TRUE. -. -. then FIRST returns NULL. -. * NEXT: Returns the smallest index of the PLSQL table greater than the specified index. Eg 01: . * FIRST: Returns the smallest index of the PLSQL table for which an element is defined.EXISTS (1) THEN . 0).PRIOR(curr_row). names_tab.Deletes 15th row in names_tab.NEXT(curr_row). Info: PRIOR will return NULL if there are no rows defined before the specified index. names_tab. 0) + 1. company_table(next_row) := company_rec. Emulation of EXISTS function: FUNCTION row_exists (table_in IN <table_type>. * LAST: Returns the greatest index of the PLSQL table. 03. if there are no rows defined after the specified index. EXCEPTION WHEN NO_DATA_FOUND THEN RETURN FALSE. Eg: last_entry_row := names_tab.01. index_in IN INTEGER) RETURN BOOLEAN IS mrvar VARCHAR2(20).. Info: NEXT will return NULL. then we need to use NVL function to convert NULL into 0. FOR company_rec IN company_cur LOOP next_row := NVL(company_table. Eg: prior_row := names_tab. Eg: first_entry_row := names_tab.LAST. 02. names_tab. then LAST returns NULL. Spec: FUNCTION LAST RETURN INTEGER. SPEC: FUNCTION FIRST RETURN INTEGER.DELETE (-15000. Info: If PLSQL table does not contain any rows. END LOOP.Deletes all rows between -15000 and 0 inclusive. ** PLSQL table of Records: TYPE <type_name> IS TABLE OF <data_type> INDEX BY BINARY_INTEGER. for which an element is defined.LAST. * PRIOR: Returns the greatest index of the PLSQL table smaller than the specified index. Spec: FUNCTION EXISTS (index_in IN INTEGER) RETURN BOOLEAN. END. Returns TRUE if an element is defined at the specified index of the table. If you plan to fill the PLSQL table sequentially with values.

0) + 1. Eg 02: CURSOR emp_cur IS SELECT * FROM employee. Assigning Records in PLSQL tables: can assign the whole record fetched from the db directly into the row of a PLSQL table.'). 10. . Eg02: IF names_table (old_name_row + 1). <function_name>(<argument_list>)(<index_expression>). Eg 03: TYPE emp_rectype IS RECORD (employee_id INTEGER.TYPE emp_tabtype IS TABLE OF employee%ROWTYPE INDEX BY BINARY_INTEGER.last_name = 'SMITH' THEN . TYPE emp_tabtype IS TABLE OF emp_rectype INDEX BY BINARY_INTEGER. FOR company_rec IN company_cur LOOP next_row := NVL(company_table. Eg03: defining functions which return PLSQL tables. company_table(next_row) := company_rec.. END LOOP.<field_name> FUNCTION best_company (year_in IN INTEGER) RETURN company_tabtype. DBMS_OUTPUT. * Referencing fields of record elements in PLSQL tables: <table name>(<index expression>).PUT_LINE(best_company(1995)(10).company_name || ' Was 10th Best. Using Cursor FOR loop to transfer data: 1. TYPE emp_tabtype IS TABLE OF emp_cur%ROWTYPE INDEX BY BINARY_INTEGER. Define a PLSQL table TYPE for each data type found in the columns of the db table.LAST.9: Working with PLSQL tables: * Transferring DB information into PLSQL tables: Cannot use a SQL SELECT statement to transfer data directly from a database table to a PLSQL table. employee_name VARCHAR2(30)).<field name> Eg01: emp_tab(375) := 'RAHULMN'..

EXCEPTION WHEN NO_DATA_FOUND THEN NULL. incorp_date_tab date_tabtype. . BEGIN FOR company_rec IN company_cur LOOP next_row := NVL(company_rec_tab.incorp_date. fill_date FROM company. WHEN OTHERS THEN NULL.2. 4. incorp_date_tab (next_row) := company_rec.fill_date.company_id. END. BEGIN FOR company_rec IN company_cur LOOP next_row := NVL(company_keys_tab. company_keys_tab (next_row) := company_rec. END LOOP. 0) + 1. DECLARE CURSOR company_cur IS SELECT company_id. DECLARE CURSOR company_cur IS SELECT company_id. fill_date_tab date_tabtype. Declare cursor against the db table. END LOOP. incorp_date.LAST. next_row INTEGER := 0. Declare PLSQL table which will each receive the contents of a single column. TYPE company_keys_tabtype IS TABLE OF company. fill_date_tab (next_row) := company_rec. TYPE date_tabtype IS TABLE OF company.incorp_date%TYPE INDEX BY BINARY_INTEGER. next_row INTEGER := 0. company_rec_tab company_rec_tabtype. 3. company_rec_tab(next_row) := company_rec. incorp_date.company_id%TYPE INDEX BY BINARY_INTEGER.LAST. fill_date FROM company. TYPE company_rec_tabtype IS TABLE OF company_cur%ROWTYPE INDEX BY BINARY_INTEGER. company_keys_tab company_keys_tabtype. Execute the FOR loop. 0) + 1.

error_msg_tab(matching_row)).PUT_LINE('No Match Found!'). Data-Smart/Sparse Storage: PROCEDURE display_error(errcode_in IN NUMBER) IS BEGIN DBMS_OUTPUT. END.PUT_LINE(error_pkg.error_code_tab (matching_row) = errcode_in THEN keep_searching := FALSE. WHEN OTHERS THEN NULL. ELSE keep_searching := matching_row <= error_pkg. END IF. EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT. Sequential/Parallel storage: Approach: Create two tables: one that holds error codes and another that holds messages. The row in which the code is found is also the row in which the message is present. DBMS_OUTPUT. ** Data-smart row numbers in PLSQL tables. WHEN OTHERS THEN NULL. BEGIN WHILE keep_searching LOOP IF error_pkg. When an error is encountered. END LOOP.EXCEPTION WHEN NO_DATA_FOUND THEN NULL. EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.last_row > 0.PUT_LINE('No Match Found For ' || errcode_in). END. procedure scans sequentiallu through the PLSQL table of codes until it finds a match.error_details_tab(errcode_in)). matching_row := matching_row + 1. WHEN OTHERS THEN NULL. END. * Displaying a PLSQL table: .last_row. PROCEDURE display_error (errcode_in IN NUMBER) IS matching_row BINARY_INTEGER := 1.PUT_LINE(error_pkg. keep_searching BOOLEAN := error_pkg.

it will stop. 02. increment_in: The amount by which the row counter is incremented. 03. Implementation of display table: PROCEDURE display_table (table_in <the_table_type>. number_of_rows_in IN INTEGER) IS BEGIN FOR table_row IN 1 . Flexible display_table_procedure: PROCEDURE table_in (table_in <the_table_type>. Default is 0. failure_threshold_in: The number of times the program can encounter and handle the no data found error. failure_threshold_in IN INTEGER := 0. FUNCTION in_range (row_in IN INTEGER) RETURN BOOLEAN IS BEGIN IF increment_in < 0 THEN RETURN row_in >= end_row_in. start_row_in IN INTEGER := 1. end_row_in: Last row of the PLSQL table. Assumptions: 01. END LOOP. All rows between one and number of rows are defined. increment_in IN INTEGER := +1) table_in: The PLSQL table to be displayed. within_threshold BOOLEAN := TRUE. start_row_in: The first row of the PLSQL table which has a value defined. ELSE . end_row_in IN INTEGER.PUT_LINE(table_in (table_row)). number_of_rows_in LOOP DBMS_OUTPUT.. the loop does not even consider the possibility of a negative number. as the procedure scans through the table.PROCEDURE display_table (table_in IN <the_table_type>. The first defined row of the table is row one. WHEN OTHERS THEN NULL. EXCEPTION WHEN NO_DATA_FOUND THEN NULL. which has a value defined. end_row_in IN INTEGER. count_misses INTEGER := 0. The FOR loop always starts at one. increment_in IN INTEGER := +1) IS current_row INTEGER := start_row_in. END. The number_of_rows_in is a positive number. failure_threshold_in IN INTEGER := 0. the first time the program encounters the NO_DATA_FOUND error. start_row_in IN INTEGER := 1.

So.1) * number_of_rows_in_array + cell_row. EXCEPTION WHEN NO_DATA_FOUND THEN NULL. can partition the single PLSQL table so that it contains all the cells of a traditional array. IF NOT within_threshold THEN DBMS_OUTPUT.RETURN row_in <= end_row_in.cell Obtain the value of any cell in the array. END IF. current_row := current_row + increment_in.PUT_LINE('Exceeded threshold on undefined rows in table. END. END IF.').PUT_LINE('Increment for table display must be non-zero!'). WHEN OTHERS THEN NULL. END. Can optionally specify the default value to be placed in each cell. END IF. ELSE WHILE in_range(current_row) AND within_threshold LOOP BEGIN DBMS_OUTPUT. END LOOP.make Make or declare an array with specified numbers of rows and columns. Can spread n different columns of n different rows across the expanse of the PLSQL table. array. EXCEPTION WHEN NO_DATA_FOUND THEN within_threshold := count_misses < failure_threshold. General Formula which converts the row and column of an array cell into a corresponding PLSQL table is: table_row := (cell_column . IF within_threshold THEN count_misses := count_misses + 1. we can use which ever rows in the table we desire. BEGIN IF increment_in = 0 THEN DBMS_OUTPUT. WHEN OTHERS THEN NULL. END IF. ** Building Traditional Arrays with PLSQL tables: Because the PLSQL table has no (practical) size limitation. Features of array package: array. END. .PUT_LINE(table_in (current_row)).

PACKAGE BODY array IS /* The Number of Rows in the Array */ number_of_rows INTEGER := NULL. end_row_in IN INTEGER := row_count. /* Erase the array */ PROCEDURE erase. /* The generic table structure for a numeric table */ . col_in IN INTEGER) RETURN NUMBER. /* Change the value in a cell */ PROCEDURE change (row_in IN INTEGER. /* The number of columns in the array */ number_of_columns INTEGER := NULL. /* Returns the number of columns in the array */ FUNCTION column_count RETURN INTEGER. array. /* Create an array */ PROCEDURE make ( num_rows_in IN INTEGER := 10. /* Display the array */ PROCEDURE display (start_row_in IN INTEGER := 1. /* Return the value in a cell. display_style_in IN VARCHAR2 := 'ARRAY' ). Can display the table contents in array style or inline style. num_columns_in IN INTEGER := 1.display Display the contents of the array. col_in IN INTEGER.change Change the value of any cell in the array. conflict_action_in IN VARCHAR2 := 'OVERWRITE' ). PACKAGE array IS /* Returns the number of rows in the array */ FUNCTION row_count RETURN INTEGER. initial_value_in IN NUMBER := NULL. END array. array. value_in IN NUMBER).array. end_col_in IN INTEGER := column_count. start_col_in IN INTEGER := 1.erase Change the value of any cell in the array. */ FUNCTION cell (row_in IN INTEGER.

then erase the existing array. then the table is in use and the user did not want to overwrite it. /*------------------Private Modules--------------------*/ FUNCTION row_for_cell (row_in IN INTEGER. END. /* The actual table which will hold the array */ number_array number_array_type. initial_value_in IN NUMBER := NULL.TYPE number_array_type IS TABLE OF NUMBER INDEX BY BINARY_INTEGER. num_columns_in IN INTEGER := 1. /*-----------------Public Modules----------------------*/ FUNCTION row_count RETURN INTEGER IS BEGIN RETURN number_of_rows. END. If the conflict action is default or OVERWRITE. */ . If it has a value. */ IF number_of_rows IS NOT NULL AND UPPER(conflict_action_in) = 'OVERWRITE' THEN erase. END IF. FUNCTION column_count RETURN INTEGER IS BEGIN RETURN number_of_columns. PROCEDURE make (num_rows_in INTEGER := 10. /* An empty table used to erase the array */ empty_array number_array_type. /* Only continue now if the number of rows is NULL. conflict_action_in IN VARCHAR2 := 'OVERWRITE' ) IS /* Create an array of the specified size with the initial value. then it will be erased and remade only if the conflict action is OVERWRITE or default. then the array is already in use. */ BEGIN /* If the number of rows is NOT NULL. col_in IN INTEGER) RETURN INTEGER IS BEGIN RETURN (col_in-1) * number_of_rows + row_in END. If the table is already in use.

We use a single table. PROCEDURE erase IS BEGIN number_array := empty_array. /* A PLSQL table's row is defined only if a value is assigned to that row. col_in INTEGER) RETURN NUMBER IS BEGIN RETURN number_array(row_for_cell(row_in. END. number_of_rows LOOP number_array(row_for_cell(row_in. We use row_for_cell function to space out the different cells of the array across the table. WHEN OTHERS THEN NULL. value_in NUMBER) IS BEGIN number_array(row_for_cell(row_in. So to create the array. col_in)) := initial_value_in. END. number_of_columns := NULL.IF number_of_rows IS NULL THEN /* Set the global variables storing the size of the array */ number_of_rows := num_rows_in. but segragate distinct areas of the table for each column of the data.. even if that is only a NULL value. EXCEPTION WHEN NO_DATA_FOUND THEN NULL. . end_row_in IN INTEGER := row_count. col_in)). END make. col_in)) := value_in. */ FOR col_index IN 1 . number_of_columns := num_columns_in. END LOOP. col_in INTEGER.. PROCEDURE change(row_in INTEGER. FUNCTION cell (row_in INTEGER. I will simply make the needed assignments. PROCEDURE display ( start_row_in IN INTEGER := 1. END LOOP. number_of_columns LOOP FOR row_index IN 1 . END. END IF. number_of_rows := NULL. start_col_in IN INTEGER := 1.

CLOSE company_cur..PUT_LINE(. store-in-plsql-table.company_name. return-company_name. Above code is in-efficient since. END display.end_col_in IN INTEGER := col_count. IF company_cur%NOTFOUND THEN DBMS_OUTPUT. Blending Database and PLSQL access. So the next time data is required. ========================================================================================== ================================================ Procedures and Functions ========================================================================================== ================================================ ***************************************************************************************************** ************************************* 17/APR/2008 ***************************************************************************************************** ************************************* 15. the name is queried from the db. Performance impact of blended access. END.. END. display_style_in IN VARCHAR2 := 'ARRAY' ) IS BEGIN . END array.). * Optimizing Foreign Key Lookups with PLSQL tables: DECLARE CURSOR company_cur IS SELECT name FROM company WHERE company_id = :employee. for every company_id. if the data is not found in the PLSQL table. we can first query the PLSQL table for data and then query the db for data. We can store data in PLSQL tables.. BEGIN OPEN company_cur.company_id. return-company-name.1: Modular Code: . END IF. as and when the data is queried from the db. FUNCTION company_name (id_in IN company. EXCEPTION get-data-from-db.company_id%TYPE) RETURN VARCHAR2 IS BEGIN get-data-from-table.. FETCH company_cur INTO :employee.

types.2. Execution Section . BEGIN hire_date := SYSDATE -2.* More Re-usable.. * Function without an exception section: FUNCTION soft_pillow (type_in IN VARCHAR2) RETURN VARCHAR2 IS CURSOR pillow_cur IS SELECT softness FROM pillow WHERE pillow_type = type_in.. END. * Package: Named collection of procedures. More Readable. hire_date). Declaration Section .. .2: PL/SQL block structure examples: PROCEDURE get_happy(ename IN VARCHAR2) IS hire_date DATE. EXCEPTION WHEN DUP_VAL_IN_INDEX THEN DBMS_OUTPUT. 15. * Procedure: A named PL/SQL block that performs one or more actions and is called as an executable PL/SQL statement.. 15. pillow_rec pillow_cur%ROWTYPE. gives developer more control over scope of identifiers and exception handling. INSERT INTO employee (emp_name... Can pass information into a function through it's parameter list.2: Review of the PL/SQL block structure: ** Common Block Structure: Header Section . hiredate) VALUES (ename_in. More Reliable. More Manageable. can pass information into and out of a procedure through it's parameter list. Exception Section .1: Sequence of Section construction: * Must always have one executable section in the block. * Function:A named PL/SQL block that returns a single value and is used like a PL/SQL expression. BEGIN OPEN pillow_cur. * Anonymous Block: Un-named PL/SQL block which performs one or more actions.PUT_LINE('Cannot Insert'). 15.2. functions and variables...

.. CLOSE pillow_cur.softness.3. END IF. BEGIN IF :employee. END. BEGIN . END. BEGIN hiredate := right_now.3: The anonymous PL/SQL block: BEGIN DBMS_OUTPUT.. RETURN pillow_rec. 6) THEN RAISE too_late.. PROCEDURE get_happy IS BEGIN DECLARE . 15. optional declaration statements . END. END.3..2: Examples of anonymous blocks: 01: BEGIN hiredate := SYSDATE. executable statements .. END.1: The structure of an anonymous block: DECLARE ... 15.. too_late EXCEPTION.FETCH pillow_cur INTO pillow_rec.. END.. 15..PUT_LINE('Hello World').. EXCEPTION WHEN too_late ... 03: DECLARE right_now DATE := SYSDATE. END.hiredate < ADD_MONTHS(right_now. EXCEPTION .hiredate := right_now.. ELSE :employee. optional exception handlers . 02: DECLARE right_now DATE := SYSDATE. BEGIN .

3.3.. BEGIN .The president shows her generosity.). ELSE DBMS_OUTPUT... BEGIN . END.. END. DECLARE .2: Nested blocks provide scope: * Use this scope to improve control over activity in the program. The body of the trigger is coded in PLSQL..3. 15.. avgsal_in IN NUMBER) IS BEGIN -.50 WHERE company_id = company_id_in AND title = 'VICE-PRESIDENT'... END.3: Anonymous blocks in the oracle tools: * Database Trigger on a record or column of a table.PUT_LINE(. EXCEPTION WHEN NO_DATA_FOUND THEN NULL.1: Nested block terminology: * Nested/Enclosed/Child/Sub Block * Blocks Calling Nested Blocks: Enclosing/Parent Blocks. -.4. the PLSQL code itself is un-named.. 15. WHEN OTHERS THEN DBMS_OUTPUT. 15. 15... hence anonymous. BEGIN SELECT salary INTO v_sal FROM employee WHERE company_id = company_id_in AND title = 'PRESIDENT'..THEN DBMS_OUTPUT.The vice-president shows his generosity.4: Nested blocks: DECLARE .PUT_LINE ('The VP is OK!'). BEGIN SELECT salary INTO v_sal FROM employee WHERE company_id = company_id_in AND title = 'VICE-PRESIDENT'.3.PUT_LINE('Error Encountered ' || SQLCODE). END IF. While the trigger has a name. END. PROCEDURE update_management (company_id_in IN NUMBER.. IF v_sal > avgsal_in * 10 ..4. IF v_sal > avgsal_in * 10 THEN UPDATE employee SET salary := salary * .

THEN UPDATE employee SET salary := salary * .50 WHERE company_id = company_id_in AND title = `PRESIDENT'; ELSE DBMS_OUTPUT.PUT_LINE ('The Prez is a pal!'); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END; END; Named modules offer scoping effect of nested block: PROCEDURE update_management (company_id_in IN NUMBER, avgsal_in IN NUMBER, decr_in IN NUMBER) IS CURSOR salcur (title_in IN VARCHAR2) IS SELECT salary FROM employee WHERE company_id = company_id_in AND title = title_in AND salary >= avgsal_in * 10; PROCEDURE update_exec (title_in IN VARCHAR2) IS salrec salcur%ROWTYPE; BEGIN OPEN salcur(title_in); FETCH salcur INTO sal_rec; IF salcur%NOTFOUND THEN DBMS_OUTPUT.PUT_LINE('The ' || title_in || ' is OK!'); ELSE UPDATE employee SET salary := salary * decr_in WHERE company_id = company_id_in AND title = title_in; END IF; CLOSE salcur; END; BEGIN update_exec('VICE_PRESIDENT'); update_exec('PRESIDENT'); END; 15.3.5: Scope and Visibility: * Scope and Visibility of identifiers: Identifier is the name of a PL/SQL object. Anything from a variable to a program name. An identifier is visible in a program when it can be referenced by an un-qualified name. Qualified Identifiers: * Qualifier for an identifier can be a package, module name (procedure/function) or loop label. Qualify the name of an identifier with dot notation. Eg: :GLOBAL.company_id : Global variable in oracle forms. std_types.dollar_amount : Subtype declared in a package.

* Scope of an indentifier is generally the block in which the identifier is declared. DECLARE first_date DATE; last_date DATE; BEGIN first_date := SYSDATE; last_date := ADD_MONTHS(SYSDATE, 6); END; Both first_date and last_date are visible in this block. The scope of the two variables is precisely this anonymous block. cannot make reference to either of these variables in a second anonymous block or procedure. If an identifier is declared or defined outside of the current PL/SQL block, it is visible (can be referenced without a qualifier) only under the following conditions: * Identifier is the name of the standalone procedure or function on which you have the EXECUTE privilege. Can call that module in the block. * Identifier is declared in a block which encloses the current block. Scope of nested blocks: When we declare a variable in a PLSQL block, then that variable is local to that block, but is visible or global to any block which are defined within the enclosing block. DECLARE last_date DATE; BEGIN last_date := LAST_DAY(SYSDATE); BEGIN IF last_date > :employee.hire_date THEN ... END IF; END; END; Qualifying identifier names with module name: PackageName.VariableName [available globally], ProcedureName.VariableName PROCEDURE calc_totals IS salary NUMBER; BEGIN ... DECLARE salary NUMBER; BEGIN salary := calc_totals.salary; END; END; Only salary always resolves to the local variable in the Inner block. calc_totals.salary resolves to the procedure wide salary variable. Cursor Scope: The rules for cursor scope are same as those for all other identifiers. PROCEDURE get_employees IS CURSOR emp_cur IS

SELECT employee_id, sal + bonus FROM employees; BEGIN OPEN emp_cur; DECLARE empid NUMBER; total_comp NUMBER; BEGIN FETCH emp_cur INTO empid, total_comp; IF total_comp < 5000 THEN MESSAGE ('I need a raise'); END IF; END; EXCEPTION WHEN OTHERS THEN NULL; END; * emp_cur can be referenced within get_employees and any nested blocks in get_employees. PROC ... IS empid NUMBER; total_comp NUMBER; BEGIN DECLARE CURSOR emp_cur IS SELECT employee_id, sal + bonus FROM employees; BEGIN OPEN emp_cur; END; -- This won't work. FETCH emp_cur INTO empid, total_comp; ... END; 15.3.6: Block Labels: * Give a name to the PLSQL block for the duration of the execution. Is a PLSQL label which is placed directly in front of the first line of the block. Format: <<label>> Example: <<calc_dependencies>> DECLARE v_senator VARCHAR2(100) := 'THURMOND, JESSE'; BEGIN IF total_contributions (v_senator, 'TOBACCO') > 25000 THEN DBMS_OUTPUT.PUT_LINE('We''re Smokin!'); END IF; END; <<tobacco_dependency>> DECLARE v_senator VARCHAR2(100) := 'THURMOND, JESSE'; BEGIN IF total_contributions (v_senator, 'TOBACCO') > 25000 THEN <<alochol_dependency>> DECLARE

TAKES'. ELSE RAISE invalid_discount. invalid_discount EXCEPTION. END IF.the American way of life!'). PROCEDURE apply_discount (company_id_in IN company. IF SQL%ROWCOUNT = 0 THEN RAISE NO_DATA_FOUND. Syntax: PROCEDURE name [( parameter [. EXCEPTION WHEN invalid_discount THEN DBMS_OUTPUT. WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.25. . Eg: apply_discount(new_company_id.v_senator = alcohol_dependency.4 Procedures: Is a module performing one or more actions. parameter. 0..company_id = company_id_in).').05. END IF.PUT_LINE('Maximizing profits . END. a PLSQL block could consist of nothing more than a single call to a procedure.order_id = item. END IF.discount_in) WHERE EXISTS (SELECT 'X' from order WHERE order.v_senator THEN DBMS_OUTPUT. BEGIN IF tobacco_dependency.v_senator VARCHAR2(100) := 'WHATEVERIT.PUT_LINE('The specified discount is invalid. since a procedure is a standalone executive statement in PLSQL. 15. max_discount CONSTANT NUMBER := .PUT_LINE('No Orders in the system for the company ' || TO_CHAR(company_id_in)).4. END apply_discount.1: Calling a Procedure: A Procedure called is an executable PLSQL statement.company_id%TYPE. discount_in IN NUMBER) IS min_discount CONSTANT NUMBER := .order_id AND order. END IF. BEGIN IF discount_in BETWEEN min_discount AND max_discount THEN UPDATE item SET item_amount = item_amount * (1 .])] IS [declaration statements] BEGIN executable statements [EXCEPTION exception handler statements] END [name]. 15. END.15)..

4: The END label: Append the name of the procedure directly after the END keyword when the procedure is completed.status_code%TYPE) IS SELECT SUM (amount * discount) FROM item WHERE EXISTS (SELECT 'X' FROM order WHERE order.3: Procedure Body: Is the code required to implement the procedure.company_id%TYPE.status_code%TYPE := NULL) RETURN NUMBER IS /* Internal Upper cased version of status code */ status_int order. Smallest PLSQL Procedure: PROCEDURE do_nothing IS BEGIN NULL.4. Eg: FUNCTION tot_sales ( company_id_in IN company.4.])] RETURN return_datatype IS [declaration_section] BEGIN [executable_section] EXCEPTION [exception handler statements] END. 15.. Everything after the IS keyword in the procedure makes up the procedure body.4.status_code%TYPE := UPPER (status_in). Name and a list of parameters [if any].1: Structure of a function: FUNCTION name [(parameter1 [. status_in IN order. 15. execution and exception sections of the procedure. 15. 15.order_id = item. return_value NUMBER. parameter2 . FETCH sales_cur INTO return_value.5. END do_nothing. consists of declaration.. BEGIN OPEN sales_cur(status_int).order_id AND company_id = company_id_in AND status_code LIKE status_in). CURSOR sales_cur (status_in IN order. 15. Call to a function can only be a part of an executable statement.2: Procedure Header: Consists of Module Type.display_store_summary. Cannot be a stand-alone executable statement.5: Functions: Is a module that returns a value. Declaration and Exception sections are optional. Name serves as a label that explicitly links up the end of the program with the beginning. IF sales_cur%NOTFOUND THEN .

5: Function Header: FUNCTION <function_name> [(parameter1 [. BINARY_INTEGER. 15...])] RETURN <return_data_type> IS 15. Datatype can be scalars like: VARCHAR2. parameter2.5.2: The RETURN datatype: Standard PL/SQL datatype or Composite Datatype.4: Calling a function: sales_for_1995 := tot_sales(1504. END.1: Functions without Parameters: IF tot_sales THEN . ELSE CLOSE sales_cur. RETURN NULL. 15.4. NUMBER. END IF. Only one RETURN statement is processed during the function's execution. 15. the function terminates immediately and returns control to the calling PLSQL block.8. END tot_sales. Object Type or LOB/CLOB/BFILE/BLOB 15. 15. Can have multiple RETURN statements.6: Function Body: Everything after the IS keyword. 'C').5.1: Multiple RETURNS IF sales_cur%NOTFOUND THEN CLOSE sales_cur. IF tot_sales(1504. 15.5.5..CLOSE sales_cur. 15. 'C') > 10000 THEN . BOOLEAN etc or complex or composite datatypes like: PLSQL Table. When a RETURN statement is processed. PLSQL Record.3: The END label: Append the end label directly after the END keyword when the function is completed.5. DECLARE sales_for_2008 NUMBER DEFAULT tot_sales(1504.5.. 'C') BEGIN . 15..7: A Tiny Function: FUNCTION does_nothing RETURN BOOLEAN IS BEGIN RETURN TRUE. ..5.5. Nested Table or VARRAY.. IF does_nothing THEN NULL.5. RETURN return_value. Datatype cannot be an exception or cursor name..8: The RETURN statement: Must have atleast one return statement.

* RETURN (100-pct_of_total_salary(employee_id)).8. 15. RETURN simply halts the procedure and returns control to the calling program..3: No RETURN is executed: PLSQL raises an error: "Function Returned Without Value.. DECLARE company_name VARCHAR2(60).1. Constrained Declaration: Constrains or limits the kind of values that can be assigned to a variable declared with that datatype. * RETURN POWER(max_salary. END IF. END.8.2: Return any valid expression: * RETURN 'String'. 15. * RETURN TO_DATE(01 || earliest_month || initial_year.RETURN NULL.1: %TYPE and %ROWTYPE: . 15. ** An Expression in the RETURN statement is evaluated when the RETURN statement is executed.8.8.5. procedure version of RETURN cannot take an expression.6. PROCEDURE display_company (company_name IN VARCHAR2) IS . 5). Avoid using both RETURN and GOTO to bypass proper control structures. 15. END IF.4: RETURN as last executable statement: FUNCTION company_type (type_code_in IN VARCHAR2) RETURN VARCHAR2 IS return_value VARCHAR2(25) := NULL. BEGIN . RETURN return_value.6: Parameters: Consideration regarding parameters: * number of parameters * types of parameters * names of parameters * default values of parameters 15.6.. 15.5.5: RETURN statement in a procedure: RETURN statements can also be used in procedures. ELSIF type_code_in = 'P' THEN return_value := 'PARTNER'. ELSE CLOSE sales_cur. BEGIN IF type_code_in = 'S' THEN return_value := 'SUBSIDIARY'." 15. RETURN return_value. Unconstrained declaration does not limit values in this way. 'DDMMYY').5..1: Defining Parameters: Parameter declaration must be un-constrained.5.

6. * OUT parameter can only be found at the left side of an assignment operation. When the program terminates successfully and returns control to the calling block. * Cannot provide a default value to the OUT parameter. cannot assign or modify values of IN parameters. not a constant or literal. any assignments to an OUT parameter are actually made to an internal copy of the OUT parameter. because at the end of the program a value has to be assigned to an OUT parameter. must be a variable. FIRST' THEN full_name_out := last_name_inout || '. END IF. * IN parameters function like constants and value of the formal IN parameter cannot be changed within the program. last_name_inout IN OUT VARCHAR2. 15.breed_name%TYPE INDEX BY BINARY_INTEGER. FIRST' ) IS BEGIN first_name_inout := UPPER(first_name_inout). the value in the local copy is traneferred to the actual OUT parameter. last_name_inout := UPPER(last_name_inout).PACKAGE pet_hotel IS TYPE breed_type IS TABLE OF breed. %TYPE and %ROWTYPE are both un-constrained declarations.2: OUT Mode: * During the execution of the program. EXCEPTION WHEN OTHERS .6. ELSIF name_format_in = 'FIRST LAST' THEN full_name_out = first_name_inout || ' ' || last_name_inout. * Can be used on both sides of the assignment. END.6. Actual sizes of the parameters depend on the structures of tables and columns to which the attributes point. PROCEDURE combine_and_format_names (first_name_inout IN OUT VARCHAR2. FUNCTION room_by_breeds (breed_table_in IN breeds_type) RETURN avail_rooms_type.6.2. 15. IN OUT and OUT 15. full_name_out OUT VARCHAR2.2: Parameter Modes: IN. TYPE avail_rooms_type IS TABLE OF room.1: IN Mode: * Default mode when nothing is specified. 15. * Any assignments made to an OUT parameter are rolled back when an exception is raised in the program.2. A constant or a literal cannot be a receptacle of this value.room_number%TYPE INDEX BY BINARY_INTEGER. This size is resolved only at compile time.3: The IN OUT Mode: * Can pass values into a program and return values from a program * Cannot have a default value. name_format_in IN VARCHAR2 := 'LAST.2. IF name_format_in = 'LAST. * Actual OUT param must be a variable. ' || first_name_inout.

var2 IN VARCHAR2 := 'HELLO') IS.1: Positional Notation: Associate the actual parameter implicitly [by position] with the formal parameter.6.7.6. 'HILDA'). * Must be located after all declaration statements..2: Named Notation: Explicitly Associate the formal parameter with the actual parameter using "=>".6.. 15.7..2: Reducing Code volume: PROCEDURE calc_percentages (tot_sales_in IN NUMBER) IS . Positional notation has to have a starting point from which to keep track of positions and the only starting point is the first parameter.6. * Can use either the DEFAULT keyword or the := for giving default values. * Improve code Readability.6. * Named notation gives us complete flexibility over parameter specification. END. sample_proc(5). status_in => 'N') If we mix. sample_proc(5.4. 15. DECLARE PROCEDURE show_data (data_in IN VARCHAR2) IS BEGIN DBMS_OUTPUT. 15. BEGIN . 15..4. EXCEPTION . then must list all our positional parameters before the named parameters. 15.7: Local Modules: * Is a procedure or function which is defined inside the declaration section of a Pl/SQL block. formal_param_name => actual_value Ex: tot_sales(company_id_in => 5675 . Switch to named notation to leave leading default parameters. PROCEDURE sample_proc IS (var1 IN NUMBER DEFAULT 5. 15. status_in => 'N') Can also mix named and positional notation in the same program call: tot_sales (company_id_in. END.5: Default Values: * Can provide default values for IN parameters. 15.. * Cannot be called by any other PLSQL blocks defined outside of that enclosing block. 15.PUT_LINE('Data is ' || data_in).3: Actual and Formal Params: Formal: Names that are declared in the parameter list of the header in a module.4: Matching Actual and Formal Parameters in PL/SQL: 15..1: Benefits of Local Modularization: * Reduce the size of the module by stripping it of repetitive code.6. Only defined in the parent block.THEN NULL. END.3: Benefits of named notation: * Named notation is self-documenting. Actual: Values or expressions placed in the parameter list to the actual call to the module.4. sample_proc(var2 => 'BYE').

first go to the execution section and understand the big-picture. FOR emp_rec IN (SELECT employee_id FROM employee WHERE department_id = department_in) LOOP IF caseload (emp_rec. 15.. schedule_case(emp_rec.. $999.7. case_number NUMBER.employee_id. END IF.7..8: Module Overloading: .4: Bottom Up Reading: Execution section of the program is pushed to the bottom.6: Spruce up your code with local modules! 15. PROCEDURE schedule_case (employee_id IN NUMBER...3: Improving Readability: PROCEDURE assign_workload (department_in IN NUMBER) IS /* Declare local variables first */ avg_workload NUMBER.FUNCTION char_format (val_in IN NUMBER) RETURN VARCHAR2 IS BEGIN RETURN TO_CHAR((val_in/tot_sales)*100. END. END LOOP. case_number).liquor_sales).employee_id) < avg_workload THEN assign_next_open_case(emp_rec. END.liquor_sales := char_format(sales_pkg. END. /*--------------LOCAL MODULES-----------------*/ PROCEDURE assign_next_case (employee_id_in IN NUMBER.. case_out OUT NUMBER) IS .. FUNCTION average_cases (department_id_in IN NUMBER) RETURN NUMBER IS BEGIN .. FUNCTION caseload (employee_id_in IN NUMBER) RETURN NUMBER IS BEGIN ..employee_id. END. 15.food_sales := char_format(sales_pkg. 15. FUNCTION next_appointment (case_id_in IN NUMBER) RETURN NUMBER IS BEGIN . BEGIN .7. To try and understand local modules....999).. BEGIN avg_workload := average_cases (department_in). 15. date_in IN DATE) IS . my_package. END. END assign_workload. END.food_sales). BEGIN my_package. case_number. next_appointment(case_number)). END. case_in IN NUMBER.7..5: Scope of local modules: Only with-in the current module. BEGIN .

2: Benefits of overloading: 15. END IF. FUNCTION calculate_net_profit (total_revenue_in IN NUMBER) RETURN NUMBER IS BEGIN . must be called using the named notation. END. END calculate_net_profit. END calculate_net_profit. BEGIN IF value_ok (SYSDATE-4) || value_ok (1500) THEN .. POSITIVE. SUBTYPE atomic_type IS VARCHAR2.. * The parameter list of overloaded programs must differ by more than parameter mode. calculate_net_profit (revenue_in => 28090). BINARY_INTEGER are NUMBER subtypes. FUNCTION value_ok (number_in IN NUMBER) RETURN BOOLEAN IS BEGIN RETURN number_in < 1000. At the time the overloaded function is called. SUBTYPE word_seperator_type IS VARCHAR2. 15.8.3: Where to Overload modules: * Inside Declaration of a PL/SQL block or inside a package.. CHAR.1: Overloading in PLSQL builtins: TO_CHAR 15.8. FUNCTION calculate_net_profit (revenue_in IN NUMBER) RETURN NUMBER IS BEGIN . FLOAT. 15.4: Restrictions on overloading: * Datatype family of atleast one param of the overloaded program must differ. END.. module or .8. * All of the overloaded programs must be defined within the same PLSQL scope or block (anonymous block. determine which version of the function to use. * Not with stand-alone procedures or functions. EXCEPTION WHEN OTHERS THEN NULL. LONG are character subtypes. VARCHAR2. REAL. ** Creation of subtypes: SUBTYPE line_text_type IS CHAR.DECLARE FUNCTION value_ok (date_in IN DATE) RETURN BOOLEAN IS BEGIN RETURN date_in < SYSDATE. if all the params are same.8.. * Overloaded programs which differ only by parameter names.. calculate_net_profit (total_revenue_in => 28090). Overloaded functions must differ by more than their return type. END. The compiler cannot therefore. INTEGER. the compiler does not know what type of data the functions will return. DECIMAL.

) RETURN NUMBER IS BEGIN IF net_profit(..) RETURN NUMBER. Therefore number chg_estimate takes precedence over date chg_estimate.)..package). BEGIN .9: Forward Declarations: * Declaring a module ahead of actual definition of the module. END. BEGIN DECLARE PROCEDURE chg_estimate (number_in IN NUMBER) IS BEGIN .. END perform_calcs. The scope of date chg_estimate is the entire body of develop_analysis... END.. 15.) < 0 THEN RETURN 0... * Forward declaration consists simply of program header followed by Semicolon. BEGIN chg_estimate (quarter_end_in). the scope of number chg_estimate is only for the anonymous block. END. END...) RETURN NUMBER IS BEGIN RETURN tot_sales(. chg_estimate (number_in). sales_in IN NUMBER) IS PROCEDURE chg_estimate (date_in IN DATE) IS BEGIN . END. END IF.... PROCEDURE develop_analysis (quarter_end_in IN DATE. we get the following error: Wrong number or type of arguments in call to 'CHG_ESTIMATE' The above two chg_estimate procedures are not overloaded.. procedure.tot_cost(. On compilation. ELSE RETURN .. FUNCTION net_profit (..10: Go Forth and Modularize ========================================================================================== ================================================ Packages ========================================================================================== ..) . function or package) PROCEDURE perform_calcs (year_in IN NUMBER) IS /* Header only for total_cost function */ FUNCTION total_cost (.. * Definition for a forwardly declared program must be contained in the declaration section of the same PL/SQL block (anonymous block. * Cannot make forward declarations to variable or cursor. works with only procedures and functions... they have a different scope and visibility. 15. FUNCTION total_cost(. because they are declared in different PLSQL blocks. END develop_analysis.. Cannot define one version in one block and the other version in a different block.

functions and packages) stored in the database. END sp_timer. All other package elements are thereby made immediately available for future calls to the package. 16. can modify package variables in one module and reference the changed variables in another module. * When an object in a package is referenced for the first time.1. * Objects defined in a package specification (visible to anyone with EXECUTE authority on that package) act as global data for all PLSQL objects in the application. * Programs that call packaged modules will compile. Data persists for the duration of the user session (db connection). Global Data: Info that persists across application components. * Can limit automatic re-compiles by placing functions and procedures inside packages. as long as the specification compiles.2: The Body: Contains all the code behind the package specification: variables.5: Performance Improvement. * Oracle RDBMS automatically tracks the validity of all program objects (procedures.2. PACKAGE BODY sp_timer IS last_timing NUMBER := NULL. Eg: PACKAGE sp_timer IS PROCEDURE capture(context_in IN VARCHAR2). any program that calls the module is not flagged as in-valid and will not have to be re-compiled. * Can restrict access to the package to only the specification. such as tables. 16. * Package variables can carry data across the boundaries of transactions.1. Can open it in one module and fetch in another module. PLSQL does not have to keep retrieving program elemnts or data from disk each time a new object is referenced.1: Enforced Information Hiding. isn't local to the current module. DB automatically re-compiles these invalid programs before thay are used. PROCEDURE capture (context_in IN VARCHAR2) . * Package specification can compile without it's body.1: The Package Specification: * Contains the definition or specification of all elements in the package that my be referenced outside the package. 16. PROCEDURE show_elapsed.1: The benefits of packages: 16. * Top Down design: Move from high-level requirements to functional decompositions to module calls.1. Do not have to explicitly define the cursor in each program. then all programs that rely on that object are flagged invalid. If you have access to the package. the entire package (already compiled and validated) is loaded into memory. * If a packaged procedure opens a cursor. cursors and other objects.2: Object Oriented Design 16.2: Overview of the package structure: 16. * Decide which elements are public and which are private. As long as spec of a package does not change. determines what other objects a program is dependent on.================================================ * Package is collection of PL/SQL objects that are packaged and grouped together within a special BEGIN-END syntax. If program A calls packaged program B.2. 16. 16. * Most basic operators of PLSQL language such as + and LIKE operators and INSTR function are all defined in a special package called STANDARD. 16. If a dependent object such as a table's structure changes.1.3: Top-Down Design * Package specification can be written before the body. it does so through the package's specification.1.4: Object Persistence * Offer ability to implement global data in your application environment. that cursor remains open and is available to other packaged routines throughout the session. They are tied to the session itself and not to a transaction.

.. END pets_inc.IS BEGIN last_timing := DBMS_UTILITY. END.] END [package_name]..2.4: Public and Private Package Elements: ** Public: Defined in the specification.2. which is called the initialization section. If a public procedure calls a private function.GET_TIME. does not appear in the specification.] [Specification of Cursors . 16.. FUNCTION next_pet_shots (pet_id_in IN NUMBER) RETURN DATE.last_timing).3: Package Syntax: Package Specification: PACKAGE package_name IS [Declarations of Variables and Type . ** Private: Defined only in the body of the package.] [Specification of modules . Use dot notation to provide a fully qualified specification for a package's object. that function must be defined above the public procedure in the package body.. These are called private elements of the package and cannot be referenced outside of the package. * Package body may also contain an execution section. It is run only once to initialize the package. PACKAGE pets_inc IS max_pets_in_facility CONSTANT INTEGER := 120.. pet_is_sick EXCEPTION. Package Body: PACKAGE BODY package_name IS [declaration of variable and types] [specification and SELECT statement of cursors] [specification and body of modules] BEGIN [executable statements] EXCEPTION [exeption handlers] END [package_name]. Cannot be accessed from outside the package. END sp_timer. 16. 16.PUT_LINE(DBMS_UTILITY.2. * Must have atleast one declaration or specification statement in the package specification.. * body may also contain elements which do not appear in the package spec.5: How to reference package elements: Package owns it's objects.GET_TIME . PROCEDURE show_elapsed IS BEGIN DBMS_OUTPUT. CURSOR pet_cur RETURN pet%ROWTYPE. END. PROCEDURE set_schedule (pet_id_in IN NUMBER). * Private elements in a package must be defined before they can be referenced by other elements in the package. Any other element of the package may reference and use a private element.

BEGIN OPEN pets_inc. CURSOR pet_cur (pet_name_in IN VARCHAR2) RETURN pet%ROWTYPE. pet_id_nu petid_type. END pets_inc. END set_schedule.pet_cur%TYPE. pet_rec pets_inc. FUNCTION next_pet_shots (pet_id_in IN petid_type) RETURN DATE. * Inside the package.pet_cur%FOUND THEN next_visit := pets_inc. END IF.pet_id). we do not need to qualify references to other elements of that package.6: Quick Tour of a package: PACKAGE pets_inc IS SUBTYPE petid_type IS pet. CLOSE pets_inc.pet_is_sick THEN . END.. EXCEPTION WHEN pets_inc.2.. END show_next_visit. IF pets_inc. END IF.. PROCEDURE set_schedule (pet_id_in IN petid_type).max_pets_in_facility > 100 THEN ..pet_id%TYPE.next_pet_shots (pet_rec. PROCEDURE set_schedule (pet_id_in IN NUMBER) IS BEGIN IF max_pets_in_facility > 100 THEN .. END IF.pet_cur(pet_in)..2. Calling package elements from an external procedure: PROCEDURE show_next_visit (pet_in IN VARCHAR2) IS next_visit DATE.2: The Package Body: .PUT_LINE('Schedule next visit for ' || pet_in || ' on ' || TO_CHAR(next_visit)). 16.pet_cur INTO pet_rec.pet_cur. * pets_inc package body: 16. FETCH pets_inc. an outside procedure: BEGIN IF pets_inc.6.

. Exception declaration. TYPE declarations. PRAGMA EXCEPTION_INIT (exc_must_be_eighteen.1: A package of exceptions: PACKAGE exchdlr IS en_general_error NUMBER := -20000. * Of the above declarations. * May contain the following: Variable Declarations. Module Specification. only the cursors and modules need to be defined in the body. error_msg_table error_msg_tabtype. END.PACKAGE BODY pets_inc IS max_date CONSTANT DATE := SYSDATE + 10.6. 16.. END. PRAGMA EXCEPTION_INIT (exc_general_error. END exchdlr. * Have included a cursor or module in your specifications.1...3: Package Specification: * Specification of a package lists all objects in that package that are available for use in applications. 16. FUNCTION next_pet_shots (pet_id_in IN petid_type) RETURN DATE IS BEGIN . max_error_number_used NUMBER := -20001.3.3: Observations: 16. cursor specification [specify cursor name and RETURN statement]. en_must_be_eighteen NUMBER := -20001. en_general_error). END. exc_general_error EXCEPTION. en_must_be_eighteen). exc_must_be_eighteen EXCEPTION. TYPE error_msg_type IS TABLE OF VARCHAR2(240) INDEX BY BINARY_INTEGER. FUNCTION pet_status (pet_id_in IN petid_type) RETURN VARCHAR2 IS BEGIN .2...1: Packages without bodies: **Package only requires body if one of the following is true: * Want to define private package elements. . 16. CURSOR pet_cur (pet_name_in IN VARCHAR2) RETURN pet%ROWTYPE IS SELECT * FROM pet WHERE name LIKE '%' || pet_name_in || '%'. END pets_inc. PROCEDURE set_schedule (pet_id_in IN petid_type) IS BEGIN .3.

PROCEDURE assign_call(call_id IN INTEGER.1.16. 120). Magic values are literals which have special significance in an application.3. In a package spec. These values should never be hard-coded into an application. PROCEDURE remove_call (call_id IN INTEGER).1.3: Top-down design with bodiless packages. because.operator(operator_id) < workload. open_status CONSTANT VARCHAR2(1) := 'O'. we must use the RETURN clause of the cursor. max_difference CONSTANT NUMBER := 100. programs or individual statements.add_call(call_id). END support. A seperate package should be maintained. dept_id IN INTEGER) IS BEGIN IF workload. these values can be changed easily when required. PACKAGE support IS PROCEDURE add_call (call_id IN INTEGER). It compiles. latest_date CONSTANT_DATE := ADD_MONTHS(SYSDATE. FUNCTION operator (operator_id IN INTEGER) RETURN NUMBER.average(dept_id) THEN support. * Above 2 are only package specifications. active_status CONSTANT VARCHAR2(1) := 'A'. END workload. Since a package specification contains the complete signature of the method. inactive_status CONSTANT VARCHAR2(1) := 'I'.2: Declaring Package Cursors: * When we include a cursor in a package specification. earliest_date CONSTANT DATE := SYSDATE. below procedure calls the methods in the above 2 specifications. END assign_call. so that. PACKAGE workload IS FUNCTION average (dept_id IN INTEGER) RETURN NUMBER.2: A package of magic values: PACKAGE config_pkg IS closed_status CONSTANT VARCHAR2(1) := 'C'. END IF.3. It is an optional part of the cursor definition when it is defined in the declaration section of a PLSQL block. operator_id IN INTEGER.3. min_difference CONSTANT NUMBER := 1. Top-Down Design: Start with the general description of an application and gradually decompose into seperate functional areas. it is mandatory. 16. this is possible. PLSQL only need to know the signature of the method when it's called. . 16. END config_pkg.

* Cursors are best used. Changes in package spec should be implemented in package body and . No need to declare again. but can include an initialization section. company_id FROM caller. Declaration section consists of any public or private objects.4: The package body: * Contains all the code required to implement the package specification. execution and exception sections. Executed when the package is instantiated. using the %ROWTYPE attribute. Exception section handles any exceptions raised by the execution section. Need to define the module in the function body. are global within the package and invisible outside the package. TYPE caller_rec IS RECORD (caller_id caller. 16. PACKAGE cursor_sampler IS CURSOR caller_tab (id_in IN NUMBER) RETURN caller%ROWTYPE. * A record defined from a programmer defined record.caller_id%TYPE IS SELECT called_id FROM caller. The SELECT statement of the cursor appears only in the package body. END cursor_sampler. PACKAGE BODY cursor_sampler IS CURSOR hiredate_cur RETURN date_variable%TYPE IS SELECT hire_date FROM employee. * We wish to execute code in the initialization section of the package body. not the spec. * Variables which exist in package body and not in the spec.1: Declare in Specification or Body: * If we declare a variable in the package spec.2: Synchronize body with package: * Package spec and body must be kept synchronized. * Package body has declaration.caller_id%TYPE. when they are included in the package specification. 16.company_id%TYPE). Package body can have a declaration section without an execution section [common format]. then it's available in the package body.* RETURN clause indicates data elements that are returned by a fetch from the cursor. END cursor_sampler. * RETURN clause made up of either of the following datatype structures: * A record defined from a database table. * Package body can have empty declaration section.4. These are called private objects. CURSOR caller_tab (id_in NUMBER) RETURN caller%ROWTYPE IS SELECT * FROM caller WHERE caller_id = id_in. * Package body is required when any of the following conditions is true: * The Package specification contains a cursor declaration: Need to specify the SELECT statement in the body. * The package specification contains a procedure or function declaration. CURSOR caller_cur RETURN caller_rec. company_id company. CURSOR caller_key RETURN caller.4. CURSOR caller_cur RETURN caller_rec IS SELECT caller_id. * Execution section is the "initialization section" of the package. 16.

* Specification of a module should give us all info we need to understand how to call and use that module. * If spec and body don't match. It is global with-in the package.5. then we won't know that by seeing the specification. If there's an error in the match. Each oracle session is assigned it's own PLSQL area.5. then they won't compile.last_timing. as it does not appear from the package specification. * Data that is declared by the package elements is also instantiated in the that remains throughout the oracle session.5. PACKAGE BODY sp_timer IS last_timing NUMBER := NULL. END IF.vice versa. That code is shared by all sessions having EXECUTE authority on the package. END.GET_TIME.5: Package Data: * Package data is any data-structure declared in a package body or specification. END sp_timer. FUNCTION elapsed RETURN NUMBER IS BEGIN IF last_timing IS NULL THEN RETURN NULL. 16.5: Providing an interface to global data: Reasons to build programmatic interface around global data: * Loss Of Control * Maintenance Headaches . This private PLSQL area is maintained in the SGA. * Can change global public data-structures.GET_TIME . the entire compiled package is loaded into the SGA. but is not shared across all sessions. * Here last_timing is global to the package. for as long as the oracle session is running.4: Global Private Data: * Also called package-level data is declared inside the package body. * This data cannot be referenced from outside the package. it verifies that everything defined in the package spec has a body.5. * When PLSQL compiles a package. then an exception is raised. * Create persistent data structures -. which contains a copy of the package data. 16. cannot be sure of what's happening in the application. ** With package data we can: * Create global variables that are accessible from any program in the current session. If a program reads or writes global data-structures. END. even when no package programs are running. This leads to better implmentations.1: Architechture of package data: * The first time we reference a package element. 16. The values assigned to packaged data structures persist for the session duration. * Always advisable to pass data as parameters in and out of modules. 16. ELSE RETURN DBMS_UTILITY. unless they are declared as CONSTANTs in the declaration statement. 16. but not accessible outside the package.3: Global Public Data: * Data structure declared in the package spec is global public DS. PROCEDURE capture (context_in IN VARCHAR2 := NULL) IS BEGIN last_timing := DBMS_UTILITY.

16. 16. show_lov = 'Y'.emp_count FROM employees WHERE effective_start_date > SYSDATE. * Executable statements buried in initialization section are much harder to maintain. EXCEPTION WHEN NO_DATA_FOUND THEN user_name = 'NOT REGISTERED'. . user_id VARCHAR2(10) := USER.1: Drawbacks of package initialization: * Can be dangerous to have the tool perform actions for you that are not explicitly triggered by user or developer action. show_lov VARCHAR2(1). BIG NO-NO */ SELECT count(employee_id) INTO employee_pkg.2: Use Initialization section for complex logic: * Use initialization section only when you need to set the initial values of package elements using rules and complex logic that cannot be handled by the default value syntax for variables. We can supplement this automatic instantiation of package code with automatic execution of initialization code for the package. * Below package contains information about the current user session. show_toolbar.6. user_id. * All the package variables are set the very first time any of the variables are accessed by the application. the first time an object in the package is referenced. show_toolbar_flag.6. * The optional initialization section consists of all statements following the BEGIN statement to the END statement for the entire package body. PACKAGE BODY company_pkg IS /* Initialization section of company_pkg setting value of global data variable in employee_pkg. END company_pkg. show_toolbar VARCHAR2(1). * harder to trace actions triggered automatically by the tool. show_lov_flag.6.16. default_printer INTO user_name. PACKAGE BODY session_pkg IS BEGIN SELECT first_name || ' ' || last_name. show_lov. the whole package is loaded into the SGA of the DB instance. 16. printer FROM user_config WHERE user_id = USER. PACKAGE session_pkg IS user_name VARCHAR2(80). user_id. Statements are executed only once.3: Side Effects: * Avoid setting values of package global data from other packages within the initialization section.6: Package Initialization: * The first time an application makes reference to a package element.6. 16.4: Load Session Data in Initialization Section: * Perfectly legitimate use of the initialization section. END session_pkg. making all objects immediately available in the memory. printer VARCHAR2(30).

* Can call functions in SELECT. GROUP BY. :note.text.][func_name[@db_link_name][parameter_list]] Function Definition: FUNCTION calc_sales (company_id_in IN company. line_text. SUM(salary) FROM employee GROUP BY title 17. 'Problem obtaining user profile for ' || USER).][package_name. * In the above case. next_line_number(:call. START WITH. * Perform actions in SQL which are otherwise impossible. line_number) VALUES (:call. * Simplify SQL statements. END session_pkg. WHEN OTHERS THEN RAISE_APPLICATION_ERROR (-20000.2: Syntax for calling stored functions in SQL: [schema_name.call_id)) UPDATE employee SET salary = max_compensation(department_id) WHERE employee_id = 1005.show_toolbar = 'Y'. Calling Examples: . column alias is assigned to function using 'AS'. status_in IN order. WHERE.call_id.1: Looking at the problem: Calling a function in PLSQL: SELECT line_number.company_id%TYPE.number_of_atomics(line_text) AS num_words FROM notes ORDER BY num_words. SET and VALUES clauses. * Improve performance of your SQL statements. ORDER BY. ========================================================================================== ================================================ Calling PLSQL functions in SQL ========================================================================================== ================================================ 17. Advantages of calling functions in SQL: * Consolidate business logic to a smaller number of well tuned and easily maintained functions. printer = 'lpt1'. can use the alias in ORDER BY without a second call to the function.status_code%TYPE := NULL) RETURN NUMBER. INSERT INTO notes (call_id. SELECT job_category(job_title_id) AS title. ps_parse. HAVING.

* As a stand-alone function SELECT calc_sales(1001, 'O') FROM orders; * As a package-based function SELECT sales_pkg.calc_sales(1001, 'O') FROM orders; * As a remote package-based function: SELECT sales_pkg.calc_sales@NEW_DELHI(1001, 'O') FROM orders; * As a stand-alone function in a specific schema: SELECT scott.calc_sales(1001, 'O') FROM orders; * Should always avoid hard-coding the module's schema and database link directly in SQL statements. Should create synonyms that hide this information. * If you ever need to change the owner of the function or move it into a different database, can change only the synonym, rather than changing all the SQL statements that call that function. * When using a stored function in an SQL statement, must use positional notation. named and mixed notations are not allowed. 17.3: Requirements for Stored Functions in SQL: Requirements programmer defined PLSQL functions should meet, to be callable from an SQL statement: * Function must be stored in the database: A function in an individual form cannot be called by SQL. No way for SQL to resolve reference to that function. * Function must be row specific function, not a column or a group function. can apply to only a row of data. not an entire column of data that crosses rows. * All function's parameters should be IN parameters. * The datatypes of the function's parameters, as well as the datatype of the RETURN clause of the function must be recognized by the oracle server. PLSQL datatypes which are not yet supported in the database: BOOLEAN, BINARY_INTEGER, PLSQL Tables, PLSQL records and programmer defined subtypes. * Functions defined in packages must have a RESTRICT_REFERENCES pragma. If you want to call from SQL, a function defined in a package, you will need to add a pragma to the package spec. Invalid Functions: -- SQL doesn't recognize PLSQL tables. TYPE string_tabtype IS TABLE OF VARCHAR2(1000) INDEX BY BINARY_INTEGER; FUNCTION temp_table RETURN string_tabtype; -- SQL doesn't recognize BOOLEAN FUNCTION call_is_open (call_id_in call.call_id%TYPE) RETURN BOOLEAN; FUNCTION calc_sales(company_id NUMBER, use_closed_orders_in BOOLEAN) RETURN NUMBER; 17.4: Restrictions on PL/SQL functions in SQL: Ex: Invalid use of a function:

FUNCTION total_comp (salary_in IN employee.salary%TYPE, bonus_in IN employee.bonus%TYPE) RETURN NUMBER IS BEGIN UPDATE employee SET salary = salary_in/2; RETURN salary_in + NVL(bonus_in, 0); END total_comp; Don'ts: * Modification of database tables: Can affect the result of the query from which the function might originate. may affect any other SQL statement in the session. * Modification of package variables in Stored functions in SQL: Since package variables act global variables in a session, this could have an impact on other stored function or procedure, which in turn could affect a SQL statement using that stored function. * In WHERE clause: Query optimizer can re-order the evaluation of predicates in the WHERE clause to minimize the number of rows processed. A function executing in this clause could subvert the query optimization process. ==== A Function should concentrate on computing and returning a value. Oracle server makes it impossible to do any of the following: * Stored function cannot modify database tables. No INSERT, DELETE or UPDATE. * A stored function called remotely or through a parallelized action may not read or write package variables. Oracle server does not support side-effects that cross user sessions. * A stored function can update values of a package only if that function is called in SELECT, VALUES and SET clauses. Cannot update package variables in WHERE or GROUP BY * We can call DBMS_OUTPUT.PUT_LINE or DBMS_PIPE. Not sure about DBMS_SQL * Cannot apply PLSQL table methods [COUNT, FIRST, LAST, NEXT, PRIOR etc] in a stored function used in SQL. * The stored function may not call another module (stored procedure and function) that breaks any of the above rules. 17.5: Calling Packaged Functions in SQL: * Spec and body of a package are distinct. Spec must exist b4 the body has been defined. When a select statement calls a packaged function, only info available to it is the package spec. But, contents of package body determine whether that function is valid for execution in SQL. * Hence, we have to explicitly assert the purity level (the extent to which a function is free of side-effects) of a stored function, in a package spec. Oracle server can then determine whether the function violates that purity level when the package body is compiled. * Can assert the purity level of a function with the RESTRICT_REFERENCES pragma. 17.5.1: The RESTRICT_REFERENCES pragma: * Pragma is a special directive in PLSQL. * With RESTRICT_REFERENCES telling the compiler the purity level the function meets or exceeds. Seperate pragma statement for each function we wish to use in an SQL statement. must come after the function declaration in the package specification. PRAGMA RESTRICT_REFERENCES (function_name, WNDS [,RNDS] [,WNPS] [,RNPS]) WNDS: Writes no database state. Asserts that the function does not modify any database tables. RNDS: Reads no database state. Asserts that the function does not read any database tables.

WNPS: Writes no package state. Asserts that the function does not modify any package variables. RNPS: Reads no package state. Asserts that the function does not read any package variables. * Only the function_name and WNDS is mandatory in the pragma, the other arguments are optional and can appear in any order. * No one argument implies the other. I can write from the database without reading from it and can read from a package variable without writing into one. PACKAGE company_financials IS FUNCTION company_type (type_code_in IN VARCHAR2) RETURN VARCHAR2; FUNCTION company_name (company_id_in IN company.company_id%TYPE) RETURN VARCHAR2; PRAGMA RESTRICT_REFERENCES (company_type, WNDS, RNDS, WNPS, RNPS); PRAGMA RESTRICT_REFERENCES (company_name, WNDS, WNPS, RNPS); END company_financials; * If a function we want to call in SQL calls a procedure in a package, must also provide the RESTRICT_REFERENCES pragma for that procedure. Can't call that procedure directly in SQL, but even if it's indirectly called, it should follow the rules. Pragma violation errors: If your function violates it's pragma, will receive PLS-00452 Error. Ex: CREATE OR REPLACE PACKAGE BODY company_financials IS FUNCTION company_type (type_code_in IN VARCHAR2) RETURN VARCHAR2 IS v_sal employee.sal%TYPE := NULL; BEGIN SELECT sal INTO v_sal FROM employee WHERE employee_id = 1; RETURN 'something'; END; FUNCTION company_name (company_id_in IN company.company_id%TYPE) RETURN VARCHAR2 IS BEGIN UPDATE emp SET sal = 100000; RETURN 'something'; END company_name; END company_financials; On Compilation: PLS-00452 -- Sub program COMPANY_TYPE violates its associated pragma. PLS-00452 -- Sub program COMPANY_NAME violates its associated pragma. 17.5.2: Asserting purity level with the package initialization section. Syntax: PRAGMA RESTRICT_REFERENCES (package_name, WNDS [, RNDS] [, WNPS] [, RNPS]);

..). .7. FUNCTION salary RETURN NUMBER. thus drastically changing and sometimes crippling existing code. 17. If you want to override the column precedence. 17. END configure. ' || fname INTO user_name FROM user_table WHERE user_id = session_pkg.2: Read-Consistency Model complications: SQL queries with functions in them can violate the read-consistency model of Oracle RDBMS.6: Column/Function name precedence: If a function has the same name as a table column in the SELECT statement and it has no parameters. must qualify the name of the function with the name of the schema that owns the function. * Tuning mechanisms such as EXPLAIN PLAN do not take into account the SQL that may be called inside functions called in SQL statements. WNPS).. 17. PACKAGE BODY configure IS BEGIN SELECT lname || '. SELECT salary FROM employee. eventhough we write the user_name package variable we specify WNPS because.Here.. user_name VARCHAR2(100).. then the column always takes precedence over the function. PACKAGE configure IS PRAGMA RESTRICT_REFERENCES(configure. SELECT scott. * Some built-in PLSQL packages cannot be called in functions called from SQL. CREATE TABLE employee (employee_id.7: Realities: Calling PLSQL functions in SQL: Some disadvantages of calling functions in SQL: * manually apply RESTRICT_REFERENCES to the code.salary FROM employee.. WNDS.7.1: Manual Application of PRAGMAS: Cascading pragmas lead to more restrictions.user_id. * Overhead of calling a function from SQL remains high. SELECT name.. The select statement referring to salary will always refer to the column and not the function. 17. salary. user_name belongs to the same package and modifying the variables of the same package is not considered a side effect. END configure. * functions execute outside the read-consistency model of oracle. total_sales (account_id) FROM account . Here. we include the name of the package itself..

FETCH tot_cur INTO tot_rec. If the last payment date is not on the last day of the month. Now. somebody with proper authorization comes and deletes all the rows in the ORDERs table and commits the transaction. 'MMYYYY') || TO_CHAR(payment_date.8. Ex: SELECT DECODE(payment_date.1: Encapsulating calculations: * In applications. ADD_MONTHS(payment_date. may need to perform the same calculations again and again. by simply using ADD_MONTHS to get the next payment date. 'YYYY')).account_id%TYPE) RETURN NUMBER IS CURSOR tot_cur IS SELECT SUM(sales) total FROM orders WHERE account_id = id_in AND year = TO_NUMBER(TO_CHAR(SYSDATE. Order table: 20 million rows. * If these functions are called for long running queries or transactions. RETURN tot_rec. it will do the same for the rest of the time. session running the query should see all those deleted rows until the query completes. * According to read-consistency model of oracle. SET TRANSACTION READ ONLY 17. 1). tot_rec tot_cur%ROWTYPE. Will be a problem if significant alteration in business logic is needed. 1). we may need to probably issue the following command to enforce read consistency between SQL statements in the current The above query can be implemented using the following function: FUNCTION new_add_months (date_in IN DATE. Code redundancy occurs. it takes 1 hr and ends at 12. months_shift IN NUMBER) . END total_sales. Explanation: If the last payment date falls on the last day of the month. LAST_DAY(payment_date).WHERE status = 'ACTIVE'. LEAST(ADD_MONTHS(payment_date. 'DD'). FUNCTION total_sales (id_in account. at 11:45. 1)) FROM premium_payments. it finds no order rows and will return NULL. then get the next payment date. * If i start the query at 11. BEGIN OPEN tot_cur. but the next time the total_sales function executes from with-in the query. TO_DATE(TO_CHAR(ADD_MONTHS(payment_date. * Suppose: The account table has 5 million rows in it. then return as the next payment date the earliest of either the result of adding one month to payment date (using ADD_MONTHS) or the same day in the new month as the day in the month of the last payment date. * Hide/encapsulate all of your formulas and calculations into stored functions. 'MMYYYYDD' )) .8 Examples of embedded PL/SQL: 17.

salary..RETURN DATE IS return_value DATE. salary. END IF. dept_salary DS salary in each department. 'DD'). IF date_in = LAST_DAY(date_in) THEN return_value := LEAST(return_value.? GROUP BY . Now. salary FROM employee E1 WHERE salary = (SELECT MAX(salary) FROM employee E2 WHERE E1. total_salary FROM employee e.8. as well as . SELECT department_id. END new_add_months. SUM(salary) FROM employee ORDER BY department_id. BEGIN return_value := ADD_MONTHS(date_in. SUM(salary) total_salary FROM employee GROUP BY department_id.. TO_DATE(TO_CHAR(return_value. 1) FROM premium_payments 17. RETURN return_value. months_shift). CREATE VIEW dept_salary AS SELECT department_id.2: Combining Scalar and aggregate values: Question: Show me the name and the salary of the employee.? WHERE . SUM (salary) FROM .? Alternate solution: Create a view.. 'MMYYYY') || TO_CHAR(date_in. 'MMYYYYDD')). Using the view: SELECT department_id. SELECT new_add_months(payment_date. since that would require listing and obtaining both scalar and aggragate values from the same table. last_name...department_id) 2: SELECT department_id..department_id = E2. we can call this new stored function to obtain the next payment date easily. who has the highest total salary for that person's department. last_name. last_name. * Cannot very easily combine them. Two parts: 1: SELECT department_id.

BEGIN OPEN grp_cur.department_id = e. * Correlated sub-query is a very powerful feature of SQL. Instead of the view.salary%TYPE. since it offers the equivalent of a procedure language's nested loop capability. .department_id%TYPE) RETURN employee. total_salary(department_id) FROM employee WHERE salary = (SELECT MAX(salary) FROM employee e1 WHERE e1.department_id = E2.department_id) * For above implementation. we could use a function: FUNCTION total_salary (dept_id employee. we need to create customized view everytime we need this kind of an implementation. END. * Correlated Sub-queries: Is a SELECT statement inside the WHERE clause of a SQL statement [SELECT.department_id AND salary = (SELECT MAX(salary) FROM employee E1 WHERE e. CLOSE grp_cur. Ex: SELECT E. 17.3: Replacing Correlated sub-queries: * Can use stored functions in SQL statements to replace correlated subqueries. salary.department_id = E1.department_id) * The Inner query is executed once for every row of the outer query. FETCH grp_cur INTO return_value.salary%TYPE IS CURSOR grp_cur IS SELECT SUM(salary) INTO return_value FROM employee WHERE department_id = dept_id. last_name. salary. INSERT or DELETE] which is correlated or makes reference to one or more columns of the SQL statement.where e. RETURN return_value. lasty_name.8. total_salary(E.department_id) FROM employee E WHERE salary = (SELECT MAX(Salary) FROM employee E2 WHERE E.department_id). * 2 drawbacks of correlated sub-query: * Logic can become fairly complicated. END LOOP. now this can be called as SELECT department_id. return_value employee. LOOP LOOP END LOOP.department_id = DS.department_id.

stat_type_in IN VARCHAR2 ) RETURN NUMBER IS v_stat_type VARCHAR2(20) := UPPER(stat_type_in). IF v_stat_type = 'SUM' THEN ret_val := grp_rec. END. Usage of above function: SELECT E. CLOSE grp_cur. CURSOR grp_cur IS SELECT SUM(salary) sumsal. CLOSE grp_cur. Now the total_salary and max_salary functions are similar.department_id. FETCH grp_cur INTO grp_rec. * Can use stored functions in-place of the correlated sub-query: FUNCTION max_salary (dept_id_in IN department. ret_val NUMBER.department_id%TYPE. BEGIN OPEN grp_cur. Consolidating all variations into one function: FUNCTION salary_stat (dept_id_in department. MIN(salary) minsal. COUNT(DISTINCT salary) countsal FROM employee WHERE department_id = dept_id_in.* The resulting SQL statement can be difficult to understand and follow. last_name. ELSIF v_stat_type = 'MAX' . MAX(salary) maxsal. BEGIN OPEN grp_cur. FETCH grp_cur INTO return_value. AVG(salary) avgsal. EXCEPTION WHEN OTHERS THEN RETURN NULL. grp_rec grp_cur%ROWTYPE.sumsal. total_salary(department_id) FROM employee E where salary = max_salary(department_id). RETURN return_value. return_value NUMBER. salary.department_id%TYPE) RETURN NUMBER IS CURSOR grp_cur IS SELECT MAX(salary) FROM employee WHERE department_id = dept_id_in.

0) 0)) Q2_Results.THEN ret_val := grp_rec. salary_stat(department_id. 0. EXCEPTION WHEN OTHERS THEN RETURN NULL. 0)) Q1_Results. DECODE(LEAST(ship_date.q2_edate).q1_sdate). FC. ship_date.q3_sdate). 1.minsal. last_name.year_number. SUM(DECODE(GREATEST(ship_date. ELSE ret_val := NULL. salary. SUM(DECODE(GREATEST(ship_date.maxsal. ship_date. ship_date. adds to the count of rows which fulfill this requirement: SELECT FC. 'sum') FROM employee where salary = salary_stat(department_id. END IF. FC. FC. * Can use DECODE to perform complex IF THEN ELSE logic within a query.q3_edate). FC.4: Replacing DECODES with IF statements: * DECODE function offers IF-like capabilities in the nonprocedural SQL environment provided by oracle. ship_date. ELSIF v_stat_type = 'MIN' THEN ret_val := grp_rec.q1_edate). 0) . 17. FC. * Downside: Difficult to write and very difficult to maintain. ELSIF v_stat_type = 'AVG' THEN ret_val := grp_rec.q2_sdate). 1). RETURN ret_val. DECODE(LEAST(ship_date. FC. ship_date.countsal. END salary_stat.8. DECODE(LEAST(ship_date. ELSIF v_stat_type = 'COUNT' THEN ret_val := grp_rec.avgsal. 1. ship_date. SUM(DECODE(GREATEST(ship_date. * Below Query uses DECODE to determine whether a date is within the prescribed range and if it is. using above function: SELECT department_id. 'max').

sdate_in DATE. q4_sdate. jo_title T WHERE E. * Earnings of different types of vice-presidents or managers. SUM(incr_in_range(ship_date. DECODE(LEAST(ship_date.0)) Q3_Results.year_number. * Following query calculates the total amount paid to all workers with the same job description: SELECT job_title_desc. Depicting the following things using a query would be difficult: * Amount Of Salary for each type or category of Job title * Earnings of various kinds of clerks in the company.8. SUM(incr_in_range(ship_date. ship_date. fiscal_calendar FC Order By FC. SUM(DECODE(GREATEST(ship_date. q2_edate)). FC. q1_edate)). q3_sdate. edate_in DATE) RETURN NUMBER IS BEGIN IF ship_date BETWEEN sdate_in AND edate_in THEN RETURN 1.q4_edate). q2_sdate. SUM(incr_in_range(ship_date. EXCEPTION WHEN OTHERS THEN RETURN NULL.5: GROUP BY partial column values: * GROUP BY clause of a SELECT statement allows us to collect data across multiple records and group them by one or more columns.job_title_id = T. SUM(salary) FROM employee E. 1.year_number * Repetition of content in the above query needs modularization. FUNCTION incr_in_range (ship_date DATE. Fiscal_calendar FC Group By FC. FC.job_title_id Group BY job_title_desc. ELSE RETURN 0. 0) 0)) Q4_Results FROM Orders O. 17.q4_sdate). SUM(incr_in_range(ship_date. q4_edate)) FROM Orders O. .year_number. END. ship_date. END IF. Query using the above function: SELECT FC. q3_edate)). q1_sdate.

SUM(salary) FROM employee GROUP BY title.Using Stored Functions to deliver the above easily: FUNCTION job_category (title_id_in IN job_title.shows all lines of text containing 'the' SELECT text FROM notes WHERE INSTR(text. IF title_rec. 17. Some SQL examples: -. END IF. ELSIF title_rec.job_title_desc LIKE 'VICE PRESIDENT' RETURN 'VICE PRESIDENT'.6: Sequential processing against a column's value: In SQL: * Easy to find out if a particular word or set of characters appears in a string with the INSTR function. END. ELSIF title_rec. ELSE CLOSE title_cur. Using the above function. -.job_title_desc LIKE 'CLERK' THEN RETURN 'CLERK'. title_rec title_cur%ROWTYPE. the SQL query can be written as: SELECT job_category(title_id_in) title. FETCH title_cur INTO title_rec. IF title_cur%NOTFOUND THEN CLOSE title_cur.8. * Easy to determine which rows have a column that contains a certain word. EXCEPTION WHEN OTHERS THEN NULL. BEGIN OPEN title_cur.using INSTR to find all lines of text containing the word 'the' atleast 3 times . 'the') > 0. * Very difficult to find out the number of times a particular word or set of characters appear in a string in a single row.job_title_id%TYPE) RETURN VARCHAR2 IS CURSOR title_cur IS SELECT job_title_desc FROM job_title WHERE job_title_id = title_id_in. RETURN NULL. END IF.job_title_desc LIKE 'MANAGER' RETURN 'MANAGER'.

3) !=0 AND INSTR(text.8. 'DAY'). PACKAGE BODY checks IS v_date DATE := NULL. ps_parse. text_table text_table_type.number_of_atomics(notes. 'the'. 'urgent') urgent_count FROM notes WHERE urgent_count > 0. 17. 'the') FROM notes. .SELECT text FROM notes WHERE INSTR(text. END checks.number_of_atomics( the number of times urgent occurs in notes for each day. Using PLSQL functions: -. BEGIN l_amount := FLOOR(amount_in). 3) > 0 -. END IF. TYPE text_table_type IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER. 'the'. -. WNDS).show the number of occurances of the in the text. -. 1. 1. count of urgent should be atleast 1 SELECT TO_CHAR(note_date. 'the'. 4) = 0. FUNCTION num2words(amount_in IN NUMBER) RETURN VARCHAR2 IS l_amount NUMBER := 0. SELECT text.using INSTR to find all lines of text containing the word 'the' exactly 3 times SELECT text FROM notes WHERE INSTR(text.Not considering decimals IF l_amount >=1000 RETURN num2words(l_amount/1000) || ' Thousand ' || num2words(MOD(l_amount/1000)). 1.7: Recursive processing in a SQL statement: * SQL does not support recursion Below package contains function which prints amounts on cheques in written version PACKAGE checks IS FUNCTION num2words(amount_in IN NUMBER) RETURN VARCHAR2. PRAGMA RESTRICT_REFERENCES(num2words. ps_parse.

END. text_table (6) := 'Sixty'. END checks. Examples: checks. END LOOP. text_table (10) := NULL.num2words(99) := Ninety Nine checks. RETURN text_table(l_amount + 10). WHERE bill_status = 'UNPAID'. comment FROM bill. 'DDSP')). checks.populating multiples of 10 text_table (1) := 'Ten'. text_table (3) := 'Thirty'. IF l_amount >= 20 THEN RETURN text_table(FLOOR(l_amount/10)) || ' ' || num2words(MOD(l_amount/10)). -.IF l_amount >= 100 RETURN num2words(l_amount/100) || ' Hundred ' || num2words(MOD(1_amount/100)).num2words(amount). 'Month DD.num2words(12345) := Twelve Thousand Three Hundred Forty Five checks. text_table (i+10) := INITCAP(TO_CHAR (v_date. payee. text_table (4) := 'Forty'. END num2words.. BEGIN -. END IF. text_table (8) := 'Eighty'. text_table (2) := 'Twenty'. 19 LOOP v_date := TO_DATE(TO_CHAR(i)||'-JAN-2008'. DD-MM-YYYY). END IF.num2words(5) := Five Using the function in a SQL query: SELECT TO_CHAR(SYSDATE. . YYYY').populating values from 1 to 19 FOR i IN 1 . text_table (5) := 'Fifty'. amount. text_table (9) := 'Ninety'. text_table (7) := 'Seventy'.

Sign up to vote on this title
UsefulNot useful

Master Your Semester with Scribd & The New York Times

Special offer for students: Only $4.99/month.

Master Your Semester with a Special Offer from Scribd & The New York Times

Cancel anytime.