You are on page 1of 23

PL/SQL Coding Standard

PL/SQL CODING STANDARDS

{REVISION 1.0 / 28-FEB-2005}

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

TABLE OF CONTENTS

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

Purpose:
PL/SQL code must be maintainable and professional. This means that it must be consistent and therefore must abide by certain standards. The standards will ensure that our product will be useful long after the current people building and maintaining it are around.

Introduction:
This documents contains two parts: Part A: PL/SQL Coding Standards Part B: PL/SQL Structure

A. PL/SQL Standards General


1. All PL/SQL code must be well documented. We must write code that is
maintainable by others. Our motto is, "Document like you are trying to impress your favorite programming professor."

2. It is important to be consistent throughout an application as much as is


possible given the nature of team development. This means carrying style and other conventions such as naming within an application, not just within one file.

A.1. Coding Standards


1. Encapsulation of related functionality is key to making our software maintainable
and upgradeable. Try to bundle your code into packages whenever possible. This will make upgrading, bug fixing, customizing, and many other things, a possibility.

2. When creating functions or procedures, use the following template. It


demonstrates most of the guidelines set forth in this document that correspond to functions and procedures: CREATE OR REPLACE PROCEDURE/FUNCTION <proc_or_func_name> ( <param_1> IN/OUT/INOUT <datatype>, <param_2> IN/OUT/INOUT <datatype>, ... <param_n > IN/OUT/INOUT <datatype> ) [RETURN <datatype>] IS <local_var_1> <datatype> <local_var_2> <datatype> ... <local_var_n> <datatype> BEGIN ... END<proc_or_func_name>;

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard


/

3. All Key words should be in uppercase.


Ex: BEGIN, END, SELECT, INSERT, UPDATE, DELETE , CREATE OR REPLACE PROCEDURE, IN, OUT, INOUT IF, END IF etc.

4. Naming convention of Procedure and functions should be P_procedurename and


F_funtionname Ex: P_sitemaster_upd, F_worklists

5. All local variables defined in procedure should be l_variablename(lowercase)


Ex: l_itemname, l_locationname etc

6. All input and output parameters to procedure should be


I_inputparametername(lowercase) and o_outputparametername. Ex: i_planid, o_cumulativeyield etc

7. Code should be proper aligned. Left alignment for BEGIN EXCEPTION and END
block. Right alignment for SQL statement. Ex: BEGIN SELECT plan_id,eff_start_date,eff_end_date INTO l_plan_id,l_eff_start_date,l_eff_end_date FROM plan_master; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE_APPLICATION_ERROR(-errcode,errormessage); END;

8. There should be proper comments for each block of code. 9. Exception should handle for each sql statement.
Ex: BEGIN SELECT plan_id,eff_start_date,eff_end_date INTO l_plan_id,l_eff_start_date,l_eff_end_date FROM plan_master; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE_APPLICATION_ERROR(-errcode,errormessage); END;

10. Cursor name should be c_cursorname(lower case)


Ex: c_mst_location

11. Reference cursor name should be c_RefCursors.


Ex: c_RefCursors

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard


Ex: -- Ref Cursor Declaration Part TYPE c_RefCursors IS REF CURSOR; c_udp_engine c_RefCursors; c_null_calendars c_RefCursors;

12. Cursor record set name should be cursorname_rec(lowercase)


Ex: c_mst_location_rec Ex: FOR c_mst_location_rec IN c_mst_location LOOP BEGIN EXCEPTION END; END LOOP;

13. Naming convention of cursor parameter should be p_cur_paramname


Ex: p_cur_planid CURSOR c_udp_engine(p_cur_planid out_release_location.plan_id%TYPE) IS SELECT DISTINCT enginename, mp_engineid FROM udt_enginenmaster WHERE mp_planid=l_planid;

14. Use %TYPE and %ROWTYPE where ever possible. 15. Use standard error handling process to log all oracle errors. 16. User exception should be declared as e_exceptioname. 17. Always put WHEN OTHERS exception at end of each code block. 18. Use DBMS_OUTPUT.PUT_LINE ('sqltext- '||sqltext); for debug the code.

A.2. Coding Style


Some general style guidelines like alignment, spaces to be followed for the purpose of consistency across applications.

1. Standard indentation is four spaces. Our PL/SQL code is not only viewable in the
SQL files but also through our SQL and PL/SQL browsers. This means that we should try to make it as consistent as possible to all source code readers.

2. Lowercase everything (table name, field name, local variable name, parameters
name etc.) with the exception of %TYPE, %ROWTYPE and all KEY WORDS.

A.3. Constraint Naming Standards


A constraint naming standard is important for one primary reason: The SYS_* name Oracle assigns to unnamed constraints is not very understandable. By correctly naming all

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard


constraints, we can quickly associate a particular constraint with our data model. This gives us two real advantages:

We can quickly identify and fix any errors We can reliably modify or drop constraints
Why do we need a naming convention? Oracle limits names, in general, to 30 characters, which is hardly enough for a human-readable constraint name.

A.3.1. Abbreviations
We propose the following naming convention for all constraints, with the following abbreviations taken from the Oracle documentation. Note that we shortened all of the constraint abbreviations to two characters to save room. Constraint type references (foreign key) unique primary key check Not null Abbreviation Fk Un Pk ck nn

A.3.2. Format of Constraint Name


<table name>_<column_name>_<constraint abbreviation> In reality, this won't be possible because of the character limitation on names inside Oracle. When the name is too long, we will follow these steps in order: Abbreviate the table name with the table's initials (for example, users -> u and users_contact -> uc). Truncate the column name until it fits. create table example_topics ( topic_id integer constraint example_topics_topic_id_pk primary key ); create table constraint_naming_example ( example_id integer constraint cne_example_id_pk primary key, one_line_description varchar(100) constraint cne_one_line_desc_nn not null,

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard


body clob, up_to_date_p char(1) default('t') constraint cne_up_to_date_p_check check(up_to_date_p in ('t','f')), topic_id constraint cne_topic_id_nn not null constraint cne_topic_id_fk references example_topics, -- Define table level constraint constraint cne_example_id_one_line_unq unique(example_id, one_line_description) );

Example B-1. Example of Constraint Name


Note If you have to abbreviate the table name for one of the constraints, abbreviate it for all the constraints. If you are defining a multicolumn constraint, try to truncate the two column names evenly.

A.3.3. Naming primary

keys

Naming primary keys might not have any obvious advantages. However, in Example B-2, the primary key helps make the SQL query clearer. SQL> set autotrace traceonly explain; SQL> select * from constraint_naming_example, example_topics where constraint_naming_example.topic_id = example_topics.topic_id; Execution Plan ---------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE 1 0 NESTED LOOPS 2 1 TABLE ACCESS (FULL) OF 'CONSTRAINT_NAMING_EXAMPLE' 3 1 INDEX (UNIQUE SCAN) OF 'EXAMPLE_TOPICS_TOPIC_ID_PK' (UNIQUE)

Example B-2. Primary Key Naming


Being able to see EXAMPLE_TOPICS_TOPIC_ID_PK in the trace helps us to know exactly which table Oracle is querying. If we had not named the constraints, the execution plan would look like: Execution Plan ---------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE 1 0 NESTED LOOPS 2 1 TABLE ACCESS (FULL) OF 'CONSTRAINT_NAMING_EXAMPLE' 3 1 INDEX (UNIQUE SCAN) OF 'SYS_C00140971' (UNIQUE)

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

The SYS_C00140971 by itself provides no information as to which index is being used in this query, and more importantly, the name of this constraint will vary from database to database. Mark Lindsey (<lindsey@acm.org>) provided another good reason to name primary keys and unique constraints. Oracle creates an index for every primary key and unique constraint with the same name as the constraint. It is an unfortunate DBA who has to wrestle with storage management of tens of mysteriously-named indexes.

A.3.4. Naming not

null

Constraints is Optional.

Red Hat Applications developers are undecided on whether or not to name not null constraints. If you want to name them, please do so and follow the above naming standard. Currently, naming not null constraints is not a requirement of WAF.

Note
Naming the not null constraints does not help immediately in error debugging (for example the error will say something like Cannot insert null value into column). We do recommend naming not null constraints to be consistent in our naming of all constraints.

A.3.5. Upgrade Scripts


Data model upgrade scripts are a crucial part of database-based applications. Standards help to ensure that upgrade scripts behave correctly and consistently. This helps save time in development, maintenance, and support.

Tip
An upgrade script should be written anytime that a change is made to a component's sql creation script that results in a different schema and/or data set. The new upgrade script should be written so that, when applied to the previous creation script, it results in the same schema and data set. Every component should have exactly zero or one upgrade script per release per supported database. However, an upgrade script may source other files. Upgrade scripts go in an upgrade/ directory below the directory containing the corresponding creation script. The name of the file should be < component-name>-<oldversion-name>-<new-version-name>.sql. Example:

Cms/sql/oracle-se/upgrade/cms-4.6.4-4.6.5.sql
Extending this process farther, there should be a single upgrade script for all of WAF per version. This upgrade script lives in kernel/sql/oracle-se/upgrade and is called coreplatform-<old-version-name> -<new-version-name>.sql.

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard


Similar to core-platform-create.sql, this script will not have its own SQL commands, but will simply source other files. Updating this script is the responsibility of each component developer.

A.4. Sample Procedure (Contains Cursor, Dynamic Cursor)

CREATE OR REPLACE PROCEDURE P_upd_rwc_ods_sitemaster(i_workflow_id IN INTEGER,i_odsschema IN VARCHAR2) AS

-- In order to use workflow list function TYPE c_RefCursors IS REF CURSOR; c_udp_engine c_RefCursors; c_null_calendars c_RefCursors;

--- Local variable declaration part -l_workflowlist VARCHAR2(1000) := ''; l_sqltext VARCHAR2(300); l_enginename varchar2(40); l_mp_engineid varchar2(40); l_release_calname l_planname VARCHAR2(200);

VARCHAR2(40); VARCHAR2(40); DATE;

l_attributevalue l_effstartdate l_effenddate

DATE;

l_startdate DATE := sysdate; l_jobname Varchar2(40) := 'Release Calendar Updation';

l_standardflow INTEGER := 1;

-- Exceptions

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

10

e_workflow_id_error Exception; -- Error Statements And Codes e_workflow_id VARCHAR2(1000) := 'Erroneous Workflow Identifier , Please Check the Input Id : '; e_update_error VARCHAR2(1000) := 'No Update Value Clause , Invalid query .. exiting'; e_lookup_error VARCHAR2(1000) := 'No Destination Look Up Field Specified , Please check Input Parameters'; --- cursor declaration part -CURSOR c_mst_location IS SELECT DISTINCT plan_id, location_id, ud_release_time_monday, ud_release_time_tuesday, ud_release_time_wednesday, ud_release_time_Thursday, ud_release_time_friday FROM out_location_release@MDM WHERE location_id IS NOT NULL

AND sys_ent_state='ACTIVE'; BEGIN -- Check the workflows For which the update needs to run - Applicable only in case l_workflowlist := NVL(F_workflowlist(i_workflow_id),'Error'); -- If the Workflow Id was Incorrect Raise An error IF l_workflowlist = 'Error' THEN

RAISE e_workflow_id_Error; END IF; -- Set All engines valid for Workflow to Null as part of deletion -- OPEN dynamic Cursor c_null_calendars OPEN c_null_calendars FOR 'SELECT DISTINCT enginename FROM UDT_ENGINEMASTER WHERE workflowid in (' || l_workflowlist || ')'; LOOP FETCH c_null_calendars INTO l_enginename; BEGIN L_sqltext:='UPDATE ' || i_odsschema || '.v_sitemaster SET UDF_RELEASE_CALENDAR_'|| l_enginename ||'= NULL'; EXECUTE IMMEDIATE l_sqltext;

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

11

EXCEPTION WHEN OTHERS THEN LOG_STANDARD_ERROR('v_sitemaster','SiteMaster Updation Failed '|| sqlerrm,l_enginename,l_jobname,1,'S3',l_startdate);

END; l_enginename:=NULL; EXIT WHEN c_null_calendars%NOTFOUND; END LOOP; COMMIT; l_enginename:=NULL; l_planname:=NULL; FOR c_mst_location_rec IN c_mst_location LOOP -- OPEN dynamic Cursor c_udp_engine OPEN c_udp_engine FOR 'SELECT DISTINCT enginename,mp_engineid,mp_planid FROM udt_enginemaster WHERE mp_planid='||''''|| c_mst_location_rec.plan_id ||''''||' and workflowid in (' || l_workflowlist || ')'; LOOP FETCH c_udp_engine INTO l_enginename,l_mp_engineid,l_planname; BEGIN EXECUTE IMMEDIATE 'SELECT a.plan_id,b.currentdate,b.effenddate FROM out_planmaster@MDM a,'|| i_odsschema ||'.planmaster b where a.plan_id=''' || l_planname ||''' AND a.plan_id=b.planid ' INTO l_planname,l_effstartdate,l_effenddate; EXCEPTION WHEN OTHERS THEN LOG_STANDARD_ERROR('OUT_PLANMASTER AND PLANMASTER','Plan Name retrival failed'|| sqlerrm, c_mst_location_rec.plan_id, l_jobname, 1, 'S3', l_startdate); END; l_release_calname:='RWC'||'.'||c_mst_location_rec.location_id||'.'|| l_planname; BEGIN l_sqltext:= 'UPDATE '||i_odsschema||'.V_SITEMASTER SET UDF_RELEASE_CALENDAR_'||l_enginename||' ='''||l_release_calname||''' WHERE SITEID='''|| c_mst_location_rec.location_id||'''';

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

12

DBMS_OUTPUT.PUT_LINE ('l_sqltext- '||l_sqltext); EXECUTE IMMEDIATE sqltext; EXCEPTION WHEN OTHERS THEN LOG_STANDARD_ERROR('SITEMASTER','SITEMASTER RWC updation failed'|| sqlerrm,l_release_calname,l_jobname,1,'S3',l_startdate); END; COMMIT; EXIT WHEN c_udp_engine%NOTFOUND; END LOOP; l_enginename:=NULL; COMMIT; END LOOP; EXCEPTION WHEN e_workflow_id_Error THEN RAISE_APPLICATION_ERROR(-20001,'Unknown Error Occurred : ' || sqlerrm); ROLLBACK; WHEN OTHERS THEN ROLLBACK; RAISE_APPLICATION_ERROR(-20001,'Unknown Error Occurred : ' || sqlerrm); END; /

B.PL/SQL Structure
This contains details of PL/SQL structures and definitions.

B.1 Headers
B.1.1 Create Command in PL/SQL
The first part of any stored procedure, package, package body, or function in PL/SQL is the CREATE command. The CREATE command should use the OR REPLACE clause to ensure that new updates write over old code. You should place the variable declarations in the CREATE command on separate lines for readability, as well as the AS keyword. All IN only variables should come first, followed by the IN OUT or OUT variables. For anonymous blocks, the CREATE command is replaced with the DECLARE keyword

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

13

B.1.2 Special Case: CREATE PACKAGE Commands


A special case is the CREATE PACKAGE command. The CREATE PACKAGE command actually defines the package specification, or application programming interface (API), of a PL/SQL package of functions and procedures. A good example of a useable code guideline for this command structure is shown in the DBMS*.SQL procedures provided by Oracle Corp. Listing 3 shows an example of the CREATE PACKAGE format (note that all of the package header is shown -- only enough to illustrate the format is shown). Notice that each PROCEDURE declaration is properly indented and a blank line is inserted between each declaration section. Also note the use of comments to provide online documentation. Other possible declaration statements are FUNCTION, CURSOR, and EXCEPTION. Before the first PROCEDURE declaration are the global variable declarations. Global variables in the package specification can be used by any program, including those defined later in the package body. Global constants can also be declared in this fashion. Listing 4 shows other declarations for the CREATE PACKAGE command. Note the use of the RETURN clause on the FUNCTION declaration in Listing 4; the EXCEPTION keyword follows the name of the exception. Generally you should list procedures and functions in order of dependency, followed by cursors, variables, and exceptions. Use comments freely to ensure that future users will be able to understand how each item should be used. For the package body, the command is: CREATE OR REPLACE PACKAGE BODY name AS followed by the code for the various package objects. The object code is identical to the code that you would use in a CREATE OR REPLACE script, except that the CREATE OR REPLACE command is not needed for individual package objects. Remember that the public package object definitions must exactly match their declarations in the package specification, and the developer can add additional private package objects.

B.2 Cursor Declaration in PL/SQL


Following the header in PL/SQL will be the DECLARE for anonymous blocks or the CREATE OR REPLACE command, the in/out variable declaration, and then the cursor declaration. Listing 5 shows an example cursor definition. The format shown in Listing 5 is easy to read and allows a quick scan of required inputs or outputs.

B.2.1 Variable Definitions in PL/SQL


After the cursor definitions in PL/SQL should come the variable definitions -- ROWTYPE, TYPE, TABLE, and RECORD -- followed by local definition variables. These definitions should be tab-separated for readability. Listing 6 shows some example variable declarations. On some platforms, tab separations may result in readability problems if the code is transferred to a different platform with different editor characteristics. If you will be using the code on multiple machines or under differing editors, use a fixed-width font and space separation. Comments should also be added to the code to ensure understanding of the use of the variables.

B.3 Body Definitions in PL/SQL


The body of the PL/SQL procedure consists of several possible constructs such as loops, selects, begin-end blocks, and exceptions. These constructs should use tab-indenting (if they are small) or space-indenting (if they are large) to promote readability. Clauses in SELECT, INSERT, or UPDATE commands should be placed on separate lines and slightly indented under the parent command. All loop contents should be indented inside of each loop construct. All block contents should be indented inside each BEGIN-END pair.

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

14

Procedure lengths should be set at a predetermined standard based on ensuring that developers modularize their code. Procedures much longer than 100 lines become hard to read. This is accomplished through compartmentalization of code into subprocedures and functions and through the use of cursors for multiple SELECT statements. A line of code is considered to be a single command, so even though a large SELECT, INSERT, or UPDATE type command spans several physical lines, it is only one line of code. For example, if the procedure in Listing 7 has been built in your schema or if you have execute privilege on it, the procedure can be called from other procedures to provide insert processing into i_temp: By using subprocedures you can reduce the space requirements in your procedures. There should be comments added into the processing body to show functions and explain special processing (if needed). See Listing 8 on the OReview Web site (www.oreview.com) for a code fragment using these techniques.

B.4 Exceptions in PL/SQL


Exceptions usually come at the end of the PL/SQL construction or at the end of a BEGINEND construct. The contents of the exception clause should be indented beneath the EXCEPTION keyword and should follow the other indention rules. Listing 9 shows an example of an exception statement. So far I have covered all of the pieces and their formats for a PL/SQL script. Let's look at a complete example. Listing 10 shows how all of these elements combine to make a very easily readable and thus maintainable piece of PL/SQL code. Each shop should determine its desired PL/SQL coding style and publish a code guidebook for use by its developers. The code guidebook should delineate capitalization rules, indentation rules, and naming standards. Developing without a code guidebook can lead to results that vary wildly from one developer to another in terms of readability and maintainability. Poorly formatted code will increase maintenance time and thus maintenance cost.

Listing 1. Example format for a PL/SQL header or footer. -- - Title: (name of script) - - Used by Application: (application the - - procedure/function/package belongs to) - - Purpose: (general purpose of script - keep it short) - - Limitations: (any prerequisites, privileges, grants, - - etc.) -- - Inputs: (list required inputs to procedure, - - function, etc.) - - Outputs: (list any output variables and their type) -- - History: - - Who: What: Date: - - (list changes to file) - - Notes: - - (List any special notes applicable to procedure, - - function, etc.)

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

15

Listing 2. Example format for a CREATE or DECLARE command.


*Header* CREATE OR REPLACE PROCEDURE AddRmaNote ( hv_eventid IN event.evntid %TYPE, hv_actseqnbr IN act.actseqnbr%TYPE, hv_user IN indiv.cuidnbr %TYPE, hv_notes IN VARCHAR2, hv_status IN OUT NUMBER ) AS - - OR - DECLARE variable, constant, cursor or sub-program declarations

Listing 3. Example of the CREATE PACKAGE command.


*Header* CREATE OR REPLACE PACKAGE dbms_refresh IS -- dbms_refresh is the interface for administering refresh groups. -- PARAMETERS: -- NAME is of the form 'foo' or 'user.foo' or 'USER'.'FOO'. -- The logged-in user is used as a default. -- LIST is a comma-separated list of objects to be refreshed, such as -- 'foo, scott.bar ,'SCOTT'.'BLUE'. The default user is the owner -- of the refresh group. -- TAB is a PL/SQL table of objects to be refreshed, starting with 1 -- and filling every number until an entry is NULL, with every entry -- formatted the same way as NAME. The default user is the owner -- of the refresh group. -- NEXT_DATE is the date for the refresh group to first be refreshed. -- See dbmsjobq.sql. If there is no current job, the default interval -- will be 'null' and the job will delete itself after refreshing the -- group at NEXT_DATE. -- INTERVAL is used to determine the next NEXT_DATE. See dbmsjobq.sql. -- If there is no current job, NEXT_DATE will default to null and the -- job will not run until you manually set NEXT_DATE to something else -- or manually refresh the group. -- IMPLICIT_DESTROY means to delete the refresh group when the last item -- is subtracted from it. The value is stored with the group definition. -- Empty groups can be created with IMPLICIT_DESTROY set. aaspriv BINARY_INTEGER; -- Privilege number for ALTER ANY SNAPSHOT

PROCEDURE subtract( name IN VARCHAR2, list IN VARCHAR2, lax IN BOOLEAN DEFAULT FALSE ); PROCEDURE subtract( name IN VARCHAR2, tab IN dbms_utility.uncl_array, lax IN BOOLEAN DEFAULT FALSE ); -- SUBTRACT some refreshable objects from a refresh group. PROCEDURE user_export_child( myowner IN VARCHAR2, myname IN VARCHAR2, mytype IN VARCHAR2, mycall IN OUT VARCHAR2);

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard


-- Produce the text of a call for recreating the given group item END dbms_refresh;

16

Listing 4. Other possible declarations with CREATE PACKAGE. CREATE PACKAGE admin.employee_package AS -- employee package contains objects for the HR application PROCEDURE new_emp( ename IN VARCHAR2, position IN VARCHAR2, supervisor IN NUMBER, category IN NUMBER, hiredate IN DATE, emp_no OUT NUMBER); -- The procedure new_emp adds a new employee and returns their emp_no value. PROCEDURE fire_them( emp_no IN NUMBER, reason IN VARCHAR2, term_date IN OUT DATE); -- The procedure fire_them removes an employee and returns their termination date PROCEDURE new_dept( emp_no IN NUMBER, dept IN VARCHAR2, new_dept IN VARCHAR2, date_of_change IN OUT DATE); -- The procedure new_dept changes an employees department and returns the date of change. FUNCTION get_status ( emp_no IN NUMBER) RETURN VARCHAR2; -- The function get_status takes in the employee number and returns their status. CURSOR c_get_emp ( emp_num NUMBER) IS SELECT ename, dept, status, hiredate, supervisor FROM emp WHERE emp_no=emp_num; -- The CURSOR c_get_emp is a general purpose cursor that returns all columns for a -- specified employee number entry. bad_category EXCEPTION; bad_date EXCEPTION; END employee_package;

Listing 5. Example cursor declaration format.

*Header* *Create or Declare* CURSOR get_one IS(cursor definition); CURSOR get_two IS(cursor definition); The cursor definition should follow this general structure: CURSOR (cursor name) (parameter list) IS SELECT (list of columns) FROM (list of tables WHERE (list of clauses)

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

17

ORDER BY (list of columns) FOR UPDATE OF (Other clauses) (list of tables, columns, etc.) ; Other clauses should be indented in a similar manner. Remember, the objective is to make the code easily readable. For example: CURSOR c_cons_cursor IS SELECT owner, constraint_name, decode(constraint_type,'C','CK_','V','DF_'), table_name, search_condition, r_owner, r_constraint_name, delete_rule FROM user_constraints WHERE owner NOT IN ('SYS','SYSTEM') AND constraint_type IN ('C','V') ORDER BY owner, constraint_type ;

Listing 6. Example variable declarations.

*Header* *Create or Declare* *Cursors* - - Table Definitions -TYPE numtab IS TABLE OF NUMBER INDEX BY BINARY_INTEGER; tab_counts numtab; user_counts numtab; -- - Record Definitions -TYPE UserRecType IS RECORD (userno NUMBER(2), dname CHAR(14), loc CHAR(23)); user_rec UserRecType; -- - Rowtype Definitions -table_rec dba_tables%ROWTYPE -- - Type Definitions -tab_nam user_constraints.table_name%TYPE; cons_owner user_constraints.owner%TYPE;

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

18

cons_name user_constraints.constraint_name%TYPE; -- - Local Variable Definitions -cons_type varchar2(11); all_columns varchar2(2000); counter integer:=0; cons_nbr integer;

Listing 7. Example of a sub-procedure. CREATE OR REPLACE PROCEDURE write_ind( p_line INTEGER, p_owner varchar2, p_name VARCHAR2, p_string VARCHAR2) IS BEGIN INSERT INTO i_temp ( lineno, id_owner, id_name, text) VALUES ( p_line, p_owner, p_name, p_string); END; Listing 8. Example code fragment using indentation, commenting and capitalization rules.

*Header* *Create or Declare* *Cursors* *Declarations* -- - Create a command set to rebuild a CREATE DATABASE command -BEGIN db_lineno:=db_lineno+1; SELECT 'CREATE DATABASE ' | | value into db_string FROM v$parameter WHERE name='db_name'; write_ind(db_lineno,db_string); <--- Note use of sub-program here db_lineno:=db_lineno+1; -- - Select the following command from dual since it isn't stored -SELECT 'CONTROLFILE REUSE' into db_string

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

19

FROM dual; write_ind(db_lineno,db_string); db_lineno:=db_lineno+1; -- - Select the following command from dual since it isn't stored -SELECT 'LOGFILE (' into db_string FROM dual; write_ind(db_lineno,db_string); COMMIT; -- - Get redo log thread information -IF thread_cursor%ISOPEN THEN CLOSE thread_cursor; OPEN thread_cursor; ELSE OPEN thread_cursor; END IF; LOOP FETCH thread_cursor INTO thrd,grp; EXIT WHEN thread_cursor%NOTFOUND; db_lineno:=db_lineno+1; db_string:= 'THREAD '||thrd||' GROUP '||grp||' ('; write_ind(db_lineno,db_string); -- - Get Thread, group, member information -IF mem_cursor%ISOPEN THEN CLOSE mem_cursor; OPEN mem_cursor(grp); ELSE OPEN mem_cursor(grp); END IF; db_lineno:=db_lineno+1; begin_count:=db_lineno; LOOP FETCH mem_cursor INTO grp_member; EXIT WHEN mem_cursor%NOTFOUND; IF begin_count=db_lineno THEN db_string:=''''| | grp_member | |''''; write_ind(db_lineno,db_string); db_lineno:=db_lineno+1; ELSE db_string:=','| |'''| | grp_member| |'''; write_ind(db_lineno,db_string); db_lineno:=db_lineno+1;

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

20

END IF; END LOOP; db_lineno:=db_lineno+1; db_string:=' )'; write_ind(db_lineno,db_string); END LOOP; db_lineno:=db_lineno+1; SELECT ')' INTO db_string FROM dual ; write_ind(db_lineno,db_string); COMMIT; -- - Get datafile information for the SYSTEM tablespace -IF dbf_cursor%ISOPEN THEN CLOSE dbf_cursor; OPEN dbf_cursor; ELSE OPEN dbf_cursor; END IF; begin_count:=db_lineno; LOOP FETCH dbf_cursor INTO filename, sz; EXIT WHEN dbf_cursor%NOTFOUND; IF begin_count=db_lineno THEN db_string:='DATAFILE '| |''''| | filename| |''''| |' SIZE '| |sz| |' REUSE'; ELSE db_string:=','| |''''| |filename| |''''| |' SIZE '| |sz| |' REUSE'; END IF; db_lineno:=db_lineno+1; write_ind(db_lineno,db_string); END LOOP; COMMIT; -- - Archive log data is stored as either a true or false setting, so we must decode -SELECT decode(value,'TRUE','ARCHIVELOG','FALSE','NOARCHIVELOG') INTO db_string FROM v$parameter WHERE name='log_archive_start'; db_lineno:=db_lineno+1; write_ind(db_lineno,db_string); SELECT

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

21

';' INTO db_string FROM dual; db_lineno:=db_lineno+1; write_ind(db_lineno,db_string); CLOSE dbf_cursor; CLOSE mem_cursor; CLOSE thread_cursor; COMMIT; END;

Listing 9. Example of the format for an EXCEPTION statement. *Header* *Create or Declare* *Cursors* *Declarations* *Body* EXCEPTION WHEN NO_DATA_FOUND THEN INSERT INTO dba_temp VALUES ( stat_name,0); COMMIT; WHEN ZERO_DIVIDE THEN INSERT INTO dba_temp VALUES ( stat_name,0); COMMIT; Listing 10. Full-length code example.

Title: AddRmaNote Used by Application: RMA GUI Purpose: Add RMA notes Limitations: None Inputs: eventid, actseqnbr, user, notes Outputs: status: 1 No note added, 0 note added History:

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

22

- - Who: What: Date: - - Mike Ault Added Header 12/16/96 - - Notes: - - None -CREATE OR REPLACE PROCEDURE AddRmaNote ( hv_eventid IN event.evntid%TYPE, hv_actseqnbr IN act.actseqnbr%TYPE, hv_user IN indiv.cuidnbr%TYPE, hv_notes IN VARCHAR2, hv_status IN OUT NUMBER ) AS -- - Local Variables --

-- - Body --

leid date1

NUMBER(10); DATE;

BEGIN hv_status := 0; leid := 0; BEGIN -- - Get the foreign key value of the leid from the - - indiv table for this user -SELECT fk_leleid INTO leid FROM indiv WHERE cuidnbr = hv_user ; EXCEPTION WHEN OTHERS THEN - If no entry then user doesn't have the leid value - required and no note will be added to RMA return a 1 - status hv_status := 1; END; - Get SYSDATE, note, an error of 1427 may indicate - multiple rows in dual SELECT sysdate INTO date1 FROM dual; -

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

PL/SQL Coding Standard

23

- - A leid number greater than zero indicates we need to - - add to the activity remark table (add an RMA note) -IF ( leid > 0 ) THEN INSERT INTO actvy_rmk ( actvyrmkdt, actvyrmktxt, fk_actfk_eventevnt, fk_actactseqnbr, fk_indivfk_leleid ) VALUES ( date1, hv_notes, hv_eventid, hv_actseqnbr, leid ); END IF; COMMIT; END AddRmaNote;

2001 i2 Technologies, PROPRIETARY and CONFIDENTIAL

28/02/2005

You might also like