Step 1:
CREATE TABLE SV_EMP_PHOTO
(
ID NUMBER(3) NOT NULL,
PHOTO_NAME VARCHAR2(40),
PHOTO_RAW BLOB,
EMP_NAME VARCHAR2(80)
)
Step2:
Create a directory where the photos will be stored. I am creating a directory in UNIX as our database is created in UNIX.
CREATE OR REPLACE DIRECTORY
SV_PHOTO_DIR AS
'/usr/tmp/SV_PHOTO_DIR';
Step 3:
Copy the image file from local machine to the above created Directory using FTP.
Kindly note that Report 6i does not support to all image formats. I used here .bmp file format.
Step4:
Script to create a procedure SV_LOAD_IMAGE that will insert records in the table.
/* Formatted on 2009/11/30 11:12 (Formatter Plus v4.8.8) */
CREATE OR REPLACE PROCEDURE apps.sv_load_image (
p_id NUMBER,
p_emp_name IN VARCHAR2,
p_photo_name IN VARCHAR2
)
IS
l_source BFILE;
l_dest BLOB;
l_length BINARY_INTEGER;
BEGIN
l_source := BFILENAME ('SV_PHOTO_DIR', p_photo_name);
INSERT INTO sv_emp_photo
(ID, photo_name, emp_name, photo_raw
)
VALUES (p_id, p_photo_name, p_emp_name, EMPTY_BLOB ()
)
RETURNING photo_raw
INTO l_dest;
-- lock record
SELECT photo_raw
INTO l_dest
FROM sv_emp_photo
WHERE ID = p_id AND photo_name = p_photo_name
FOR UPDATE;
-- open the file
DBMS_LOB.fileopen (l_source, DBMS_LOB.file_readonly);
-- get length
l_length := DBMS_LOB.getlength (l_source);
-- read the file and store in the destination
DBMS_LOB.loadfromfile (l_dest, l_source, l_length);
-- update the blob field with destination
UPDATE sv_emp_photo
SET photo_raw = l_dest
WHERE ID = p_id AND photo_name = p_photo_name;
-- close file
DBMS_LOB.fileclose (l_source);
END sv_load_image;
Step5:
exec sv_load_image(1,'Mr. Kamlesh Nikhade','Kamlesh.bmp');
Step6 :
select * from SV_EMP_PHOTO;
Display Image In report 6i
We will use same SV_EMP_TABLE as a reference and using Reports 6i create a report that prints ID Card. For simplicity there are no parameters to the report and it will create ID cards for all the employees for which record exists in the table.
In the data model create following SQL Query.
Step 1 :
SELECT ID, PHOTO_NAME, PHOTO_RAW ,emp_name
FROM SV_EMP_PHOTO;
Step2:
The next step is to create a layout for the report. Screenshot below.
The field that stores the value of BLOB column in the properties for the field remember to change the file format to image,
This image data will be retrieved from database and will change with record.
Procedure : Take One text field=>properties=>file format=>image.
Step 3:
Compile & Run the report.
Sunday, November 29, 2009
Sunday, November 22, 2009
Concurrent Program Registration Scripts
Concurrent Program Registration Scripts ==========================================================
The scripts in this article can be used to:
1) Register the executable and Program
2) Attach Concurrent program to a Request Group
3) Submit Concurrent program
1) Registering the Executable from back end
================================================
Usually we create executable in the front-end, but this can be done from the database tier i.e. back-end too.
Below is the PL/SQL code to create an executable from back-end.
BEGIN
FND_PROGRAM.executable('XXMZ_ERPSCHOOLS_EMPLOYEE' -- executable
, 'XXMZ Custom' -- application
, 'XXMZ_ERPSCHOOLS_EMPLOYEE' -- short_name
, 'Executable for ERPSCHOOLS Employee INFORMATION' -- description
, 'PL/SQL Stored Procedure' -- execution_method
, 'XXMZ_ERPSCHOOLS_EMPLOYEE' -- execution_file_name
, '' -- subroutine_name
, '' -- Execution File Path
, 'US' -- language_code
, '');
COMMIT;
END;
Query in the front-end to see whether your executable is created or not.
2) Registering the Concurrent program from back end
===========================================================
Usually we create Concurrent program in the front-end, but this can be done from the database tier too.
Below is the program to create a Concurrent program from back-end.
BEGIN
FND_PROGRAM.register('Concurrent program for ErpSchools Employee Information' -- program
, 'XXMZ Custom' -- application
, 'Y' -- enabled
, 'XXMZ_ERPSCHOOLS_EMPLOYEE' -- short_name
, 'ErpSchools Employee Information' -- description
, 'XXMZ_ERPSCHOOLS_EMPLOYEE' -- executable_short_name
, 'XXMZ Custom' -- executable_application
, '' -- execution_options
, '' -- priority
, 'Y' -- save_output
, 'Y' -- print
, '' -- cols
, '' -- rows
, '' -- style
, 'N' -- style_required
, '' -- printer
, '' -- request_type
, '' -- request_type_application
, 'Y' -- use_in_srs
, 'N' -- allow_disabled_values
, 'N' -- run_alone
, 'TEXT' – output_type
, 'N' -- enable_trace
, 'Y' -- restart
, 'Y' -- nls_compliant
, '' -- icon_name
, 'US'); -- language_code
COMMIT;
END;
Query in the front-end to see whether your Concurrent program is created or not.
3) Attaching the concurrent program to the request group
==================================================================
Usually we Attach Concurrent program to the request group in the front-end, but this can be done from database tier too.
Below is the program to Attach Concurrent program to the request group from back-end.
BEGIN
FND_PROGRAM.add_to_group('XXMZ_ERPSCHOOLS_EMPLOYEE' -- program_short_name
, 'XXMZ Custom' -- application
, 'xxmz Request Group' -- Report Group Name
, 'XXMZ'); -- Report Group Application
COMMIT;
END;
Query in the front-end to see whether your Concurrent program is Attached to Request Group or not.
4) Submitting Concurrent Program from Back-end
====================================================
We first need to initialize oracle applications session using
fnd_global.apps_initialize(user_id,responsibility_id,application_responsibility_id) and then run fnd_request.submit_request
DECLARE
l_request_id NUMBER(30);
begin
FND_GLOBAL.APPS_INITIALIZE (user_id => 1318, resp_id => 59966, resp_appl_id => 20064);
l_request_id:= FND_REQUEST.SUBMIT_REQUEST ('XXMZ' --Application Short name,
'VENDOR_FORM'-- Concurrent Program Short Name );
DBMS_OUTPUT.PUT_LINE(l_request_id);
commit;
end;
*****************************************************
To get the resp_id and resp_appl_id use the below queries.
--SELECT APPLICATION_ID, RESPONSIBILITY_ID
FROM FND_RESPONSIBILITY_TL
WHERE RESPONSIBILITY_NAME='xxmz Custom'
--SELECT USER_ID FROM FND_USER WHERE USER_NAME='OPERATIONS'
*****************************************************
Once the concurrent program is submitted from back-end, status of the concurrent program can be checked using below query.
SELECT * FROM FND_CONCURRENT_REQUESTS WHERE REQUEST_ID=2766602
==============================================================================
This document is from ERP SCHOOLS
==============================================================================
The scripts in this article can be used to:
1) Register the executable and Program
2) Attach Concurrent program to a Request Group
3) Submit Concurrent program
1) Registering the Executable from back end
================================================
Usually we create executable in the front-end, but this can be done from the database tier i.e. back-end too.
Below is the PL/SQL code to create an executable from back-end.
BEGIN
FND_PROGRAM.executable('XXMZ_ERPSCHOOLS_EMPLOYEE' -- executable
, 'XXMZ Custom' -- application
, 'XXMZ_ERPSCHOOLS_EMPLOYEE' -- short_name
, 'Executable for ERPSCHOOLS Employee INFORMATION' -- description
, 'PL/SQL Stored Procedure' -- execution_method
, 'XXMZ_ERPSCHOOLS_EMPLOYEE' -- execution_file_name
, '' -- subroutine_name
, '' -- Execution File Path
, 'US' -- language_code
, '');
COMMIT;
END;
Query in the front-end to see whether your executable is created or not.
2) Registering the Concurrent program from back end
===========================================================
Usually we create Concurrent program in the front-end, but this can be done from the database tier too.
Below is the program to create a Concurrent program from back-end.
BEGIN
FND_PROGRAM.register('Concurrent program for ErpSchools Employee Information' -- program
, 'XXMZ Custom' -- application
, 'Y' -- enabled
, 'XXMZ_ERPSCHOOLS_EMPLOYEE' -- short_name
, 'ErpSchools Employee Information' -- description
, 'XXMZ_ERPSCHOOLS_EMPLOYEE' -- executable_short_name
, 'XXMZ Custom' -- executable_application
, '' -- execution_options
, '' -- priority
, 'Y' -- save_output
, 'Y' -- print
, '' -- cols
, '' -- rows
, '' -- style
, 'N' -- style_required
, '' -- printer
, '' -- request_type
, '' -- request_type_application
, 'Y' -- use_in_srs
, 'N' -- allow_disabled_values
, 'N' -- run_alone
, 'TEXT' – output_type
, 'N' -- enable_trace
, 'Y' -- restart
, 'Y' -- nls_compliant
, '' -- icon_name
, 'US'); -- language_code
COMMIT;
END;
Query in the front-end to see whether your Concurrent program is created or not.
3) Attaching the concurrent program to the request group
==================================================================
Usually we Attach Concurrent program to the request group in the front-end, but this can be done from database tier too.
Below is the program to Attach Concurrent program to the request group from back-end.
BEGIN
FND_PROGRAM.add_to_group('XXMZ_ERPSCHOOLS_EMPLOYEE' -- program_short_name
, 'XXMZ Custom' -- application
, 'xxmz Request Group' -- Report Group Name
, 'XXMZ'); -- Report Group Application
COMMIT;
END;
Query in the front-end to see whether your Concurrent program is Attached to Request Group or not.
4) Submitting Concurrent Program from Back-end
====================================================
We first need to initialize oracle applications session using
fnd_global.apps_initialize(user_id,responsibility_id,application_responsibility_id) and then run fnd_request.submit_request
DECLARE
l_request_id NUMBER(30);
begin
FND_GLOBAL.APPS_INITIALIZE (user_id => 1318, resp_id => 59966, resp_appl_id => 20064);
l_request_id:= FND_REQUEST.SUBMIT_REQUEST ('XXMZ' --Application Short name,
'VENDOR_FORM'-- Concurrent Program Short Name );
DBMS_OUTPUT.PUT_LINE(l_request_id);
commit;
end;
*****************************************************
To get the resp_id and resp_appl_id use the below queries.
--SELECT APPLICATION_ID, RESPONSIBILITY_ID
FROM FND_RESPONSIBILITY_TL
WHERE RESPONSIBILITY_NAME='xxmz Custom'
--SELECT USER_ID FROM FND_USER WHERE USER_NAME='OPERATIONS'
*****************************************************
Once the concurrent program is submitted from back-end, status of the concurrent program can be checked using below query.
SELECT * FROM FND_CONCURRENT_REQUESTS WHERE REQUEST_ID=2766602
==============================================================================
This document is from ERP SCHOOLS
==============================================================================
Friday, November 20, 2009
Oracle tuning - Tune individual SQL statements
Collected From Burleson Consulting
________________________________________
Tuning individual Oracle SQL statements
The acronym SQL stands for Structured Query Language. SQL is an industry standard database query language that was adopted in the mid-1980s. It should not be confused with commercial products such as Microsoft SQL Server or open source products such as MySQL, both of which use the acronym as part of the title of their products.
Do this before you start individual SQL statement tuning
This broad-brush approach can save thousands of hours of tedious SQL tuning because you can hundreds of queries at once. Remember, you MUST do this first, else later changes to the optimizer parameters or statistics may un-tune your SQL.
Remember, you must ALWAYS start with system-level SQL tuning, else later changes might undo your tuned execution plans:
• Optimize the server kernel - You must always tune your disk and network I/O subsystem (RAID, DASD bandwidth, network) to optimize the I/O time, network packet size and dispatching frequency.
• Adjusting your optimizer statistics - You must always collect and store optimizer statistics to allow the optimizer to learn more about the distribution of your data to take more intelligent execution plans. Also, histograms can hypercharge SQL in cases of determining optimal table join order, and when making access decisions on skewed WHERE clause predicates.
• Adjust optimizer parameters - Optimizer optimizer_mode, optimizer_index_caching, optimizer_index_cost_adj.
• Optimize your instance - Your choice of db_block_size, db_cache_size, and OS parameters (db_file_multiblock_read_count, cpu_count, &c), can influence SQL performance.
• Tune your SQL Access workload with physical indexes and materialized views - Just as the 10g SQLAccess advisor recommends missing indexes and missing materialized views, you should always optimize your SQL workload with indexes, especially function-based indexes, a Godsend for SQL tuning.
11g Note: The Oracle 11g SQL Performance Analyzer (SPA), is primarily designed to speed up the holistic SQL tuning process.
Once you create a workload (called a SQL Tuning Set, or STS), Oracle will repeatedly execute the workload, using sophisticated predictive models (using a regression testing approach) to accurately identify the salient changes to SQL execution plans, based on your environmental changes. Using SPA, we can predict the impact of system changes on a workload, and we can forecast changes in response times for SQL after making any change, like parameter changes, schema changes, hardware changes, OS changes, or Oracle upgrades. For details, see the book Oracle 11g New Features.
Once the environment, instance, and objects have been tuned, the Oracle administrator can focus on what is probably the single most important aspect of tuning an Oracle database: tuning the individual SQL statements. In this final article in my series on Oracle tuning, I will share some general guidelines for tuning individual SQL statements to improve Oracle performance.
Oracle SQL tuning goals
Oracle SQL tuning is a phenomenally complex subject. Entire books have been written about the nuances of Oracle SQL tuning; however, there are some general guidelines that every Oracle DBA follows in order to improve the performance of their systems. Again, see the book "Oracle Tuning: The Definitive Reference", for complete details.
The goals of SQL tuning focus on improving the execution plan to fetch the rows with the smallest number of database "touches" (LIO buffer gets and PIO physical reads).
• Remove unnecessary large-table full-table scans—Unnecessary full-table scans cause a huge amount of unnecessary I/O and can drag-down an entire database. The tuning expert first evaluates the SQL based on the number of rows returned by the query. The most common tuning remedy for unnecessary full-table scans is adding indexes. Standard b-tree indexes can be added to tables, and bitmapped and function-based indexes can also eliminate full-table scans. In some cases, an unnecessary full-table scan can be forced to use an index by adding an index hint to the SQL statement.
• Cache small-table full-table scans—In cases where a full-table scan is the fastest access method, the administrator should ensure that a dedicated data buffer is available for the rows. In Oracle8 and beyond, a small table can be cached by forcing it into the KEEP pool.
• Verify optimal index usage—Oracle sometimes has a choice of indexes, and the tuning professional must examine each index and ensure that Oracle is using the proper index.
• Materialize your aggregations and summaries for static tables - One features of the Oracle 10g SQLAccess advisor is recommendations for new indexes and suggestions for materialized views. Materialized views pre-join tables and pre-summarize data, a real silver bullet for data mart reporting databases where the data is only updated daily. Again, see the book "Oracle Tuning: The Definitive Reference", for complete details on SQL tuning with materialized views.
These are the goals of SQL tuning in a nutshell. However, they are deceptively simple, and to effectively meet them, we need to have a through understanding of the internals of Oracle SQL. Let's begin with an overview of the Oracle SQL optimizers.
Oracle SQL optimizers
One of the first things the Oracle DBA looks at is the default optimizer mode for the database. The Oracle initialization parameters offer many cost-based optimizer modes as well as the deprecated yet useful rule-based hint:
The cost-based optimizer uses “statistics” that are collected from the table using the “analyze table” command. Oracle uses these metrics about the tables in order to intelligently determine the most efficient way of servicing the SQL query. It is important to recognize that in many cases, the cost-based optimizer may not make the proper decision in terms of the speed of the query. The cost-based optimizer is constantly being improved, but there are still many cases in which the rule-based optimizer will result in faster Oracle queries.
Prior to Oracle 10g, Oracle's default optimizer mode was called “choose.” In the choose optimizer mode, Oracle will execute the rule-based optimizer if there are no statistics present for the table; it will execute the cost-based optimizer if statistics are present. The danger with using the choose optimizer mode is that problems can occur in cases where one Oracle table in a complex query has statistics and the other tables do not.
Starting in Oracle 10g, the default optimizer mode is all_rows, favoring full-table scans over index access. The all_rows optimizer mode is designed to minimize computing resources and it favors full-table scans. Index access (first_rows_n) adds additional I/O overhead, but they return rows faster, back to the originating query:
Full-table scans touch all data blocks
Hence, many OLTP shops will choose first_rows, first_rows_100 or first_rows_10, asking Oracle to use indexes to reduce block touches:
Index scans return rows fast by doing additional I/O
Note: Staring in Oracle9i release 2, the Oracle performance tuning guide says that the first_rows optimizer mode has been deprecated and to use first_rows_n instead.
When only some tables contain CBO statistics, Oracle will use the cost-based optimization and estimate statistics for the other tables in the query at runtime. This can cause significant slowdown in the performance of the individual query.
In sum, the Oracle database administrator will always try changing the optimizer mode for queries as the very first step in Oracle tuning. The foremost tenet of Oracle SQL tuning is avoiding the dreaded full-table scan. One of the hallmarks of an inefficient SQL statement is the failure of the SQL statement to use all of the indexes that are present within the Oracle database in order to speed up the query.
Of course, there are times when a full-table scan is appropriate for a query, such as when you are doing aggregate operations such as a sum or an average, and the majority of the rows within the Oracle table must be read to get the query results. The task of the SQL tuning expert is to evaluate each full-table scan and see if the performance can be improved by adding an index.
In most Oracle systems, a SQL statement will be retrieving only a small subset of the rows within the table. The Oracle optimizers are programmed to check for indexes and to use them whenever possible to avoid excessive I/O. However, if the formulation of a query is inefficient, the cost-based optimizer becomes confused about the best access path to the data, and the cost-based optimizer will sometimes choose to do a full-table scan against the table. Again, the general rule is for the Oracle database administrator to interrogate the SQL and always look for full-table scans.
For the full story, see my book "Oracle Tuning: The Definitive Reference" for details on choosing the right optimizer mode.
A strategic plan for Oracle SQL tuning
Many people ask where they should start when tuning Oracle SQL. Tuning Oracle SQL is like fishing. You must first fish in the Oracle library cache to extract SQL statements and rank the statements by their amount of activity.
Step 1—Identify high-impact SQL
The SQL statements will be ranked according the number of executions and will be tuned in this order. The executions column of the v$sqlarea view and the stats$sql_summary or the dba_hist_sql_summary table can be used to locate the most frequently used SQL. Note that we can display SQL statements by:
• Rows processed—Queries that process a large number of rows will have high I/O and may also have impact on the TEMP tablespace.
• Buffer gets—High buffer gets may indicate a resource-intensive query.
• Disk reads—High disk reads indicate a query that is causing excessive I/O.
• Memory KB—The memory allocation of a SQL statement is useful for identifying statements that are doing in-memory table joins.
• CPU secs—This identifies the SQL statements that use the most processor resources.
• Sorts—Sorts can be a huge slowdown, especially if they’re being done on a disk in the TEMP tablespace.
• Executions—The more frequently executed SQL statements should be tuned first, since they will have the greatest impact on overall performance.
Step 2—Determine the execution plan for SQL
As each SQL statement is identified, it will be “explained” to determine its existing execution plan. There are a host of third-party tools on the market that show the execution plan for SQL statements. The most common way of determining the execution plan for a SQL statement is to use Oracle's explain plan utility. By using explain plan, the Oracle DBA can ask Oracle to parse the statement and display the execution class path without actually executing the SQL statement.
To see the output of an explain plan, you must first create a “plan table.” Oracle provides a script in $ORACLE_HOME/rdbms/admin called utlxplan.sql. Execute utlxplan.sql and create a public synonym for the plan_table:
sqlplus > @utlxplan
Table created.
sqlplus > create public synonym plan_table for sys.plan_table;
Synonym created.
Most relational databases use an explain utility that takes the SQL statement as input, runs the SQL optimizer, and outputs the access path information into a plan_table, which can then be interrogated to see the access methods. Listing 1 runs a complex query against a database.
EXPLAIN PLAN SET STATEMENT_ID = 'test1' FOR
SET STATEMENT_ID = 'RUN1'
INTO plan_table
FOR
SELECT 'T'||plansnet.terr_code, 'P'||detplan.pac1
|| detplan.pac2 || detplan.pac3, 'P1', sum(plansnet.ytd_d_ly_tm),
sum(plansnet.ytd_d_ty_tm),
sum(plansnet.jan_d_ly),
sum(plansnet.jan_d_ty),
FROM plansnet, detplan
WHERE
plansnet.mgc = detplan.mktgpm
AND
detplan.pac1 in ('N33','192','195','201','BAI',
'P51','Q27','180','181','183','184','186','188',
'198','204','207','209','211')
GROUP BY 'T'||plansnet.terr_code, 'P'||detplan.pac1 || detplan.pac2 || detplan.pac3;
This syntax is piped into the SQL optimizer, which will analyze the query and store the plan information in a row in the plan table identified by RUN1. Please note that the query will not execute; it will only create the internal access information in the plan table. The plan tables contains the following fields:
• operation—The type of access being performed. Usually table access, table merge, sort, or index operation
• options—Modifiers to the operation, specifying a full table, a range table, or a join
• object_name—The name of the table being used by the query component
• Process ID—The identifier for the query component
• Parent_ID—The parent of the query component. Note that several query components may have the same parent.
Now that the plan_table has been created and populated, you may interrogate it to see your output by running the following query in Listing 2.
plan.sql - displays contents of the explain plan table
SET PAGES 9999;
SELECT lpad(' ',2*(level-1))||operation operation,
options,
object_name,
position
FROM plan_table
START WITH id=0
AND
statement_id = 'RUN1'
CONNECT BY prior id = parent_id
AND
statement_id = 'RUN1';
Listing 3 shows the output from the plan table shown in Listing 1. This is the execution plan for the statement and shows the steps and the order in which they will be executed.
SQL> @list_explain_plan
OPERATION
-------------------------------------------------------------------------------------
OPTIONS OBJECT_NAME POSITION
------------------------------ -------------------------------------------------------
SELECT STATEMENT
SORT
GROUP BY 1
CONCATENATION 1
NESTED LOOPS 1
TABLE ACCESS FULL PLANSNET 1
TABLE ACCESS BY ROWID DETPLAN 2
INDEX RANGE SCAN DETPLAN_INDEX5 1
NESTED LOOPS
From this output, we can see the dreaded TABLE ACCESS FULL on the PLANSNET table. To diagnose the reason for this full-table scan, we return to the SQL and look for any plansnet columns in the WHERE clause. There, we see that the plansnet column called “mgc” is being used as a join column in the query, indicating that an index is necessary on plansnet.mgc to alleviate the full-table scan.
While the plan table is useful for determining the access path to the data, it does not tell the entire story. The configuration of the data is also a consideration. The SQL optimizer is aware of the number of rows in each table (the cardinality) and the presence of indexes on fields, but it is not aware of data distribution factors such as the number of expected rows returned from each query component.
Step 3—Tune the SQL statement
For those SQL statements that possess a sub-optimal execution plan, the SQL will be tuned by one of the following methods:
• Adding SQL “hints” to modify the execution plan
• Re-write SQL with Global Temporary Tables
• Rewriting the SQL in PL/SQL. For certain queries this can result in more than a 20x performance improvement. The SQL would be replaced with a call to a PL/SQL package that contained a stored procedure to perform the query.
Using hints to tune Oracle SQL
Among the most common tools for tuning SQL statements are hints. A hint is a directive that is added to the SQL statement to modify the access path for a SQL query.
Troubleshooting tip! For testing, you can quickly test the effect of another optimizer parameter value at the query level without using an “alter session” command, using the new opt_param SQL hint:
select /*+ opt_param('optimizer_mode','first_rows_10') */ col1, col2 . . .
select /*+ opt_param('optimizer_index_cost_adj',20) */ col1, col2 . .
Oracle publishes many dozens of SQL hints, and hints become increasingly more complicated through the various releases of Oracle and on into Oracle.
Note: Hints are only used for de-bugging and you should adjust your optimizer statistics to make the CBO replicate the hinted SQL. Let’s look at the most common hints to improve tuning:
• Mode hints: first_rows_10, first_rows_100
• Oracle leading and ordered hints Also see how to tune table join order with histograms
• Dynamic sampling: dynamic_sampling
• Oracle SQL undocumented tuning hints - Guru's only
• The cardinality hint
• Self-order the table joins - If you find that Oracle is joining the tables together in a sub-optimal order, you can use the ORDERED hint to force the tables to be joined in the order that they appear in the FROM clause. See
• Try a first_rows_n hint. Oracle has two cost-based optimizer modes, first_rows_n and all_rows. The first_rows mode will execute to begin returning rows as soon as possible, whereas the all_rows mode is designed to optimize the resources on the entire query before returning rows.
SELECT /*+ first_rows */
A case study in SQL tuning
One of the historic problems with SQL involves formulating SQL queries. Simple queries can be written in many different ways, each variant of the query producing the same result—but with widely different access methods and query speeds.
For example, a simple query such as “What students received an A last semester?” can be written in three ways, as shown in below, each returning an identical result.
A standard join:
SELECT *
FROM STUDENT, REGISTRATION
WHERE
STUDENT.student_id = REGISTRATION.student_id
AND
REGISTRATION.grade = 'A';
A nested query:
SELECT *
FROM STUDENT
WHERE
student_id =
(SELECT student_id
FROM REGISTRATION
WHERE
grade = 'A'
);
A correlated subquery:
SELECT *
FROM STUDENT
WHERE
0 <
(SELECT count(*)
FROM REGISTRATION
WHERE
grade = 'A'
AND
student_id = STUDENT.student_id
);
Let’s wind up with a review of the basic components of a SQL query and see how to optimize a query for remote execution.
Tips for writing more efficient SQL
Space doesn’t permit me to discuss every detail of Oracle tuning, but I can share some general rules for writing efficient SQL in Oracle regardless of the optimizer that is chosen. These rules may seem simplistic but following them in a diligent manner will generally relieve more than half of the SQL tuning problems that are experienced:
• Rewrite complex subqueries with temporary tables - Oracle created the global temporary table (GTT) and the SQL WITH operator to help divide-and-conquer complex SQL sub-queries (especially those with with WHERE clause subqueries, SELECT clause scalar subqueries and FROM clause in-line views). Tuning SQL with temporary tables (and materializations in the WITH clause) can result in amazing performance improvements.
• Use minus instead of EXISTS subqueries - Some say that using the minus operator instead of NOT IN and NOT Exists will result in a faster execution plan.
• Use SQL analytic functions - The Oracle analytic functions can do multiple aggregations (e.g. rollup by cube) with a single pass through the tables, making them very fast for reporting SQL.
• Re-write NOT EXISTS and NOT EXISTS subqueries as outer joins - In many cases of NOT queries (but ONLY where a column is defined as NULL), you can re-write the uncorrelated subqueries into outer joins with IS NULL tests. Note that this is a non-correlated sub-query, but it could be re-written as an outer join.
select book_key from book
where
book_key NOT IN (select book_key from sales);
Below we combine the outer join with a NULL test in the WHERE clause without using a sub-query, giving a faster execution plan.
select b.book_key from book b, sales s
where
b.book_key = s.book_key(+)
and
s.book_key IS NULL;
• Index your NULL values - If you have SQL that frequently tests for NULL, consider creating an index on NULL values. To get around the optimization of SQL queries that choose NULL column values (i.e. where emp_name IS NULL), we can create a function-based index using the null value built-in SQL function to index only on the NULL columns.
• Leave column names alone - Never do a calculation on an indexed column unless you have a matching function-based index (a.k.a. FBI). Better yet, re-design the schema so that common where clause predicates do not need transformation with a BIF:
where salary*5 > :myvalue
where substr(ssn,7,4) = "1234"
where to_char(mydate,mon) = "january"
• Avoid the use of NOT IN or HAVING. Instead, a NOT EXISTS subquery may run faster (when appropriate).
• Avoid the LIKE predicate = Always replace a "like" with an equality, when appropriate.
• Never mix data types - If a WHERE clause column predicate is numeric, do not to use quotes. For char index columns, always use quotes. There are mixed data type predicates:
where cust_nbr = "123"
where substr(ssn,7,4) = 1234
• Use decode and case - Performing complex aggregations with the “decode” or "case" functions can minimize the number of times a table has to be selected.
• Don't fear full-table scans - Not all OLTP queries are optimal when they uses indexes. If your query will return a large percentage of the table rows, a full-table scan may be faster than an index scan. This depends on many factors, including your configuration (values for db_file_multiblock_read_count, db_block_size), query parallelism and the number of table/index blocks in the buffer cache.
• Use those aliases - Always use table aliases when referencing columns.
Collected From Burleson Consulting
________________________________________
Tuning individual Oracle SQL statements
The acronym SQL stands for Structured Query Language. SQL is an industry standard database query language that was adopted in the mid-1980s. It should not be confused with commercial products such as Microsoft SQL Server or open source products such as MySQL, both of which use the acronym as part of the title of their products.
Do this before you start individual SQL statement tuning
This broad-brush approach can save thousands of hours of tedious SQL tuning because you can hundreds of queries at once. Remember, you MUST do this first, else later changes to the optimizer parameters or statistics may un-tune your SQL.
Remember, you must ALWAYS start with system-level SQL tuning, else later changes might undo your tuned execution plans:
• Optimize the server kernel - You must always tune your disk and network I/O subsystem (RAID, DASD bandwidth, network) to optimize the I/O time, network packet size and dispatching frequency.
• Adjusting your optimizer statistics - You must always collect and store optimizer statistics to allow the optimizer to learn more about the distribution of your data to take more intelligent execution plans. Also, histograms can hypercharge SQL in cases of determining optimal table join order, and when making access decisions on skewed WHERE clause predicates.
• Adjust optimizer parameters - Optimizer optimizer_mode, optimizer_index_caching, optimizer_index_cost_adj.
• Optimize your instance - Your choice of db_block_size, db_cache_size, and OS parameters (db_file_multiblock_read_count, cpu_count, &c), can influence SQL performance.
• Tune your SQL Access workload with physical indexes and materialized views - Just as the 10g SQLAccess advisor recommends missing indexes and missing materialized views, you should always optimize your SQL workload with indexes, especially function-based indexes, a Godsend for SQL tuning.
11g Note: The Oracle 11g SQL Performance Analyzer (SPA), is primarily designed to speed up the holistic SQL tuning process.
Once you create a workload (called a SQL Tuning Set, or STS), Oracle will repeatedly execute the workload, using sophisticated predictive models (using a regression testing approach) to accurately identify the salient changes to SQL execution plans, based on your environmental changes. Using SPA, we can predict the impact of system changes on a workload, and we can forecast changes in response times for SQL after making any change, like parameter changes, schema changes, hardware changes, OS changes, or Oracle upgrades. For details, see the book Oracle 11g New Features.
Once the environment, instance, and objects have been tuned, the Oracle administrator can focus on what is probably the single most important aspect of tuning an Oracle database: tuning the individual SQL statements. In this final article in my series on Oracle tuning, I will share some general guidelines for tuning individual SQL statements to improve Oracle performance.
Oracle SQL tuning goals
Oracle SQL tuning is a phenomenally complex subject. Entire books have been written about the nuances of Oracle SQL tuning; however, there are some general guidelines that every Oracle DBA follows in order to improve the performance of their systems. Again, see the book "Oracle Tuning: The Definitive Reference", for complete details.
The goals of SQL tuning focus on improving the execution plan to fetch the rows with the smallest number of database "touches" (LIO buffer gets and PIO physical reads).
• Remove unnecessary large-table full-table scans—Unnecessary full-table scans cause a huge amount of unnecessary I/O and can drag-down an entire database. The tuning expert first evaluates the SQL based on the number of rows returned by the query. The most common tuning remedy for unnecessary full-table scans is adding indexes. Standard b-tree indexes can be added to tables, and bitmapped and function-based indexes can also eliminate full-table scans. In some cases, an unnecessary full-table scan can be forced to use an index by adding an index hint to the SQL statement.
• Cache small-table full-table scans—In cases where a full-table scan is the fastest access method, the administrator should ensure that a dedicated data buffer is available for the rows. In Oracle8 and beyond, a small table can be cached by forcing it into the KEEP pool.
• Verify optimal index usage—Oracle sometimes has a choice of indexes, and the tuning professional must examine each index and ensure that Oracle is using the proper index.
• Materialize your aggregations and summaries for static tables - One features of the Oracle 10g SQLAccess advisor is recommendations for new indexes and suggestions for materialized views. Materialized views pre-join tables and pre-summarize data, a real silver bullet for data mart reporting databases where the data is only updated daily. Again, see the book "Oracle Tuning: The Definitive Reference", for complete details on SQL tuning with materialized views.
These are the goals of SQL tuning in a nutshell. However, they are deceptively simple, and to effectively meet them, we need to have a through understanding of the internals of Oracle SQL. Let's begin with an overview of the Oracle SQL optimizers.
Oracle SQL optimizers
One of the first things the Oracle DBA looks at is the default optimizer mode for the database. The Oracle initialization parameters offer many cost-based optimizer modes as well as the deprecated yet useful rule-based hint:
The cost-based optimizer uses “statistics” that are collected from the table using the “analyze table” command. Oracle uses these metrics about the tables in order to intelligently determine the most efficient way of servicing the SQL query. It is important to recognize that in many cases, the cost-based optimizer may not make the proper decision in terms of the speed of the query. The cost-based optimizer is constantly being improved, but there are still many cases in which the rule-based optimizer will result in faster Oracle queries.
Prior to Oracle 10g, Oracle's default optimizer mode was called “choose.” In the choose optimizer mode, Oracle will execute the rule-based optimizer if there are no statistics present for the table; it will execute the cost-based optimizer if statistics are present. The danger with using the choose optimizer mode is that problems can occur in cases where one Oracle table in a complex query has statistics and the other tables do not.
Starting in Oracle 10g, the default optimizer mode is all_rows, favoring full-table scans over index access. The all_rows optimizer mode is designed to minimize computing resources and it favors full-table scans. Index access (first_rows_n) adds additional I/O overhead, but they return rows faster, back to the originating query:
Full-table scans touch all data blocks
Hence, many OLTP shops will choose first_rows, first_rows_100 or first_rows_10, asking Oracle to use indexes to reduce block touches:
Index scans return rows fast by doing additional I/O
Note: Staring in Oracle9i release 2, the Oracle performance tuning guide says that the first_rows optimizer mode has been deprecated and to use first_rows_n instead.
When only some tables contain CBO statistics, Oracle will use the cost-based optimization and estimate statistics for the other tables in the query at runtime. This can cause significant slowdown in the performance of the individual query.
In sum, the Oracle database administrator will always try changing the optimizer mode for queries as the very first step in Oracle tuning. The foremost tenet of Oracle SQL tuning is avoiding the dreaded full-table scan. One of the hallmarks of an inefficient SQL statement is the failure of the SQL statement to use all of the indexes that are present within the Oracle database in order to speed up the query.
Of course, there are times when a full-table scan is appropriate for a query, such as when you are doing aggregate operations such as a sum or an average, and the majority of the rows within the Oracle table must be read to get the query results. The task of the SQL tuning expert is to evaluate each full-table scan and see if the performance can be improved by adding an index.
In most Oracle systems, a SQL statement will be retrieving only a small subset of the rows within the table. The Oracle optimizers are programmed to check for indexes and to use them whenever possible to avoid excessive I/O. However, if the formulation of a query is inefficient, the cost-based optimizer becomes confused about the best access path to the data, and the cost-based optimizer will sometimes choose to do a full-table scan against the table. Again, the general rule is for the Oracle database administrator to interrogate the SQL and always look for full-table scans.
For the full story, see my book "Oracle Tuning: The Definitive Reference" for details on choosing the right optimizer mode.
A strategic plan for Oracle SQL tuning
Many people ask where they should start when tuning Oracle SQL. Tuning Oracle SQL is like fishing. You must first fish in the Oracle library cache to extract SQL statements and rank the statements by their amount of activity.
Step 1—Identify high-impact SQL
The SQL statements will be ranked according the number of executions and will be tuned in this order. The executions column of the v$sqlarea view and the stats$sql_summary or the dba_hist_sql_summary table can be used to locate the most frequently used SQL. Note that we can display SQL statements by:
• Rows processed—Queries that process a large number of rows will have high I/O and may also have impact on the TEMP tablespace.
• Buffer gets—High buffer gets may indicate a resource-intensive query.
• Disk reads—High disk reads indicate a query that is causing excessive I/O.
• Memory KB—The memory allocation of a SQL statement is useful for identifying statements that are doing in-memory table joins.
• CPU secs—This identifies the SQL statements that use the most processor resources.
• Sorts—Sorts can be a huge slowdown, especially if they’re being done on a disk in the TEMP tablespace.
• Executions—The more frequently executed SQL statements should be tuned first, since they will have the greatest impact on overall performance.
Step 2—Determine the execution plan for SQL
As each SQL statement is identified, it will be “explained” to determine its existing execution plan. There are a host of third-party tools on the market that show the execution plan for SQL statements. The most common way of determining the execution plan for a SQL statement is to use Oracle's explain plan utility. By using explain plan, the Oracle DBA can ask Oracle to parse the statement and display the execution class path without actually executing the SQL statement.
To see the output of an explain plan, you must first create a “plan table.” Oracle provides a script in $ORACLE_HOME/rdbms/admin called utlxplan.sql. Execute utlxplan.sql and create a public synonym for the plan_table:
sqlplus > @utlxplan
Table created.
sqlplus > create public synonym plan_table for sys.plan_table;
Synonym created.
Most relational databases use an explain utility that takes the SQL statement as input, runs the SQL optimizer, and outputs the access path information into a plan_table, which can then be interrogated to see the access methods. Listing 1 runs a complex query against a database.
EXPLAIN PLAN SET STATEMENT_ID = 'test1' FOR
SET STATEMENT_ID = 'RUN1'
INTO plan_table
FOR
SELECT 'T'||plansnet.terr_code, 'P'||detplan.pac1
|| detplan.pac2 || detplan.pac3, 'P1', sum(plansnet.ytd_d_ly_tm),
sum(plansnet.ytd_d_ty_tm),
sum(plansnet.jan_d_ly),
sum(plansnet.jan_d_ty),
FROM plansnet, detplan
WHERE
plansnet.mgc = detplan.mktgpm
AND
detplan.pac1 in ('N33','192','195','201','BAI',
'P51','Q27','180','181','183','184','186','188',
'198','204','207','209','211')
GROUP BY 'T'||plansnet.terr_code, 'P'||detplan.pac1 || detplan.pac2 || detplan.pac3;
This syntax is piped into the SQL optimizer, which will analyze the query and store the plan information in a row in the plan table identified by RUN1. Please note that the query will not execute; it will only create the internal access information in the plan table. The plan tables contains the following fields:
• operation—The type of access being performed. Usually table access, table merge, sort, or index operation
• options—Modifiers to the operation, specifying a full table, a range table, or a join
• object_name—The name of the table being used by the query component
• Process ID—The identifier for the query component
• Parent_ID—The parent of the query component. Note that several query components may have the same parent.
Now that the plan_table has been created and populated, you may interrogate it to see your output by running the following query in Listing 2.
plan.sql - displays contents of the explain plan table
SET PAGES 9999;
SELECT lpad(' ',2*(level-1))||operation operation,
options,
object_name,
position
FROM plan_table
START WITH id=0
AND
statement_id = 'RUN1'
CONNECT BY prior id = parent_id
AND
statement_id = 'RUN1';
Listing 3 shows the output from the plan table shown in Listing 1. This is the execution plan for the statement and shows the steps and the order in which they will be executed.
SQL> @list_explain_plan
OPERATION
-------------------------------------------------------------------------------------
OPTIONS OBJECT_NAME POSITION
------------------------------ -------------------------------------------------------
SELECT STATEMENT
SORT
GROUP BY 1
CONCATENATION 1
NESTED LOOPS 1
TABLE ACCESS FULL PLANSNET 1
TABLE ACCESS BY ROWID DETPLAN 2
INDEX RANGE SCAN DETPLAN_INDEX5 1
NESTED LOOPS
From this output, we can see the dreaded TABLE ACCESS FULL on the PLANSNET table. To diagnose the reason for this full-table scan, we return to the SQL and look for any plansnet columns in the WHERE clause. There, we see that the plansnet column called “mgc” is being used as a join column in the query, indicating that an index is necessary on plansnet.mgc to alleviate the full-table scan.
While the plan table is useful for determining the access path to the data, it does not tell the entire story. The configuration of the data is also a consideration. The SQL optimizer is aware of the number of rows in each table (the cardinality) and the presence of indexes on fields, but it is not aware of data distribution factors such as the number of expected rows returned from each query component.
Step 3—Tune the SQL statement
For those SQL statements that possess a sub-optimal execution plan, the SQL will be tuned by one of the following methods:
• Adding SQL “hints” to modify the execution plan
• Re-write SQL with Global Temporary Tables
• Rewriting the SQL in PL/SQL. For certain queries this can result in more than a 20x performance improvement. The SQL would be replaced with a call to a PL/SQL package that contained a stored procedure to perform the query.
Using hints to tune Oracle SQL
Among the most common tools for tuning SQL statements are hints. A hint is a directive that is added to the SQL statement to modify the access path for a SQL query.
Troubleshooting tip! For testing, you can quickly test the effect of another optimizer parameter value at the query level without using an “alter session” command, using the new opt_param SQL hint:
select /*+ opt_param('optimizer_mode','first_rows_10') */ col1, col2 . . .
select /*+ opt_param('optimizer_index_cost_adj',20) */ col1, col2 . .
Oracle publishes many dozens of SQL hints, and hints become increasingly more complicated through the various releases of Oracle and on into Oracle.
Note: Hints are only used for de-bugging and you should adjust your optimizer statistics to make the CBO replicate the hinted SQL. Let’s look at the most common hints to improve tuning:
• Mode hints: first_rows_10, first_rows_100
• Oracle leading and ordered hints Also see how to tune table join order with histograms
• Dynamic sampling: dynamic_sampling
• Oracle SQL undocumented tuning hints - Guru's only
• The cardinality hint
• Self-order the table joins - If you find that Oracle is joining the tables together in a sub-optimal order, you can use the ORDERED hint to force the tables to be joined in the order that they appear in the FROM clause. See
• Try a first_rows_n hint. Oracle has two cost-based optimizer modes, first_rows_n and all_rows. The first_rows mode will execute to begin returning rows as soon as possible, whereas the all_rows mode is designed to optimize the resources on the entire query before returning rows.
SELECT /*+ first_rows */
A case study in SQL tuning
One of the historic problems with SQL involves formulating SQL queries. Simple queries can be written in many different ways, each variant of the query producing the same result—but with widely different access methods and query speeds.
For example, a simple query such as “What students received an A last semester?” can be written in three ways, as shown in below, each returning an identical result.
A standard join:
SELECT *
FROM STUDENT, REGISTRATION
WHERE
STUDENT.student_id = REGISTRATION.student_id
AND
REGISTRATION.grade = 'A';
A nested query:
SELECT *
FROM STUDENT
WHERE
student_id =
(SELECT student_id
FROM REGISTRATION
WHERE
grade = 'A'
);
A correlated subquery:
SELECT *
FROM STUDENT
WHERE
0 <
(SELECT count(*)
FROM REGISTRATION
WHERE
grade = 'A'
AND
student_id = STUDENT.student_id
);
Let’s wind up with a review of the basic components of a SQL query and see how to optimize a query for remote execution.
Tips for writing more efficient SQL
Space doesn’t permit me to discuss every detail of Oracle tuning, but I can share some general rules for writing efficient SQL in Oracle regardless of the optimizer that is chosen. These rules may seem simplistic but following them in a diligent manner will generally relieve more than half of the SQL tuning problems that are experienced:
• Rewrite complex subqueries with temporary tables - Oracle created the global temporary table (GTT) and the SQL WITH operator to help divide-and-conquer complex SQL sub-queries (especially those with with WHERE clause subqueries, SELECT clause scalar subqueries and FROM clause in-line views). Tuning SQL with temporary tables (and materializations in the WITH clause) can result in amazing performance improvements.
• Use minus instead of EXISTS subqueries - Some say that using the minus operator instead of NOT IN and NOT Exists will result in a faster execution plan.
• Use SQL analytic functions - The Oracle analytic functions can do multiple aggregations (e.g. rollup by cube) with a single pass through the tables, making them very fast for reporting SQL.
• Re-write NOT EXISTS and NOT EXISTS subqueries as outer joins - In many cases of NOT queries (but ONLY where a column is defined as NULL), you can re-write the uncorrelated subqueries into outer joins with IS NULL tests. Note that this is a non-correlated sub-query, but it could be re-written as an outer join.
select book_key from book
where
book_key NOT IN (select book_key from sales);
Below we combine the outer join with a NULL test in the WHERE clause without using a sub-query, giving a faster execution plan.
select b.book_key from book b, sales s
where
b.book_key = s.book_key(+)
and
s.book_key IS NULL;
• Index your NULL values - If you have SQL that frequently tests for NULL, consider creating an index on NULL values. To get around the optimization of SQL queries that choose NULL column values (i.e. where emp_name IS NULL), we can create a function-based index using the null value built-in SQL function to index only on the NULL columns.
• Leave column names alone - Never do a calculation on an indexed column unless you have a matching function-based index (a.k.a. FBI). Better yet, re-design the schema so that common where clause predicates do not need transformation with a BIF:
where salary*5 > :myvalue
where substr(ssn,7,4) = "1234"
where to_char(mydate,mon) = "january"
• Avoid the use of NOT IN or HAVING. Instead, a NOT EXISTS subquery may run faster (when appropriate).
• Avoid the LIKE predicate = Always replace a "like" with an equality, when appropriate.
• Never mix data types - If a WHERE clause column predicate is numeric, do not to use quotes. For char index columns, always use quotes. There are mixed data type predicates:
where cust_nbr = "123"
where substr(ssn,7,4) = 1234
• Use decode and case - Performing complex aggregations with the “decode” or "case" functions can minimize the number of times a table has to be selected.
• Don't fear full-table scans - Not all OLTP queries are optimal when they uses indexes. If your query will return a large percentage of the table rows, a full-table scan may be faster than an index scan. This depends on many factors, including your configuration (values for db_file_multiblock_read_count, db_block_size), query parallelism and the number of table/index blocks in the buffer cache.
• Use those aliases - Always use table aliases when referencing columns.
Monday, November 16, 2009
SQL And PL/SQL Coding Standards
PL/SQL has become a mature and popular language since its introduction in 1991, yet the language is still relatively young compared to its ancestors (both direct ancestors, like Ada, and indirect ancestors, like COBOL and Fortran). One of the most common questions I am asked is about coding standards for SQL and PL/SQL.
The primary purpose of coding standards has always been to make maintenance easier for developers. In order to satisfy this requirement, PL/SQL coding standards must address several areas of the development process. Table D.1 displays some of the development areas that PL/SQL coding standards address.
Table D.1 Development areas addressed by coding standards. ________________________________________
Development Area Includes
Vertical spacing Spacing between statements; spacing between procedures and functions within package bodies.
Horizontal spacing Spacing between identifiers and operators; number of statements per line; maximum line width.
Procedural calls Use of positional or named notation when calling stored PL/SQL objects.
Commenting Type of comments to be used and the frequency, spacing, positioning, and content of comments.
Code reuse/modularity Contents of procedures and functions; organization of procedures and functions into packages.
Identifiers Rules for naming identifiers; rules for naming stored PL/SQL objects.
SQL statements Formatting rules for embedded SQL statements.
Performance Performance tips (particularly for embedded SQL statements).
Debugging A standard method of handling exceptions inside PL/SQL objects as well as inside other applications that call stored PL/SQL objects.
Testability Rules for writing stored PL/SQL objects so that unit testing can be accomplished.
Development environment Rules determining what tools will be used for application development and what processes must be followed by developers.
Capitalization Rules determining which keywords will be used in UPPER case, Mixed case, and lower case.
Conformance to standards Rules designating when it is allowable for code to not agree with the coding standard.
Documentation Rules that designate whether the coding standards will be applied to design documents and what type of documentation must exist for particular routines.
The level of specifics contained in a coding standard should be fairly strict. While this may seem to be a burden during the development process, the people who do maintenance down the line will appreciate adherence to the standards.
The best way to ensure adherence to standards is to use structured peer review when a code module is completed. If the code is readable, the peer review process will flow more smoothly, and the reviewers will be able to concentrate their review time on understanding the intimacies of code rather than on deciphering an entry for the obfuscated PL/SQL contest! Peer reviews also provide a last line of defense against “sleeper” bugs (often overlooked by even the best developers), which are found a lot more easily when the code is readable.
If your organization does some or all of its own SQL and PL/SQL training, you should make an effort to incorporate standards training into your course materials.
I hope you find the examples presented in this text easy to read and understand. I also hope that you notice the consistency of style in the way examples appear. Every piece of code on the CD-ROM and in the text conforms to the following coding standard.
A Sample SQL And PL/SQL Coding Standard
This document defines the SQL and PL/SQL environment and programming standards and procedures for. The standards established in this document apply to all SQL and PL/SQL development efforts.
These PL/SQL coding standards were written to allow for consistency in PL/SQL code written by various developers while allowing for some individual styles and preferences to be expressed. The central purpose of any coding standard for SQL and PL/SQL must deal with database performance, clarity of code, and maintainability of code. This standard should be considered a guideline for developing easily maintainable SQL and PL/SQL applications in a high-performance Oracle database.
Developers should attempt to meet the spirit of this document by applying good judgment, rather than strictly adhering to the letter of the standard. This standard applies to all developer-written SQL and PL/SQL code (including scripts, stored procedures and functions, database triggers, and stored packages). Generated code is not governed by this standard.
This document is a living document that evolves based on the experiences of you, the developer. You should be aware that changes may occur to this document in the future, based on your (or other developers’) experiences and insights.
The Development Environment And Processes
This section of the standard is highly dependent on the nature of your organization; therefore, I will only give suggestions on the types of material that should be included in this section of your coding standard. The Development Environment And Processes section of the standard should address the following issues:
• Your version control processes—A good working knowledge of PL/SQL is important to a developer, but the inability of a single developer to follow version control processes could be disastrous!
• Standard tools for your development efforts—If you have a standard configuration for your tools, it should be described in painstaking detail. If your tools support central administration of this configuration, so much the better.
• Peer review practices—Many organizations have formal peer review processes in place to validate the quality of design documents, code, and test results. If your organization uses these processes, the coding standard should (at the very least) point developers to your process documents.
• Documentation—Describe the documentation required when developers create new code and what documents need to be updated when maintenance is performed. Provide reasonable facsimiles of this documentation (or excerpts from real documents that satisfy your requirements).
• Testing standards in place—Provide examples of thorough test scripts and plans. Tests for each aspect of your system (front end, stored procedures and functions, packages, and database triggers) should be discussed in detail.
• Standard routines—You should describe whether standard routines, such as error handling, help system calls, and so forth, are available and when these routines should be used. Provide a detailed explanation of each standard call and its interfaces.
These issues can be addressed in appendices to your coding standard or in other documents, as long as your developers receive the necessary information.
________________________________________
References To Other Documents
I personally favor one stop shopping; pack the standard with as much information as possible and reduce the amount of time developers spend tracking down other documents.
________________________________________
Programming Design Standards
Developers should design for modularity. Black box is a term often used in conjunction with modules; each module should perform one (and only one) function using a defined interface and produce a predictable result. So long as the interface for a code module is not changed, the code module may be altered without affecting outside code.
Each module contains one or more routines (and the data structures and variables needed to support the routines). PL/SQL allows developers to implement modularity through the use of packages, which can contain procedures and functions as well as global type, variable, and constant declarations.
Stored functions that use parameters of the IN OUT and OUT types are not allowed. Stored functions should use only the RETURN statement to return a value. Developers are encouraged to identify routines that can be reused. This code can be centralized, tested, and used by other developers to improve the reliability of system code and to reduce development time for more complex modules.
________________________________________
The Modularity Ombudsman
If your organization consists of ten or more people, it might be a good idea to appoint a “Modularity Ombudsman” who has some experience with SQL and PL/SQL. The responsibilities of this position include:
• Identifying code segments that can be reused
• Developing and testing reusable modules
• Documenting modules and promoting their use
The modularity ombudsman should also take part in peer review for new modules to help increase code reuse throughout your project.
________________________________________
Headers
A header should appear at the start of any script, procedure, function, package body, or package spec. Consider this template header:
-- *****************************************************************
-- Description: Describe the purpose of the object. If necessary,
-- describe the design of the object at a very high level.
--
-- Input Parameters:
--
-- Output Parameters:
--
-- Error Conditions Raised:
--
-- Author:
--
-- Revision History
-- Date Author Reason for Change
-- ----------------------------------------------------------------
-- 03 JAN 1997 J.Schmoe Created.
-- *****************************************************************
________________________________________
The Usefulness Of Headers
There are some people who dislike headers or feel that a header is a useless burden to place on a developer. I disagree: At no other point in the code are provisions made for documenting the overall purpose, logic, and interface of a module. In my opinion, a header is the most essential documentation for any piece of stored code.
________________________________________
Formatting Guidelines For SQL And PL/SQL Statements
These guidelines are provided to give code a generally consistent appearance, including indentation, horizontal alignment, and vertical alignment. Adherence to these standards will make code more readable and more easily understood when maintenance is necessary.
Alignment Of Operators
These guidelines enhance the readability of code by adding white space and clarifying complex expressions.
• Arrange series of statements containing similar operators into columns whenever it will not cause excessive white space and you have sufficient room to do so.
Correct:
vFirstName := 'Roger';
vLastName := 'Smith';
vSSN := 999999999;
Incorrect:
vFirstName := 'Roger';
vLastName := 'Smith';
vSSN := 999999999;
• Always use parentheses in expressions containing more than one identifier or literal. This clarifies code for inexperienced developers who are not familiar with operator precedence and helps eliminate the possibility that you’ve overlooked something in your equation.
Correct:
IF (nSSN < 2.5) THEN
END IF;
Incorrect:
IF nSSN < 2.5 THEN
END IF;
• Align the IN and OUT keywords in columns when defining the interface for a procedure or function.
Correct:
PROCEDURE Days_Between (dStartDate IN date,
dEndDate IN date,
nGPA IN OUT number,
nDaysBetween OUT number)
Incorrect:
PROCEDURE Days_Between (dStartDate IN date,
dEndDate IN date,
nGPA IN OUT number,
nDaysBetween OUT number)
• When calling a procedure or function, align the parameters into a column. This reduces the visual clutter around the call, making it stand out from the rest of the code.
Correct:
DaysBetween (dStartDate => dEnrolledDate,
dEndDate => dGraduationDate,
nGPA => nFinalGPA,
nDaysBetween => nDuration);
Incorrect:
DaysBetween (dStartDate => dEnrolledDate,
dEndDate => dGraduationDate,
nGPA => nFinalGPA,
nDaysBetween => nDuration);
Capitalization
Table D.2 contains a list of keywords that should always be fully capitalized when referenced in code. Some of these keywords are commonly used reserved words; reserved words that do not appear on this list should be capitalized as well.
Table D.2 Capitalize these keywords. ________________________________________
ALL FALSE MINUS ROWTYPE
AND FETCH NOT SELECT
AS FOR NOTFOUND SET
BEGIN FOUND NULL SQLCODE
BETWEEN FROM OPEN SQLERRM
BODY FUNCTION OR TABLE
CLOSE GOTO ORDER BY THEN
COMMIT GROUP BY OUT TYPE
CONSTANT HAVING PACKAGE UNION
CREATE IF PROCEDURE UNION ALL
DECLARE IN RAISE UPDATE
DELETE INSERT REPLACE VALUES
ELSE INTERSECT RETURN VIEW
ELSIF INTO ROLLBACK WHEN
END IS ROWCOUNT WHERE
EXCEPTION LIKE ROWID WHILE
EXIT LOOP ROWNUM
The keyword REPLACE is to be used in uppercase only when used as part of the CREATE OR REPLACE clause that is used to create a stored PL/SQL object. Calls to the SQL function replace() should not be presented in uppercase.
In addition to the keywords presented in Table D.2, fully capitalize all of the following:
• The names of all standard exceptions (NO_DATA_FOUND, OTHERS, TOO_MANY_ROWS), and all user-defined exceptions.
• The names of all constants and all user-defined datatypes.
• All acronyms (ANSI, ASCII, HUD, NASA, NOAA, YMCA, and so forth).
• The names of all tables, snapshots, and views, as well as the aliases given to these objects in queries.
• The names of all database triggers.
Use mixed case to refer to the names of user-defined procedures and functions (functions provided by SQL*Plus and PL/SQL are still referenced in lower case). For example:
Calculate_GPA
DBMS_Output.Put_Line
Optionally, use mixed case for user-defined identifiers. If you choose this method, use capital letters to help make the identifier names more meaningful by visually breaking variable names into words; here are some examples:
vString
nBaseSalary
nGPA
iTardyDays
iClassNumber
lComments
rStudentPhoto
All text not handled by these rules should use lowercase. Consider the following:
CREATE OR REPLACE PACKAGE My_Sample_Package AS
PROCEDURE My_Sample_Procedure (nParameter1 IN number,
nParameter2 OUT number)
IS
YES CONSTANT char (1) := 'Y';
BEGIN
IF (some expression) THEN
replace (vString, chr (9), ' ');
END IF;
END My_Sample_Procedure;
END My_Sample_Package;
Comments
As much as some developers dislike the task, commenting code is essential if the code is going to be maintained. There are a number of steps that can be taken to make comments less necessary:
• Use meaningful identifiers for variables, constants, and parameters. If you use abbreviations to compose identifiers, use the abbreviations consistently (e.g., don’t use both ADDR and ADRS to signify ADDRESS).
• Use the named parameter style of executing procedures and functions. This is especially effective if both the parameters and the variables passed to the stored PL/SQL object have meaningful identifiers.
• Comments about revisions belong in the prologue, not in the body of the module.
________________________________________
TIP: Commenting Changes And Problem Tracking
If you’re using a problem-tracking system on your project, it’s better to reference a particular report from that system and provide a brief summary of the changes made to solve that problem. Don’t attempt to include all the information about the problem in the prologue; that’s why you bought a problem tracking system!
________________________________________
• Break complex equations and formulas into several smaller statements.
• Reuse existing functions and procedures to accomplish your tasks. Identify code that can be reused.
There are a number of locations in PL/SQL code where comments should almost always be used, including the following instances:
• Before each loop structure.
• Before each BEGIN…END sub-block.
• Before each conditional logic expression (IF THEN).
• Before any other logically significant statements.
Do not comment each line of code! Only comment important parts of your code, explaining why the code is written in a particular way. Explain business rules if possible. Never use a comment to restate the actions of a piece of code.
PL/SQL supports the following two styles of commenting:
/* We need to determine which students are in academic trouble. */
-- We need to determine which students are in academic trouble.
PL/SQL does not support the nesting of C-style comments; you cannot comment out a C-style comment using other C-style comments. For this reason, it is strongly recommended that only the double-dash (--) style of commenting be used except when commenting out blocks of code.
The exception to this rule is inside 3GL programs that use the Oracle Precompilers. The Oracle Precompilers don’t support single line comments. On these occasions, use the commenting style most appropriate to the 3GL.
If a comment is required, place the comment on the line immediately preceding the line of code. Do not append comments to the end of code; if a comment is warranted by the complexity of the code and you have used meaningful identifiers, the comment should be complicated enough that you need to explain the situation using more than one or two words.
Correct:
--
-- Determine which students might be in trouble academically. We want
-- to help them perform better in school.
--
IF (some condition) THEN
Incorrect:
IF (some condition) THEN -- who's got bad grades?
All comments should use proper grammar, punctuation, and spelling. Comments should be complete, coherent sentences.
________________________________________
TIP: Volume Of Comments
As a general rule, about one-third of your final code should be comments. This figure often varies depending on the size and complexity of the code, but is an excellent rule of thumb.
________________________________________
Indentation
The most important element in readable code is consistent indentation, which illustrates clearly the logic flow of a procedure. Consider these blocks of code:
IF (x < 7) THEN
IF (y < 0) THEN
END IF;
ELSIF (x > 10) THEN
ELSE
END IF;
IF (x < 7) THEN
IF (y < 0) THEN
END IF;
ELSIF (x > 10) THEN
ELSE
END IF;
Horizontal alignment in the second block of code makes it much easier to follow, even though it is syntactically and functionally identical to the first block of code.
You should not use more than three or four levels of indentation in any block of code. If this many levels of indentation become necessary, consider breaking the code into smaller modules. Too many levels of indentation is almost as bad as no indentation at all.
• Code should always be indented consistently, using three spaces for each level of indentation. Variable, type, and constant declarations should all be indented to the first level of indentation. Do not use tab characters.
IF (some expression) THEN
IF (some expression) THEN
IF (some expression) THEN
ELSIF (some expression) THEN
END IF;
END IF;
END IF;
• Statements following the WHEN clause of an exception handler should be indented five spaces, in order to create a column-like effect within the exception handler.
Correct:
EXCEPTION
WHEN OTHERS THEN
DBMS_Output.Put_Line (SQLERRM);
Incorrect:
EXCEPTION
WHEN OTHERS THEN
DBMS_Output.Put_Line (SQLERRM);
Horizontal Spacing
You’ve probably heard of the obfuscated C contest, where the entrants attempt to cram as much code as possible onto a single line that does some type of work while remaining completely unreadable. If you have any experience maintaining code, you’ve probably seen more than a few pieces of code that have a good chance of winning a contest like this. The guidelines presented in this section are an attempt to guide you away from writing hard-to-read code.
• One of the most important elements in creating readable code is the spacing placed around operators. Table D.3 shows common operators and keywords that need to be preceded and followed by a space when they are used in expressions.
Table D.3 Operators and keywords to be preceded and followed by a space when used in expressions. ________________________________________
+ - * /
& < > =
!= <= >= :=
=> || .. :
<> IN OUT AND
OR NOT NULL
Often more than one of the operators and keywords shown in Table D.3 will be adjacent to each other inside an expression. In this instance, it is recommended that only one space lie between the two operators/identifiers. For example:
IF (vMajor IS NOT NULL) THEN
• Spaces should precede and follow character (') literals.
SELECT first_name || ' ' || middle_name || ' ' || last_name
'student_name'
FROM STUDENTS
WHERE ssn = 999999999;
• Do not leave any blank spaces preceding or following the ** operator.
nRaisedNum := nNum**nPower;
• Do not leave blank spaces before or after the plus (+) and minus (-) signs when used as unary operators.
nNumber := -nSecondNumber;
nNumber := +nSecondNumber;
• Do not use spaces between multiple parentheses or semicolons (;). Always precede the first opening parenthesis of a set with a space.
AND (((x < 5) AND (y < 5))
OR ((x > 5) AND (y > 5)));
Vertical Spacing
Vertical spacing helps distance elements in the code from one another, reducing the visual clutter above and below statements. To create appropriate vertical spacing for your code, place a blank line in the locations described in the following list:
• Before lines containing the keywords IF, ELSE, ELSIF, and EXCEPTION. If the line is preceded by a comment, place the blank line before the comment instead of before the line of text.
--
-- If the student's grade point average meets the criteria for
-- mandatory academic counseling, add the student's name and social
-- security number to the list.
--
IF (nRealGPA < 1.5) THEN
--
-- We also want to consider students who are failing two or more
-- classes, even if their GPA is above 1.5.
--
ELSIF Has_Two_Fails (nForSSN => nSSN) THEN
ELSE
END IF;
• Before any line containing the LOOP keyword. Do not place a blank line before source code containing the END LOOP keyword. (As with lines of code containing the IF keyword, keep the comments for a line of code with the comment by placing a blank line before the comment.)
--
-- For each student returned by the query, add the student's social
-- security number to the PL/SQL table.
--
FOR Students_rec IN Students_cur LOOP
END LOOP;
• Before each exception after the first declared within the EXCEPTION section of a PL/SQL block.
EXCEPTION
WHEN NO_DATA_FOUND THEN
WHEN TOO_MANY_ROWS THEN
WHEN OTHERS THEN
• Before and after the variable, constant, and type declarations for a PL/SQL block.
PROCEDURE Update_Student_GPA (nSSN IN number)
IS
BEGIN
;
END Update_Student_GPA;
• Following the declaration of the procedure and its parameters.
PROCEDURE Update_Student_GPA (nSSN IN number)
IS
• Do not place an empty line before a line containing the END IF keyword. Do place blank lines after the last line of code containing the END IF keyword.
IF (some expression) THEN
IF (some expression) THEN
IF (some expression) THEN
END IF;
END IF;
END IF;
Named Vs. Positional Notation
Procedures and functions should always be called using named notation for their parameters. This helps identify the data that is being passed to the stored PL/SQL object (assuming that the identifiers chosen for the parameters are meaningful). Place only one parameter on each line of the call:
DaysBetween (dStartDate => dEnrolledDate,
dEndDate => dGraduationDate,
nGPA => nFinalGPA,
nDaysBetween => nDuration);
Statements Per Line And Line Width
Place only one statement (or part thereof) per line of code. Break compound statements over multiple lines. Do not exceed a maximum line width of 80 characters (including indentation). Due to space limitations, the lines in this book are limited to a maximum of 70 characters.
If your code has a complex equation or formula that is expressed using a single statement or operation, consider breaking the code into several smaller statements to make the operations less intimidating. The equation will be much easier to debug; in addition, the process of breaking the equation into subsections will increase your awareness of any mistakes that you make.
SQL Statement Formatting Rules
The formatting of SQL statements is one of the few areas in which this standard can be “the law.” All SQL statements should conform precisely to these standards to the maximum extent possible; even slight deviations can have a performance impact on an Oracle database.
Oracle caches individual SQL statements within its shared global area (SGA). When a new statement is issued, Oracle does a block comparison of the statement against statements in the SGA. If a match is found, Oracle re-executes the stored version of the statement, rather than parsing the new statement and then executing it.
For instance, presume that the SGA contains the following SQL statement:
SELECT name, ssn, grade
FROM STUDENT_GRADES
WHERE grade > 90;
Now presume that someone executes this statement:
SELECT name, ssn, grade
FROM STUDENT_GRADES
WHERE grade > 90;
The second statement, although functionally identical to the statement already existing in the SGA, would not find a match in the SGA and would be parsed to determine the proper execution plan for the statement, thus increasing the overhead of the statement and slowing the response to the user.
To make matters worse, the first (and the correct) statement might be pushed out of the SGA to make room for the new statement, further impacting the processing of code that does follow the standard. The placement of a single space or shifting the case of a single character will force an SQL statement to be parsed and placed in the SGA instead of finding a matching statement in the SGA, due to the nature of the block comparison algorithm that Oracle uses.
Admittedly, the time required to parse a single statement to determine an execution plan is very small, but with tens or hundreds of users hitting the database at the same time, those milliseconds add up very quickly. Performance improvements are sometimes achieved in one fell swoop, but a lot of performance tuning work is the result of painstakingly wringing small improvements out of a lot of different pieces of code.
A number of examples are listed in the following sections. In each section, one example is labeled Correct and the other examples are labeled Incorrect. The example labeled correct is the only acceptable format for statements of the type, even though the other statements shown (and many variations not shown) are functionally equivalent to the “correct” statement.
DELETE Statements
Correct:
DELETE
FROM STUDENTS
WHERE ssn = 999999999;
Incorrect:
DELETE
FROM STUDENTS
WHERE ssn = 999999999;
Incorrect:
DELETE STUDENTS WHERE ssn = 999999999;
INSERT Statements
Correct:
INSERT
INTO STUDENTS
(ssn,
first_name,
last_name,
...
most_recent_gpa)
VALUES (999999999,
'Roger',
'Smith',
...
NULL);
Incorrect:
INSERT
INTO STUDENTS (ssn, first_name, last_name,... most_recent_gpa)
VALUES (999999999, 'Roger', 'Smith',...NULL);
Incorrect:
INSERT INTO STUDENTS
VALUES (999999999, 'Roger', 'Smith', ... NULL);
Incorrect:
INSERT INTO STUDENTS VALUES (999999999, 'Roger', 'Smith',...NULL);
SELECT Statements
Correct:
SELECT last_name, first_name, middle_name, ssn, most_recent_gpa
FROM STUDENTS
WHERE ssn = 999999999;
Incorrect:
SELECT last_name, first_name FROM STUDENTS
WHERE
ssn = 999999999;
Incorrect:
SELECT ssn, most_recent_gpa
FROM STUDENTS
WHERE most_recent_gpa < 2.0;
UPDATE Statements
Correct:
UPDATE STUDENTS
SET apartment_number = 'H',
street_address = '16 Northwest Main Street'
WHERE ssn = 999999999;
Incorrect:
UPDATE STUDENTS
SET apartment_number = 'H',
street_address = '16 Northwest Main Street'
WHERE ssn = 999999999;
Incorrect:
UPDATE STUDENTS
SET apartment_number = 'H', street_address = '16 Northwest Main
Street'
WHERE ssn = 999999999;
PL/SQL Naming Conventions
Using a set of naming conventions for PL/SQL objects tends to create more meaningful identifiers. This section of the standard will come into play most often when creating identifiers (variables and constants) inside blocks of code.
It is common to utilize abbreviations to shorten identifiers. When doing so, the abbreviations should be meaningful and used consistently (e.g., do not use both ADDR and ADRS as an abbreviation for ADDRESS).
Database Triggers
Database triggers are named using this convention:
table_name + trigger_type_extension
In this example, table_name is the name of the trigger’s base table, and trigger_type_extension represents one of the four types of database triggers listed in Table D.4, plus one or more of the letters shown in Table D.5 to indicate which DML statements cause the trigger to fire.
Table D.4 The four types of database triggers. ________________________________________
Trigger Type Extension Fires
Before statement _B Once, before the DML statement acting on the table.
Before row _BR Once for each row affected by the DML statement, before the DML statement is executed.
After row _AR Once for each row affected by the DML statement, after the DML statement is executed.
After statement _A Once, after the DML statement has finished executing.
Table D.5 Modifications to trigger_type_extension to indicate DML statements handled by the trigger. ________________________________________
Trigger Type Extension DML Statement
D The trigger fires when a DELETE statement modifies the base table.
I The trigger fires when an INSERT statement modifies the base table.
U The trigger fires when an UPDATE statement modifies the base table.
Thus, a BEFORE INSERT or UPDATE row level trigger on the STUDENTS table would be named STUDENTS_BRIU.
If the length of the trigger name exceeds 30 characters when following this standard, abbreviate the name of the trigger’s base table to create the name of the trigger. Under no circumstances should the trigger type extension be abbreviated.
Identifiers
When declaring variables and constants, the developer should preface a meaningful identifier with one of the prefixes shown in Table D.6.
Table D.6 Datatype prefixes for use in identifiers. ________________________________________
Datatype Prefix Example
binary_integer bi biArrayIndex
boolean b bStudentQualifiesForAid
char c cYesOrNo
date d dEnrolledDate
exception x xTABLE_DOES_NOT_EXIST
integer i iCoursesCarried
long l lComments
longraw lr lrStudentPhoto
natural na naArrayIndex
number n nRemainingBalance
raw r rStudentPhoto
rowid row rowStudent
varchar2 v vStudentFirstName
Identifiers should always use mixed-case and capital letters to indicate separation of elements within an identifier. Thus, a variable of type varchar2 that holds a student’s first name would be vStudentFirstName.
The identifiers used for explicitly declared cursors should be meaningful; the suffix _cur should be appended to the identifier. For example:
CURSOR Students_cur
IS
SELECT first_name, middle_name, last_name, overall_gpa, most_recent_gpa
FROM STUDENTS;
Identifiers declared using %TYPE should still include a datatype prefix as part of the identifier name:
nStudentSSN STUDENTS.ssn%TYPE;
Identifiers declared using %ROWTYPE should be named like the object that is lending the variable its structure. These identifiers should always include the _rec suffix as part of the identifier:
Students_rec STUDENTS%ROWTYPE;
FailingStudents_rec FailingStudents_cur%ROWTYPE;
Procedures And Functions
Stored procedures and functions should be named first by the type of action the object performs and then by the object of that action. For instance, a procedure that calculates interest on a student’s remaining balance would be named Calculate_Balance_Interest.
Packages
Packages should be named in accordance with the general purpose of the procedures and functions contained within the package. For instance, a package containing routines used to calculate a student’s GPA would be named GPA_Calculations.
Written Documentation
All documentation pertaining to system design should follow the conventions identified in this standard. This applies especially to pseudocode that is used to document stored PL/SQL objects.
In addition, all written documentation should reference calls to built-in and developer-written procedures and functions using a set of parentheses, as in “will call the Calculate_Semester_GPA() procedure”. The names of built-in and developer-written code modules should also be referenced in bold.
Taking Advantage Of Standardized Code
Oracle stores the source code for PL/SQL objects inside the ALL_SOURCE view of the DBA_SOURCE table in the SYS schema. This allows you to query the most recent source code for a procedure or function from the database.
If the coding standards are followed fairly closely, it’s possible to write scripts that work with your source code to assist you in your documentation efforts. Included on the CD-ROM is a script that takes full advantage of the coding standards to collect information about and document source code.
The Build_SUID_Matrix Package
Oracle stores dependencies in the ALL_DEPENDENCIES view, but this information exists only at the object-to-object and object-to-table levels. The view cannot, for instance, state precisely which objects perform INSERT statements on a specific table.
The Build_SUID_Matrix package was designed to locate all references to tables within a specified PL/SQL object. The procedures and functions in the package locate table references and sort the references by type (SELECT, INSERT, UPDATE, DELETE, and %TYPE or %ROWTYPE). The package populates the SUID_MATRIX table with this information.
Once the SUID_MATRIX table is fully populated, a query can be run to see precisely which objects access a specified table or which tables a specified object references. This is particularly useful when:
• Examining the impact of creating a new index.
• Examining the impact of altering a table’s structure.
• Determining which objects perform a particular type of operation against a table.
Improving The Build_SUID_Matrix Package
There are a number of potential improvements that can be made to the Build_SUID_Matrix package:
• Improving the level of detail determined to the column level. Knowing which objects modify data in a table is very useful, but if 20 routines update a table and only a few routines update a particular column that has a suspect value, it’s even quicker to find the routines that modify the value in that column.
• Recognizing objects within packages. The current implementation of the package only recognizes objects to the package level and doesn’t differentiate between procedures and functions within the package.
The primary purpose of coding standards has always been to make maintenance easier for developers. In order to satisfy this requirement, PL/SQL coding standards must address several areas of the development process. Table D.1 displays some of the development areas that PL/SQL coding standards address.
Table D.1 Development areas addressed by coding standards. ________________________________________
Development Area Includes
Vertical spacing Spacing between statements; spacing between procedures and functions within package bodies.
Horizontal spacing Spacing between identifiers and operators; number of statements per line; maximum line width.
Procedural calls Use of positional or named notation when calling stored PL/SQL objects.
Commenting Type of comments to be used and the frequency, spacing, positioning, and content of comments.
Code reuse/modularity Contents of procedures and functions; organization of procedures and functions into packages.
Identifiers Rules for naming identifiers; rules for naming stored PL/SQL objects.
SQL statements Formatting rules for embedded SQL statements.
Performance Performance tips (particularly for embedded SQL statements).
Debugging A standard method of handling exceptions inside PL/SQL objects as well as inside other applications that call stored PL/SQL objects.
Testability Rules for writing stored PL/SQL objects so that unit testing can be accomplished.
Development environment Rules determining what tools will be used for application development and what processes must be followed by developers.
Capitalization Rules determining which keywords will be used in UPPER case, Mixed case, and lower case.
Conformance to standards Rules designating when it is allowable for code to not agree with the coding standard.
Documentation Rules that designate whether the coding standards will be applied to design documents and what type of documentation must exist for particular routines.
The level of specifics contained in a coding standard should be fairly strict. While this may seem to be a burden during the development process, the people who do maintenance down the line will appreciate adherence to the standards.
The best way to ensure adherence to standards is to use structured peer review when a code module is completed. If the code is readable, the peer review process will flow more smoothly, and the reviewers will be able to concentrate their review time on understanding the intimacies of code rather than on deciphering an entry for the obfuscated PL/SQL contest! Peer reviews also provide a last line of defense against “sleeper” bugs (often overlooked by even the best developers), which are found a lot more easily when the code is readable.
If your organization does some or all of its own SQL and PL/SQL training, you should make an effort to incorporate standards training into your course materials.
I hope you find the examples presented in this text easy to read and understand. I also hope that you notice the consistency of style in the way examples appear. Every piece of code on the CD-ROM and in the text conforms to the following coding standard.
A Sample SQL And PL/SQL Coding Standard
This document defines the SQL and PL/SQL environment and programming standards and procedures for
These PL/SQL coding standards were written to allow for consistency in PL/SQL code written by various developers while allowing for some individual styles and preferences to be expressed. The central purpose of any coding standard for SQL and PL/SQL must deal with database performance, clarity of code, and maintainability of code. This standard should be considered a guideline for developing easily maintainable SQL and PL/SQL applications in a high-performance Oracle database.
Developers should attempt to meet the spirit of this document by applying good judgment, rather than strictly adhering to the letter of the standard. This standard applies to all developer-written SQL and PL/SQL code (including scripts, stored procedures and functions, database triggers, and stored packages). Generated code is not governed by this standard.
This document is a living document that evolves based on the experiences of you, the developer. You should be aware that changes may occur to this document in the future, based on your (or other developers’) experiences and insights.
The Development Environment And Processes
This section of the standard is highly dependent on the nature of your organization; therefore, I will only give suggestions on the types of material that should be included in this section of your coding standard. The Development Environment And Processes section of the standard should address the following issues:
• Your version control processes—A good working knowledge of PL/SQL is important to a developer, but the inability of a single developer to follow version control processes could be disastrous!
• Standard tools for your development efforts—If you have a standard configuration for your tools, it should be described in painstaking detail. If your tools support central administration of this configuration, so much the better.
• Peer review practices—Many organizations have formal peer review processes in place to validate the quality of design documents, code, and test results. If your organization uses these processes, the coding standard should (at the very least) point developers to your process documents.
• Documentation—Describe the documentation required when developers create new code and what documents need to be updated when maintenance is performed. Provide reasonable facsimiles of this documentation (or excerpts from real documents that satisfy your requirements).
• Testing standards in place—Provide examples of thorough test scripts and plans. Tests for each aspect of your system (front end, stored procedures and functions, packages, and database triggers) should be discussed in detail.
• Standard routines—You should describe whether standard routines, such as error handling, help system calls, and so forth, are available and when these routines should be used. Provide a detailed explanation of each standard call and its interfaces.
These issues can be addressed in appendices to your coding standard or in other documents, as long as your developers receive the necessary information.
________________________________________
References To Other Documents
I personally favor one stop shopping; pack the standard with as much information as possible and reduce the amount of time developers spend tracking down other documents.
________________________________________
Programming Design Standards
Developers should design for modularity. Black box is a term often used in conjunction with modules; each module should perform one (and only one) function using a defined interface and produce a predictable result. So long as the interface for a code module is not changed, the code module may be altered without affecting outside code.
Each module contains one or more routines (and the data structures and variables needed to support the routines). PL/SQL allows developers to implement modularity through the use of packages, which can contain procedures and functions as well as global type, variable, and constant declarations.
Stored functions that use parameters of the IN OUT and OUT types are not allowed. Stored functions should use only the RETURN statement to return a value. Developers are encouraged to identify routines that can be reused. This code can be centralized, tested, and used by other developers to improve the reliability of system code and to reduce development time for more complex modules.
________________________________________
The Modularity Ombudsman
If your organization consists of ten or more people, it might be a good idea to appoint a “Modularity Ombudsman” who has some experience with SQL and PL/SQL. The responsibilities of this position include:
• Identifying code segments that can be reused
• Developing and testing reusable modules
• Documenting modules and promoting their use
The modularity ombudsman should also take part in peer review for new modules to help increase code reuse throughout your project.
________________________________________
Headers
A header should appear at the start of any script, procedure, function, package body, or package spec. Consider this template header:
-- *****************************************************************
-- Description: Describe the purpose of the object. If necessary,
-- describe the design of the object at a very high level.
--
-- Input Parameters:
--
-- Output Parameters:
--
-- Error Conditions Raised:
--
-- Author:
--
-- Revision History
-- Date Author Reason for Change
-- ----------------------------------------------------------------
-- 03 JAN 1997 J.Schmoe Created.
-- *****************************************************************
________________________________________
The Usefulness Of Headers
There are some people who dislike headers or feel that a header is a useless burden to place on a developer. I disagree: At no other point in the code are provisions made for documenting the overall purpose, logic, and interface of a module. In my opinion, a header is the most essential documentation for any piece of stored code.
________________________________________
Formatting Guidelines For SQL And PL/SQL Statements
These guidelines are provided to give code a generally consistent appearance, including indentation, horizontal alignment, and vertical alignment. Adherence to these standards will make code more readable and more easily understood when maintenance is necessary.
Alignment Of Operators
These guidelines enhance the readability of code by adding white space and clarifying complex expressions.
• Arrange series of statements containing similar operators into columns whenever it will not cause excessive white space and you have sufficient room to do so.
Correct:
vFirstName := 'Roger';
vLastName := 'Smith';
vSSN := 999999999;
Incorrect:
vFirstName := 'Roger';
vLastName := 'Smith';
vSSN := 999999999;
• Always use parentheses in expressions containing more than one identifier or literal. This clarifies code for inexperienced developers who are not familiar with operator precedence and helps eliminate the possibility that you’ve overlooked something in your equation.
Correct:
IF (nSSN < 2.5) THEN
END IF;
Incorrect:
IF nSSN < 2.5 THEN
END IF;
• Align the IN and OUT keywords in columns when defining the interface for a procedure or function.
Correct:
PROCEDURE Days_Between (dStartDate IN date,
dEndDate IN date,
nGPA IN OUT number,
nDaysBetween OUT number)
Incorrect:
PROCEDURE Days_Between (dStartDate IN date,
dEndDate IN date,
nGPA IN OUT number,
nDaysBetween OUT number)
• When calling a procedure or function, align the parameters into a column. This reduces the visual clutter around the call, making it stand out from the rest of the code.
Correct:
DaysBetween (dStartDate => dEnrolledDate,
dEndDate => dGraduationDate,
nGPA => nFinalGPA,
nDaysBetween => nDuration);
Incorrect:
DaysBetween (dStartDate => dEnrolledDate,
dEndDate => dGraduationDate,
nGPA => nFinalGPA,
nDaysBetween => nDuration);
Capitalization
Table D.2 contains a list of keywords that should always be fully capitalized when referenced in code. Some of these keywords are commonly used reserved words; reserved words that do not appear on this list should be capitalized as well.
Table D.2 Capitalize these keywords. ________________________________________
ALL FALSE MINUS ROWTYPE
AND FETCH NOT SELECT
AS FOR NOTFOUND SET
BEGIN FOUND NULL SQLCODE
BETWEEN FROM OPEN SQLERRM
BODY FUNCTION OR TABLE
CLOSE GOTO ORDER BY THEN
COMMIT GROUP BY OUT TYPE
CONSTANT HAVING PACKAGE UNION
CREATE IF PROCEDURE UNION ALL
DECLARE IN RAISE UPDATE
DELETE INSERT REPLACE VALUES
ELSE INTERSECT RETURN VIEW
ELSIF INTO ROLLBACK WHEN
END IS ROWCOUNT WHERE
EXCEPTION LIKE ROWID WHILE
EXIT LOOP ROWNUM
The keyword REPLACE is to be used in uppercase only when used as part of the CREATE OR REPLACE clause that is used to create a stored PL/SQL object. Calls to the SQL function replace() should not be presented in uppercase.
In addition to the keywords presented in Table D.2, fully capitalize all of the following:
• The names of all standard exceptions (NO_DATA_FOUND, OTHERS, TOO_MANY_ROWS), and all user-defined exceptions.
• The names of all constants and all user-defined datatypes.
• All acronyms (ANSI, ASCII, HUD, NASA, NOAA, YMCA, and so forth).
• The names of all tables, snapshots, and views, as well as the aliases given to these objects in queries.
• The names of all database triggers.
Use mixed case to refer to the names of user-defined procedures and functions (functions provided by SQL*Plus and PL/SQL are still referenced in lower case). For example:
Calculate_GPA
DBMS_Output.Put_Line
Optionally, use mixed case for user-defined identifiers. If you choose this method, use capital letters to help make the identifier names more meaningful by visually breaking variable names into words; here are some examples:
vString
nBaseSalary
nGPA
iTardyDays
iClassNumber
lComments
rStudentPhoto
All text not handled by these rules should use lowercase. Consider the following:
CREATE OR REPLACE PACKAGE My_Sample_Package AS
PROCEDURE My_Sample_Procedure (nParameter1 IN number,
nParameter2 OUT number)
IS
YES CONSTANT char (1) := 'Y';
BEGIN
IF (some expression) THEN
replace (vString, chr (9), ' ');
END IF;
END My_Sample_Procedure;
END My_Sample_Package;
Comments
As much as some developers dislike the task, commenting code is essential if the code is going to be maintained. There are a number of steps that can be taken to make comments less necessary:
• Use meaningful identifiers for variables, constants, and parameters. If you use abbreviations to compose identifiers, use the abbreviations consistently (e.g., don’t use both ADDR and ADRS to signify ADDRESS).
• Use the named parameter style of executing procedures and functions. This is especially effective if both the parameters and the variables passed to the stored PL/SQL object have meaningful identifiers.
• Comments about revisions belong in the prologue, not in the body of the module.
________________________________________
TIP: Commenting Changes And Problem Tracking
If you’re using a problem-tracking system on your project, it’s better to reference a particular report from that system and provide a brief summary of the changes made to solve that problem. Don’t attempt to include all the information about the problem in the prologue; that’s why you bought a problem tracking system!
________________________________________
• Break complex equations and formulas into several smaller statements.
• Reuse existing functions and procedures to accomplish your tasks. Identify code that can be reused.
There are a number of locations in PL/SQL code where comments should almost always be used, including the following instances:
• Before each loop structure.
• Before each BEGIN…END sub-block.
• Before each conditional logic expression (IF
• Before any other logically significant statements.
Do not comment each line of code! Only comment important parts of your code, explaining why the code is written in a particular way. Explain business rules if possible. Never use a comment to restate the actions of a piece of code.
PL/SQL supports the following two styles of commenting:
/* We need to determine which students are in academic trouble. */
-- We need to determine which students are in academic trouble.
PL/SQL does not support the nesting of C-style comments; you cannot comment out a C-style comment using other C-style comments. For this reason, it is strongly recommended that only the double-dash (--) style of commenting be used except when commenting out blocks of code.
The exception to this rule is inside 3GL programs that use the Oracle Precompilers. The Oracle Precompilers don’t support single line comments. On these occasions, use the commenting style most appropriate to the 3GL.
If a comment is required, place the comment on the line immediately preceding the line of code. Do not append comments to the end of code; if a comment is warranted by the complexity of the code and you have used meaningful identifiers, the comment should be complicated enough that you need to explain the situation using more than one or two words.
Correct:
--
-- Determine which students might be in trouble academically. We want
-- to help them perform better in school.
--
IF (some condition) THEN
Incorrect:
IF (some condition) THEN -- who's got bad grades?
All comments should use proper grammar, punctuation, and spelling. Comments should be complete, coherent sentences.
________________________________________
TIP: Volume Of Comments
As a general rule, about one-third of your final code should be comments. This figure often varies depending on the size and complexity of the code, but is an excellent rule of thumb.
________________________________________
Indentation
The most important element in readable code is consistent indentation, which illustrates clearly the logic flow of a procedure. Consider these blocks of code:
IF (x < 7) THEN
IF (y < 0) THEN
END IF;
ELSIF (x > 10) THEN
ELSE
END IF;
IF (x < 7) THEN
IF (y < 0) THEN
END IF;
ELSIF (x > 10) THEN
ELSE
END IF;
Horizontal alignment in the second block of code makes it much easier to follow, even though it is syntactically and functionally identical to the first block of code.
You should not use more than three or four levels of indentation in any block of code. If this many levels of indentation become necessary, consider breaking the code into smaller modules. Too many levels of indentation is almost as bad as no indentation at all.
• Code should always be indented consistently, using three spaces for each level of indentation. Variable, type, and constant declarations should all be indented to the first level of indentation. Do not use tab characters.
IF (some expression) THEN
IF (some expression) THEN
IF (some expression) THEN
ELSIF (some expression) THEN
END IF;
END IF;
END IF;
• Statements following the WHEN clause of an exception handler should be indented five spaces, in order to create a column-like effect within the exception handler.
Correct:
EXCEPTION
WHEN OTHERS THEN
DBMS_Output.Put_Line (SQLERRM);
Incorrect:
EXCEPTION
WHEN OTHERS THEN
DBMS_Output.Put_Line (SQLERRM);
Horizontal Spacing
You’ve probably heard of the obfuscated C contest, where the entrants attempt to cram as much code as possible onto a single line that does some type of work while remaining completely unreadable. If you have any experience maintaining code, you’ve probably seen more than a few pieces of code that have a good chance of winning a contest like this. The guidelines presented in this section are an attempt to guide you away from writing hard-to-read code.
• One of the most important elements in creating readable code is the spacing placed around operators. Table D.3 shows common operators and keywords that need to be preceded and followed by a space when they are used in expressions.
Table D.3 Operators and keywords to be preceded and followed by a space when used in expressions. ________________________________________
+ - * /
& < > =
!= <= >= :=
=> || .. :
<> IN OUT AND
OR NOT NULL
Often more than one of the operators and keywords shown in Table D.3 will be adjacent to each other inside an expression. In this instance, it is recommended that only one space lie between the two operators/identifiers. For example:
IF (vMajor IS NOT NULL) THEN
• Spaces should precede and follow character (') literals.
SELECT first_name || ' ' || middle_name || ' ' || last_name
'student_name'
FROM STUDENTS
WHERE ssn = 999999999;
• Do not leave any blank spaces preceding or following the ** operator.
nRaisedNum := nNum**nPower;
• Do not leave blank spaces before or after the plus (+) and minus (-) signs when used as unary operators.
nNumber := -nSecondNumber;
nNumber := +nSecondNumber;
• Do not use spaces between multiple parentheses or semicolons (;). Always precede the first opening parenthesis of a set with a space.
AND (((x < 5) AND (y < 5))
OR ((x > 5) AND (y > 5)));
Vertical Spacing
Vertical spacing helps distance elements in the code from one another, reducing the visual clutter above and below statements. To create appropriate vertical spacing for your code, place a blank line in the locations described in the following list:
• Before lines containing the keywords IF, ELSE, ELSIF, and EXCEPTION. If the line is preceded by a comment, place the blank line before the comment instead of before the line of text.
--
-- If the student's grade point average meets the criteria for
-- mandatory academic counseling, add the student's name and social
-- security number to the list.
--
IF (nRealGPA < 1.5) THEN
--
-- We also want to consider students who are failing two or more
-- classes, even if their GPA is above 1.5.
--
ELSIF Has_Two_Fails (nForSSN => nSSN) THEN
ELSE
END IF;
• Before any line containing the LOOP keyword. Do not place a blank line before source code containing the END LOOP keyword. (As with lines of code containing the IF keyword, keep the comments for a line of code with the comment by placing a blank line before the comment.)
--
-- For each student returned by the query, add the student's social
-- security number to the PL/SQL table.
--
FOR Students_rec IN Students_cur LOOP
END LOOP;
• Before each exception after the first declared within the EXCEPTION section of a PL/SQL block.
EXCEPTION
WHEN NO_DATA_FOUND THEN
WHEN TOO_MANY_ROWS THEN
WHEN OTHERS THEN
• Before and after the variable, constant, and type declarations for a PL/SQL block.
PROCEDURE Update_Student_GPA (nSSN IN number)
IS
BEGIN
END Update_Student_GPA;
• Following the declaration of the procedure and its parameters.
PROCEDURE Update_Student_GPA (nSSN IN number)
IS
• Do not place an empty line before a line containing the END IF keyword. Do place blank lines after the last line of code containing the END IF keyword.
IF (some expression) THEN
IF (some expression) THEN
IF (some expression) THEN
END IF;
END IF;
END IF;
Named Vs. Positional Notation
Procedures and functions should always be called using named notation for their parameters. This helps identify the data that is being passed to the stored PL/SQL object (assuming that the identifiers chosen for the parameters are meaningful). Place only one parameter on each line of the call:
DaysBetween (dStartDate => dEnrolledDate,
dEndDate => dGraduationDate,
nGPA => nFinalGPA,
nDaysBetween => nDuration);
Statements Per Line And Line Width
Place only one statement (or part thereof) per line of code. Break compound statements over multiple lines. Do not exceed a maximum line width of 80 characters (including indentation). Due to space limitations, the lines in this book are limited to a maximum of 70 characters.
If your code has a complex equation or formula that is expressed using a single statement or operation, consider breaking the code into several smaller statements to make the operations less intimidating. The equation will be much easier to debug; in addition, the process of breaking the equation into subsections will increase your awareness of any mistakes that you make.
SQL Statement Formatting Rules
The formatting of SQL statements is one of the few areas in which this standard can be “the law.” All SQL statements should conform precisely to these standards to the maximum extent possible; even slight deviations can have a performance impact on an Oracle database.
Oracle caches individual SQL statements within its shared global area (SGA). When a new statement is issued, Oracle does a block comparison of the statement against statements in the SGA. If a match is found, Oracle re-executes the stored version of the statement, rather than parsing the new statement and then executing it.
For instance, presume that the SGA contains the following SQL statement:
SELECT name, ssn, grade
FROM STUDENT_GRADES
WHERE grade > 90;
Now presume that someone executes this statement:
SELECT name, ssn, grade
FROM STUDENT_GRADES
WHERE grade > 90;
The second statement, although functionally identical to the statement already existing in the SGA, would not find a match in the SGA and would be parsed to determine the proper execution plan for the statement, thus increasing the overhead of the statement and slowing the response to the user.
To make matters worse, the first (and the correct) statement might be pushed out of the SGA to make room for the new statement, further impacting the processing of code that does follow the standard. The placement of a single space or shifting the case of a single character will force an SQL statement to be parsed and placed in the SGA instead of finding a matching statement in the SGA, due to the nature of the block comparison algorithm that Oracle uses.
Admittedly, the time required to parse a single statement to determine an execution plan is very small, but with tens or hundreds of users hitting the database at the same time, those milliseconds add up very quickly. Performance improvements are sometimes achieved in one fell swoop, but a lot of performance tuning work is the result of painstakingly wringing small improvements out of a lot of different pieces of code.
A number of examples are listed in the following sections. In each section, one example is labeled Correct and the other examples are labeled Incorrect. The example labeled correct is the only acceptable format for statements of the type, even though the other statements shown (and many variations not shown) are functionally equivalent to the “correct” statement.
DELETE Statements
Correct:
DELETE
FROM STUDENTS
WHERE ssn = 999999999;
Incorrect:
DELETE
FROM STUDENTS
WHERE ssn = 999999999;
Incorrect:
DELETE STUDENTS WHERE ssn = 999999999;
INSERT Statements
Correct:
INSERT
INTO STUDENTS
(ssn,
first_name,
last_name,
...
most_recent_gpa)
VALUES (999999999,
'Roger',
'Smith',
...
NULL);
Incorrect:
INSERT
INTO STUDENTS (ssn, first_name, last_name,... most_recent_gpa)
VALUES (999999999, 'Roger', 'Smith',...NULL);
Incorrect:
INSERT INTO STUDENTS
VALUES (999999999, 'Roger', 'Smith', ... NULL);
Incorrect:
INSERT INTO STUDENTS VALUES (999999999, 'Roger', 'Smith',...NULL);
SELECT Statements
Correct:
SELECT last_name, first_name, middle_name, ssn, most_recent_gpa
FROM STUDENTS
WHERE ssn = 999999999;
Incorrect:
SELECT last_name, first_name FROM STUDENTS
WHERE
ssn = 999999999;
Incorrect:
SELECT ssn, most_recent_gpa
FROM STUDENTS
WHERE most_recent_gpa < 2.0;
UPDATE Statements
Correct:
UPDATE STUDENTS
SET apartment_number = 'H',
street_address = '16 Northwest Main Street'
WHERE ssn = 999999999;
Incorrect:
UPDATE STUDENTS
SET apartment_number = 'H',
street_address = '16 Northwest Main Street'
WHERE ssn = 999999999;
Incorrect:
UPDATE STUDENTS
SET apartment_number = 'H', street_address = '16 Northwest Main
Street'
WHERE ssn = 999999999;
PL/SQL Naming Conventions
Using a set of naming conventions for PL/SQL objects tends to create more meaningful identifiers. This section of the standard will come into play most often when creating identifiers (variables and constants) inside blocks of code.
It is common to utilize abbreviations to shorten identifiers. When doing so, the abbreviations should be meaningful and used consistently (e.g., do not use both ADDR and ADRS as an abbreviation for ADDRESS).
Database Triggers
Database triggers are named using this convention:
table_name + trigger_type_extension
In this example, table_name is the name of the trigger’s base table, and trigger_type_extension represents one of the four types of database triggers listed in Table D.4, plus one or more of the letters shown in Table D.5 to indicate which DML statements cause the trigger to fire.
Table D.4 The four types of database triggers. ________________________________________
Trigger Type Extension Fires
Before statement _B Once, before the DML statement acting on the table.
Before row _BR Once for each row affected by the DML statement, before the DML statement is executed.
After row _AR Once for each row affected by the DML statement, after the DML statement is executed.
After statement _A Once, after the DML statement has finished executing.
Table D.5 Modifications to trigger_type_extension to indicate DML statements handled by the trigger. ________________________________________
Trigger Type Extension DML Statement
D The trigger fires when a DELETE statement modifies the base table.
I The trigger fires when an INSERT statement modifies the base table.
U The trigger fires when an UPDATE statement modifies the base table.
Thus, a BEFORE INSERT or UPDATE row level trigger on the STUDENTS table would be named STUDENTS_BRIU.
If the length of the trigger name exceeds 30 characters when following this standard, abbreviate the name of the trigger’s base table to create the name of the trigger. Under no circumstances should the trigger type extension be abbreviated.
Identifiers
When declaring variables and constants, the developer should preface a meaningful identifier with one of the prefixes shown in Table D.6.
Table D.6 Datatype prefixes for use in identifiers. ________________________________________
Datatype Prefix Example
binary_integer bi biArrayIndex
boolean b bStudentQualifiesForAid
char c cYesOrNo
date d dEnrolledDate
exception x xTABLE_DOES_NOT_EXIST
integer i iCoursesCarried
long l lComments
longraw lr lrStudentPhoto
natural na naArrayIndex
number n nRemainingBalance
raw r rStudentPhoto
rowid row rowStudent
varchar2 v vStudentFirstName
Identifiers should always use mixed-case and capital letters to indicate separation of elements within an identifier. Thus, a variable of type varchar2 that holds a student’s first name would be vStudentFirstName.
The identifiers used for explicitly declared cursors should be meaningful; the suffix _cur should be appended to the identifier. For example:
CURSOR Students_cur
IS
SELECT first_name, middle_name, last_name, overall_gpa, most_recent_gpa
FROM STUDENTS;
Identifiers declared using %TYPE should still include a datatype prefix as part of the identifier name:
nStudentSSN STUDENTS.ssn%TYPE;
Identifiers declared using %ROWTYPE should be named like the object that is lending the variable its structure. These identifiers should always include the _rec suffix as part of the identifier:
Students_rec STUDENTS%ROWTYPE;
FailingStudents_rec FailingStudents_cur%ROWTYPE;
Procedures And Functions
Stored procedures and functions should be named first by the type of action the object performs and then by the object of that action. For instance, a procedure that calculates interest on a student’s remaining balance would be named Calculate_Balance_Interest.
Packages
Packages should be named in accordance with the general purpose of the procedures and functions contained within the package. For instance, a package containing routines used to calculate a student’s GPA would be named GPA_Calculations.
Written Documentation
All documentation pertaining to system design should follow the conventions identified in this standard. This applies especially to pseudocode that is used to document stored PL/SQL objects.
In addition, all written documentation should reference calls to built-in and developer-written procedures and functions using a set of parentheses, as in “will call the Calculate_Semester_GPA() procedure”. The names of built-in and developer-written code modules should also be referenced in bold.
Taking Advantage Of Standardized Code
Oracle stores the source code for PL/SQL objects inside the ALL_SOURCE view of the DBA_SOURCE table in the SYS schema. This allows you to query the most recent source code for a procedure or function from the database.
If the coding standards are followed fairly closely, it’s possible to write scripts that work with your source code to assist you in your documentation efforts. Included on the CD-ROM is a script that takes full advantage of the coding standards to collect information about and document source code.
The Build_SUID_Matrix Package
Oracle stores dependencies in the ALL_DEPENDENCIES view, but this information exists only at the object-to-object and object-to-table levels. The view cannot, for instance, state precisely which objects perform INSERT statements on a specific table.
The Build_SUID_Matrix package was designed to locate all references to tables within a specified PL/SQL object. The procedures and functions in the package locate table references and sort the references by type (SELECT, INSERT, UPDATE, DELETE, and %TYPE or %ROWTYPE). The package populates the SUID_MATRIX table with this information.
Once the SUID_MATRIX table is fully populated, a query can be run to see precisely which objects access a specified table or which tables a specified object references. This is particularly useful when:
• Examining the impact of creating a new index.
• Examining the impact of altering a table’s structure.
• Determining which objects perform a particular type of operation against a table.
Improving The Build_SUID_Matrix Package
There are a number of potential improvements that can be made to the Build_SUID_Matrix package:
• Improving the level of detail determined to the column level. Knowing which objects modify data in a table is very useful, but if 20 routines update a table and only a few routines update a particular column that has a suspect value, it’s even quicker to find the routines that modify the value in that column.
• Recognizing objects within packages. The current implementation of the package only recognizes objects to the package level and doesn’t differentiate between procedures and functions within the package.
Using SQL*Loader to Populate the Payables Open InterfaceTables
You can create a SQL*Loader program that produces a flat file containing the invoice details you want to bring into Payables. For detailed information on the tables, refer to Open Interface Import Table Descriptions.
To use SQL*Loader to populate the Payables Open Interface tables:
1. For each invoice, the program should identify the supplier by providing a value for at least one of the following columns: VENDOR_ID, VENDOR_NUM, VENDOR_SITE_ID, or PO_NUMBER. In addition, for each record, you must populate the following required columns in AP_INVOICES_INTERFACE:
INVOICE_ID
INVOICE_NUM
INVOICE_AMOUNT
SOURCE. Make certain that you specify a Source type QuickCode exactly as you entered it in the Payables QuickCodes window.
For each invoice line, include the following values to populate the columns in the AP_INVOICE_LINES_INTERFACE:
INVOICE_ID
LINE_TYPE_LOOKUP_CODE
AMOUNT
In addition, if you are importing foreign currency invoices, include information to populate the following columns in AP_INVOICES_INTERFACE:
EXCHANGE_RATE_TYPE
EXCHANGE_RATE (if you use User-defined rate type)
If this exchange rate information is the same for all invoices and invoice distributions you import, you can include this information in your SQL*Loader control file, instead of entering it separately for each invoice.
Finally, ensure that your invoice flat file has the appropriate information to populate AP_INVOICES_INTERFACE and AP_INVOICE_LINES_INTERFACE as indicated in the preceding table descriptions. If a value is not required for a column, you may leave the column empty.
2. Create a SQL*Loader control file.
Read the SQL*Loader documentation to learn how to write a SQL*Loader control file. The file you write will vary greatly depending on the nature and format of the invoice flat file you use. Your control file must populate AP_INVOICES_INTERFACE and AP_INVOICE_LINES_INTERFACE as indicated in the previous table descriptions. See also: SQL*Loader (ORACLE8 Server Utilities Guide).
Code your control file to populate the SOURCE column in AP_INVOICES_INTERFACE with the Source code exactly as you defined it in the QuickCodes window.
If all your invoices are in the same currency, you can hard code the value for DEFAULT_CURRENCY_CODE in AP_INVOICES_INTERFACE and CURRENCY_CODE in AP_INVOICE_LINES_INTERFACE in your control file.
3. Use the SQL*Loader program you created to load your invoice information from your flat file into the interface tables. Indicate your control file as the SQL*Loader control file, your invoice flat file as the SQL*Loader input file, and your Payables SQL*Plus username and password as the database sign on. For more information see: Oracle8 Server Utilities Guide.
4. Use the Open Interface Invoices window to review and edit the data in the Payables Open Interface tables.
To use SQL*Loader to populate the Payables Open Interface tables:
1. For each invoice, the program should identify the supplier by providing a value for at least one of the following columns: VENDOR_ID, VENDOR_NUM, VENDOR_SITE_ID, or PO_NUMBER. In addition, for each record, you must populate the following required columns in AP_INVOICES_INTERFACE:
INVOICE_ID
INVOICE_NUM
INVOICE_AMOUNT
SOURCE. Make certain that you specify a Source type QuickCode exactly as you entered it in the Payables QuickCodes window.
For each invoice line, include the following values to populate the columns in the AP_INVOICE_LINES_INTERFACE:
INVOICE_ID
LINE_TYPE_LOOKUP_CODE
AMOUNT
In addition, if you are importing foreign currency invoices, include information to populate the following columns in AP_INVOICES_INTERFACE:
EXCHANGE_RATE_TYPE
EXCHANGE_RATE (if you use User-defined rate type)
If this exchange rate information is the same for all invoices and invoice distributions you import, you can include this information in your SQL*Loader control file, instead of entering it separately for each invoice.
Finally, ensure that your invoice flat file has the appropriate information to populate AP_INVOICES_INTERFACE and AP_INVOICE_LINES_INTERFACE as indicated in the preceding table descriptions. If a value is not required for a column, you may leave the column empty.
2. Create a SQL*Loader control file.
Read the SQL*Loader documentation to learn how to write a SQL*Loader control file. The file you write will vary greatly depending on the nature and format of the invoice flat file you use. Your control file must populate AP_INVOICES_INTERFACE and AP_INVOICE_LINES_INTERFACE as indicated in the previous table descriptions. See also: SQL*Loader (ORACLE8 Server Utilities Guide).
Code your control file to populate the SOURCE column in AP_INVOICES_INTERFACE with the Source code exactly as you defined it in the QuickCodes window.
If all your invoices are in the same currency, you can hard code the value for DEFAULT_CURRENCY_CODE in AP_INVOICES_INTERFACE and CURRENCY_CODE in AP_INVOICE_LINES_INTERFACE in your control file.
3. Use the SQL*Loader program you created to load your invoice information from your flat file into the interface tables. Indicate your control file as the SQL*Loader control file, your invoice flat file as the SQL*Loader input file, and your Payables SQL*Plus username and password as the database sign on. For more information see: Oracle8 Server Utilities Guide.
4. Use the Open Interface Invoices window to review and edit the data in the Payables Open Interface tables.
Sunday, November 1, 2009
Exporting OA_Page from Database
Exporting OA_Page from Database
=========================
Connect to db server with putty .go to $JAVA_TOPAnd then execute the follwing command.Page exported to this directory created. $JAVA_TOP/chola/this is the "/oracle/apps/per/selfservice/loa/webui/LoaSummaryPG" page.
Exporting Page
========================
java oracle.jrad.tools.xml.exporter.XMLExporter /oracle/apps/per/selfservice/loa/webui/LoaSummaryPG -rootdir $JAVA_TOP/chola -username apps -password password -dbconnection "(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP) (HOST =dbserver ) (PORT = ))) (CONNECT_DATA = (SID = TEST)))"
Exporting Region
========================
java oracle.jrad.tools.xml.exporter.XMLExporter /oracle/apps/per/selfservice/accruals/webui/AccrualBalanceRN -rootdir $JAVA_TOP/chola -username apps -password password -dbconnection "(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP) (HOST = dbserver) (PORT = ))) (CONNECT_DATA = (SID = TEST)))"
=========================
Connect to db server with putty .go to $JAVA_TOPAnd then execute the follwing command.Page exported to this directory created. $JAVA_TOP/chola/this is the "/oracle/apps/per/selfservice/loa/webui/LoaSummaryPG" page.
Exporting Page
========================
java oracle.jrad.tools.xml.exporter.XMLExporter /oracle/apps/per/selfservice/loa/webui/LoaSummaryPG -rootdir $JAVA_TOP/chola -username apps -password password -dbconnection "(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP) (HOST =dbserver ) (PORT = ))) (CONNECT_DATA = (SID = TEST)))"
Exporting Region
========================
java oracle.jrad.tools.xml.exporter.XMLExporter /oracle/apps/per/selfservice/accruals/webui/AccrualBalanceRN -rootdir $JAVA_TOP/chola -username apps -password password -dbconnection "(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP) (HOST = dbserver) (PORT = ))) (CONNECT_DATA = (SID = TEST)))"
Subscribe to:
Posts (Atom)