Showing posts with label PL/SQL. Show all posts
Showing posts with label PL/SQL. Show all posts

Monday, 17 March 2008

Oracle Package Initialization

Did you notice that Oracle PL/SQL packages can have an initialization section? The initialization part of a package is run only once, the first time you reference the package. Here is an example:

CREATE PACKAGE emp_actions AS
/* Declare externally callable subprograms. */
FUNCTION hire_employee (
ename VARCHAR2,
job VARCHAR2,
mgr REAL,
sal REAL,
comm REAL,
deptno REAL) RETURN INT;
END emp_actions;

CREATE PACKAGE BODY emp_actions AS
number_hired INT; -- visible only in this package

/* Fully define subprograms specified in package. */
FUNCTION hire_employee (
ename VARCHAR2,
job VARCHAR2,
mgr REAL,
sal REAL,
comm REAL,
deptno REAL) RETURN INT IS
new_empno INT;
BEGIN
SELECT empno_seq.NEXTVAL INTO new_empno
FROM dual;
INSERT INTO emp VALUES (new_empno, ename, job,
mgr, SYSDATE, sal, comm, deptno);
number_hired := number_hired + 1;
RETURN new_empno;
END hire_employee;

BEGIN -- initialization part starts here
INSERT INTO emp_audit
VALUES (SYSDATE, USER, 'EMP_ACTIONS');
number_hired := 0;
END emp_actions;


The initialization part of a package is run just once, the first time you reference the package. So, in the last example, only one row is inserted into the database table emp_audit. Likewise, the variable number_hired is initialized only once.

Every time the procedure hire_employee is called, the variable number_hired is updated. However, the count kept by number_hired is session specific. That is, the count reflects the number of new employees processed by one user, not the number processed by all users.

Monday, 10 March 2008

Sending E-Mail in PL/SQL

Here some example code you can use for sending email from within PL/SQL

CREATE OR REPLACE PROCEDURE SEND_MAIL_TCP (
msg_from VARCHAR2 := 'sender@testing.com'
, msg_to VARCHAR
, msg_subject VARCHAR2 := 'E-Mail message from your database'
, msg_text VARCHAR2 := ''
)
IS
c UTL_TCP.CONNECTION;
rc INTEGER;
BEGIN
c := UTL_TCP.OPEN_CONNECTION('localhost', 25); -- open the SMTP port 25 on local machine
rc := UTL_TCP.WRITE_LINE(c, 'HELO localhost');
rc := UTL_TCP.WRITE_LINE(c, 'MAIL FROM: '||msg_from);
rc := UTL_TCP.WRITE_LINE(c, 'RCPT TO: '||msg_to);
rc := UTL_TCP.WRITE_LINE(c, 'DATA'); -- Start message body
rc := UTL_TCP.WRITE_LINE(c, 'Subject: '||msg_subject);
rc := UTL_TCP.WRITE_LINE(c, '');
rc := UTL_TCP.WRITE_LINE(c, msg_text);
rc := UTL_TCP.WRITE_LINE(c, '.'); -- End of message body
rc := UTL_TCP.WRITE_LINE(c, 'QUIT');
UTL_TCP.CLOSE_CONNECTION(c); -- Close the connection
EXCEPTION
WHEN others THEN
RAISE_APPLICATION_ERROR(-20000, 'Unable to send e-mail message from PL/SQL routine.');
END;

Friday, 15 February 2008

dbms_backup_restore Package

The dbms_backup_restore package is used as a PL/SQL command-line interface for replacing native RMAN commands, and it has very little documentation.

The Oracle docs note how to install and configure the dbms_backup_restore package:

“The DBMS_BACKUP_RESTORE package is an internal package created by the dbmsbkrs.sql and prvtbkrs.plb scripts. This package, along with the target database version of DBMS_RCVMAN, is automatically installed in every Oracle database when the catproc.sql script is run. This package interfaces with the Oracle database server and the operating system to provide the I/O services for backup and restore operations as directed by RMAN.”

The docs also note that “The DBMS_BACKUP_RESTORE package has a PL/SQL procedure to normalize filenames on Windows NT platforms.”

Oracle DBA John Parker gives this example of dbms_backup_restore to recover a controlfile:

declare
devtype varchar2(256);
done boolean;
begin
devtype:=dbms_backup_restore.deviceallocate( type=>'sbt_tape',
params=>'ENV=(OB2BARTYPE=Oracle8,OB2APPNAME=rdcs,OB2BARLIST=ORA_RDCS_WEEKLY)',
ident=>'t1');
dbms_backup_restore.restoresetdatafile;
dbms_backup_restore.restorecontrolfileto('D:\oracle\ora81\dbs\CTL1rdcs.ORA');
dbms_backup_restore.restorebackuppiece(
'ORA_RDCS_WEEKLY.dbf', DONE=>done );
dbms_backup_restore.restoresetdatafile;
dbms_backup_restore.restorecontrolfileto('D:\DBS\RDCS\CTL2RDCS.ORA');
dbms_backup_restore.restorebackuppiece(
'ORA_RDCS_WEEKLY.dbf', DONE=>done );
dbms_backup_restore.devicedeallocate('t1');
end;


Here are some other examples of using dbms_backup_restore:
DECLARE
devtype varchar2(256);
done boolean;
BEGIN
devtype := dbms_backup_restore.DeviceAllocate (type => '',ident => 'FUN');
dbms_backup_restore.RestoreSetDatafile;
dbms_backup_restore.RestoreDatafileTo(dfnumber => 1,toname => 'D:\ORACLE_BASE\datafiles\SYSTEM01.DBF');
dbms_backup_restore.RestoreDatafileTo(dfnumber => 2,toname => 'D:\ORACLE_BASE\datafiles\UNDOTBS.DBF');
--dbms_backup_restore.RestoreDatafileTo(dfnumber => 3,toname => 'D:\ORACLE_BASE\datafiles\MYSPACE.DBF');
dbms_backup_restore.RestoreBackupPiece(done => done,handle => 'D:\ORACLE_BASE\RMAN_BACKUP\MYDB_DF_BCK05H2LLQP_1_1', params => null);
dbms_backup_restore.DeviceDeallocate;
END;
/

--restore archived redolog
DECLARE
devtype varchar2(256);
done boolean;
BEGIN
devtype := dbms_backup_restore.DeviceAllocate (type => '',ident => 'FUN');
dbms_backup_restore.RestoreSetArchivedLog(destination=>'D:\ORACLE_BASE\achive\');
dbms_backup_restore.RestoreArchivedLog(thread=>1,sequence=>1);
dbms_backup_restore.RestoreArchivedLog(thread=>1,sequence=>2);
dbms_backup_restore.RestoreArchivedLog(thread=>1,sequence=>3);
dbms_backup_restore.RestoreBackupPiece(done => done,handle => 'D:\ORACLE_BASE\RMAN_BACKUP\MYDB_LOG_BCK0DH1JGND_1_1', params => null);
dbms_backup_restore.DeviceDeallocate;
END;
/

Tuesday, 12 February 2008

Finding Tables References Inside PL/SQL Store Procedure

The Oracle data dictionary tracks the object types referenced in PL/SQL with the dba_dependencies view. To track the dependency among packages and tables, try this dictionary query:

select
referenced_owner,
referenced_name,
referenced_type
from
dba_dependencies
where
name= 'MY_STORE_PROC'
and
owner = 'SCOTT'
order by
referenced_owner, referenced_name, referenced_type;
you can also select the referenced_type, ie. tables

Sunday, 27 January 2008

Getting Table Details with SQL*Plus

You can use SQL*Plus to provide the following details about a table:

  1. Column Details
  2. PRIMARY KEY
  3. INDEXES
  4. FOREIGN KEYS
  5. CONSTRAINTS
  6. ROWCOUNT
  7. Other Tables That REFER to this Table
  8. PARTITIONED COLUMNS
  9. PARTITIONS
  10. TRIGGERS
  11. DEPENDANTS
Use the following code in SQL*Plus to provide this information:
      SET AUTOTRACE OFF
SET TIMING OFF
COLUMN COMMENTS FORMAT A50
COLUMN column_name FORMAT A35
COLUMN Data_Type FORMAT A15
COLUMN DATA_DEFAULT FORMAT A20
COLUMN "PK Column" FORMAT A35
COLUMN "FK Column" FORMAT A20

UNDEF Owner
ACCEPT Owner PROMPT 'Enter Owner :'

UNDEF Table_Name
ACCEPT Table_Name PROMPT 'Enter Table Name :'


SET HEADING OFF

PROMPT
PROMPT Comments for Table &Table_Name.
SELECT COMMENTS
FROM ALL_TAB_COMMENTS
WHERE TABLE_NAME = UPPER('&Table_Name.')
AND Owner = UPPER('&Owner.') ;

SET HEADING ON

PROMPT
PROMPT Column Details for Table &Table_Name.

SELECT
ROWNUM "Sr No", T.COLUMN_NAME , T.Data_Type , T.DATA_LENGTH,
DECODE(T.Nullable, 'N' , 'NOT NULL' , 'Y', ' ') NULLABLE , T.Data_Default , C.Comments
FROM
ALL_TAB_COLS T , All_Col_Comments C
WHERE
T.OWNER = C.OWNER
AND T.TABLE_NAME = C.TABLE_NAME
AND T.COLUMN_NAME = C.COLUMN_NAME
AND T.TABLE_NAME = UPPER('&Table_Name.')
AND T.Owner = UPPER('&Owner.') ;


PROMPT
PROMPT PRIMARY KEY for Table &Table_Name.

select COLUMN_NAME
FROM ALL_CONS_COLUMNS
WHERE TABLE_NAME = UPPER('&Table_Name.')
AND Owner = UPPER('&Owner.')
AND CONSTRAINT_NAME = ( SELECT CONSTRAINT_NAME
FROM ALL_CONSTRAINTS
WHERE TABLE_NAME = UPPER('&Table_Name.')
AND CONS! TRAINT_T YPE = 'P'
AND Owner = UPPER('&Owner.')
)
ORDER BY POSITION
/

PROMPT
PROMPT INDEXES for Table &Table_Name.

BREAK ON INDEX_NAME ON UNIQUENESS SKIP 1

SELECT I.INDEX_NAME , C.COLUMN_NAME , I.UNIQUENESS
FROM ALL_IND_COLUMNS C , ALL_INDEXES I
WHERE C.INDEX_NAME = I.INDEX_NAME
AND C.TABLE_NAME = I.TABLE_NAME
AND I.TABLE_NAME = UPPER('&Table_Name.')
AND I.Owner = UPPER('&Owner.')
AND C.Table_Owner = UPPER('&Owner.')
AND NOT EXISTS ( SELECT 'X'
FROM ALL_CONSTRAINTS
WHERE CONSTRAINT_NAME = I.INDEX_NAME
AND Owner = UPPER('&Owner.')
)
ORDER BY INDEX_NAME , COLUMN_POSITION
/

CLEAR BREAKS

PROMPT
PROMPT FOREIGN KEYS for Table &Table_Name.

BREAK ON CONSTRAINT_NAME ON TABLE_NAME ON R_CONSTRAINT_NAME SKIP 1
COLUMN POSITION NOPRINT

SELECT UNIQUE A.CONSTRAINT_NAME,
C.COLUMN_NAME "FK Column" ,
B.TABLE_NAME || '.' || B.COLUMN_NAME "PK Column",
A.R_CONSTRAINT_NAME ,
C.POSITION
FROM ALL_CONSTRAINTS A, ALL_CONS_COLUMNS B, ALL_CONS_COLUMNS C
WHERE A.R_CONSTRAINT_NAME=B.CONSTRAINT_NAME
AND B.OWNER=UPPER('&OWNER')
AND A.CONSTRAINT_NAME=C.CONSTRAINT_NAME
AND A.OWNER=C.OWNER
AND A.OWNER = B.OWNER
AND A.TABLE_NAME=C.TABLE_NAME
AND B.POSITION=C.POSITION
AND A.TABLE_NAME LIKE UPPER('&TABLE_NAME')
ORDER BY A.CONSTRAINT_NAME, C.POSITION
/

COLUMN POSITION NOPRINT
CLEAR BREAKS

PROMPT
PROMPT CONSTRAINTS for Table &Table_Name.

SELECT CONSTRAINT_NAME , SEARCH_CONDITION
FROM ALL_CONSTRAINTS
WHERE TABLE_NAME = UPPER('&Table_Name.')
AND Owner = UPPER('&Owner.')
AND CONSTRAINT_TYPE NOT IN ( 'P' , 'R');

PROMPT
PROMPT ROWCOUNT for Table &Table_Name.

SET FEEDBACK OFF
SET SERVEROUTPUT ON
DECLARE N NU MBER ;
V VARCHAR2(100) ;
BEGIN
V := 'SELECT COUNT(*) FROM ' || UPPER('&Table_Name.') ;
EXECUTE IMMEDIATE V INTO N ;
DBMS_OUTPUT.PUT_LINE (N);
END;
/

SET FEEDBACK ON

PROMPT
PROMPT Tables That REFER to Table &Table_Name.

BREAK ON TABLE_NAME ON CONSTRAINT_NAME skip 1

SELECT C.TABLE_NAME , C.CONSTRAINT_Name , CC.COLUMN_NAME "FK Column"
FROM ALL_CONSTRAINTS C
, All_Cons_colUMNs CC
WHERE C.Constraint_Name = CC.Constraint_Name
AND R_CONSTRAINT_NAME = ( SELECT CONSTRAINT_NAME
FROM ALL_CONSTRAINTS
WHERE TABLE_NAME = UPPER('&Table_Name.')
AND CONSTRAINT_TYPE = 'P'
AND Owner = UPPER('&Owner.')
)
AND C.Owner = UPPER('&Owner.')
/

CLEAR BREAKS


PROMPT
PROMPT PARTITIONED COLUMNS for Table &Table_Name.

SELECT COLUMN_NAME , COLUMN_POSITION
FROM All_Part_Key_Columns
WHERE NAME = UPPER('&Table_Name.')
AND Owner = UPPER('&Owner.') ;


PROMPT
PROMPT PARTITIONS for Table &Table_Name.

SELECT PARTITION_NAME , NUM_ROWS
FROM All_Tab_Partitions
WHERE TABLE_NAME = UPPER('&Table_Name.')
AND Table_Owner = UPPER('&Owner.') ;


PROMPT
PROMPT TRIGGERS for Table &Table_Name.

SELECT Trigger_Name
FROM All_Triggers
WHERE TABLE_NAME = UPPER('&Table_Name.')
AND Owner = UPPER('&Owner.') ;

PROMPT
PROMPT DEPENDANTS for Table &Table_Name.

BREAK ON TYPE SKIP 1

SELECT TYPE , NAME
FROM ALL_DEPENDENCIES
WHERE REFERENCED_NAME = UPPER('&Table_Name.')
ORDER BY TYPE ;

CLEAR BREAKS

SET TERMOUT OFF
SET AUTOTRACE ON
SET TIMING ON
SET TERMOUT ON

Monday, 21 January 2008

How To : Displaying Column Data Horisontally

Some time you need to create a result set where the rows need to be columns, or vice versa. This commonly requirement can be done by using pivot (or crosstab) query.

1. A simple pivot query can be done by doing the following
2. Add some kind of count or row number to your query, if necessary for the grouping
3. Then use your (revised) original query as a sub-query
4. Use "decode" to turn rows into columns (ie. a "sparse" matrix).
5. Use "max" to "squash" the multiple rows you moved to columns, into single rows. Don't forget to group by.

(Note: it gets more complicated if you don't know how many columns you'll need).

Here is an example of a pivot query. Say you have the following set of data:


And you would like to make DEPTNO be a column. We have 4 deptno's in EMP, 10,20,30,40.
We can make columns dept_10, dept_20, dept_30, dept_40 that have the values that are
currently in the count column. It would look like this:

Note: don't confuse pivot queries with pivot tables. Pivot tables are a different concept, and have different uses (most typically to fill in missing data).

Monday, 14 January 2008

Trigger on Insert and Delete

A friend of mine is asking, how to insert into TBL_A, but first you have to insert into TBL_B as master table (TBL_A referencing to TBL_B). We could use trigger on this. Here's two example on creating trigger while insert and delete on a table.

create or replace trigger TBL_A_TRIG_INS
before insert on TBL_A
for each row

begin
insert into TBL_B (ID ,NAME )
values ( :new.ID, :new.NAME );
end;



create or replace trigger TBL_A_TRIG_DEL
after
delete
on TBL_A
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
begin
delete from TBL_B
where ID = :new.ID and NAME = :new.NAME
end;

Thursday, 3 January 2008

Oracle, SQL, PL / SQL Technical Interview Questions

Listed here are Interview Questions for Database Interviews.
This mainly lists Oracle, SQL, PL SQL frequently asked questions in technical Interviews

1. What is DDL, DML ? How are they different?
2. What are different types of joins in SQL?
3. How do you select unique rows using SQL?
4. What is the difference between DELETE and TRUNCATE ?
5. What is the difference between a "where" clause and a "having" clause?
6. What is the difference between "procedure" and "function"?
7. What is the difference between "translate" and "replace" ?
8. How to remove duplicate records from a table?
9. What is a "trigger"?
10.What is the difference between "translate" and "replace"?
11.What is a VIEW?
12.What is the difference among "dropping a table", "truncating a table"
and "deleting all records" from a table
13.Explain new feature of 9i Database ? Explain new feature of 10g Database ?
14.How to use DECODE function?
15.What is “Group by” clause?
16.What are cursors and what are the situations you will use them?
17.What default packages are provided by Oracle?
18.How do you debug a oracle procedure /function?
19.How many triggers are available?
20.How are procedures executed?

Oracle SQL, PL/SQL Interview Question List

These are some Oracle SQL, PL/SQL Interview Question List
1. How do you convert a date to a string?
2. What is an aggregate function?
3. What is the dual table?
4. What are cursors? Distinguish between implicit and explict cursors?
5. Explain how cursors are used by Oracle?
6. What is PL/SQL? Describe the block structure of PL/SQL?
7. What is a nested subquery?
8. What are the various types of queries ?
9. Which of the following is not a schema object : Index, table, public synonym, trigger and package ?
10. What is dynamic sql in oracle?
11. What is the difference between a package, procedure and function?
12. What is the difference between delete, drop and truncating a table?
13. How many triggers are supported in Oracle
14. Are you aware of FLASHBACK concept ? What is it?
15. Describe oracle’s logical and physical structure?
16. What is data dictionary ?
17. What is the use of control files ?
18. How would store XML data in table ? What data type would be used for the columns?
19. Difference between post and commit?
20. Difference between commit and rollback?
21. What are savepoints?
22. What is the Difference between a View and Synonym?
23. How would you fetch system date from oracle?
24. What is the difference between primary key, unique key, foreign key?
25. What is the difference between NO DATA FOUND and %NOTFOUND?
26. What is cursor for loop?
27. What are cursor attributes?
28. What will you use in Query : IN or EXISTS? Why?
29. Explain the difference between a data block, an extent and a segment.?
30. What's the difference between logical and physical I/O?
31. What is an anonymous block?
32. What is a PL/SQL collection?
33. How can you tell if an UPDATE updated no rows?
34. How can you tell if a SELECT returned no rows?

The Difference Between %TYPE and %ROWTYPE

%TYPE and %ROWTYPE are used to define variables in PL/SQL as it is defined within the database. If the datatype or precision of a column changes, the program automically picks up the new definition from the database without having to make any code changes.

The %TYPE and %ROWTYPE constructs provide data independence, reduces maintenance costs, and allows programs to adapt as the database changes to meet new business needs.

%TYPE is used to declare a field with the same type as that of a specified table's column. Example:

DECLARE
v_EmpName emp.ename%TYPE;
BEGIN
SELECT ename INTO v_EmpName FROM emp WHERE ROWNUM = 1;
DBMS_OUTPUT.PUT_LINE('Name = ' || v_EmpName);
END;
/


%ROWTYPE is used to declare a record with the same types as found in the specified database table, view or cursor. Examples:
DECLARE
v_emp emp%ROWTYPE;
BEGIN
v_emp.empno := 10;
v_emp.ename := 'XXXXXXX';
END;
/

DECLARE
v_EmpRecord emp%ROWTYPE;
BEGIN
SELECT * INTO v_EmpRecord FROM emp WHERE ROWNUM = 1;
DBMS_OUTPUT.PUT_LINE('Name = ' || v_EmpRecord.ename);
DBMS_OUTPUT.PUT_LINE('Salary = ' || v_EmpRecord.sal);
END;
/

Wednesday, 2 January 2008

Oracle 10g UTL_COMPRESS

Oracle 10g includes many PL/SQL enhancements including: utl_compress
The UTL_COMPRESS package provides an API to allow compression and decompression of binary data (RAW, BLOB and BFILE). It uses the Lempel-Ziv compression algorithm which is equivalent to functionality of the gzip utility. A simple example of it's usage would be:

SET SERVEROUTPUT ON
DECLARE
l_original_blob BLOB;
l_compressed_blob BLOB;
l_uncompressed_blob BLOB;
BEGIN
-- Initialize both BLOBs to something.
l_original_blob := TO_BLOB(UTL_RAW.CAST_TO_RAW('1234567890123456789012345678901234567890'));
l_compressed_blob := TO_BLOB('1');
l_uncompressed_blob := TO_BLOB('1');

-- Compress the data.
UTL_COMPRESS.lz_compress (src => l_original_blob,
dst => l_compressed_blob);

-- Uncompress the data.
UTL_COMPRESS.lz_uncompress (src => l_compressed_blob,
dst => l_uncompressed_blob);

-- Display lengths.
DBMS_OUTPUT.put_line('Original Length : ' || LENGTH(l_original_blob));
DBMS_OUTPUT.put_line('Compressed Length : ' || LENGTH(l_compressed_blob));
DBMS_OUTPUT.put_line('Uncompressed Length: ' || LENGTH(l_uncompressed_blob));

-- Free temporary BLOBs.
DBMS_LOB.FREETEMPORARY(l_original_blob);
DBMS_LOB.FREETEMPORARY(l_compressed_blob);
DBMS_LOB.FREETEMPORARY(l_uncompressed_blob);
END;
/

Thursday, 27 December 2007

SQL & PL/SQL : When Your Query Takes Too Long

When your query takes too long and too slow, first of all you have to investigate the root caused of the problem, you have to know why it is slow

The tools at your disposal are, among more:
1. dbms_profiler
2. explain plan
3. SQL*Trace / tkprof
4. statspack

Use dbms_profiler if you want to know where time is being spent in PL/SQL code.
Statspack is a must if you are a dba and want to know what is going on in your entire database. For a single query or a small process, explain plan and SQL*Trace and tkprof are your tools.

explain plan in SQL*Plus you have to type:

explain plan for [your query];
select * from table(dbms_xplan.display);

When you get error messages or a message complaining about an old version of plan_table, make sure you run the script utlxplan.sql.

The output you get here basically shows you what the cost based optimizer expects. It gives you an idea on why the cost based optimizer chooses an access path.

SQL*Trace/tkprof

For this you have to type in SQL*Plus:
- alter session set sql_trace true;
- run your query
- disconnect (this step is important, because it ensures all cursors get closed, and "row source operation" is generated)
- identify your trace file in the server directory as specified in the parameter user_dump_dest
- on your operating system: "tkprof [trace file] a.txt sys=no sort=prsela exeela fchela"

The file a.txt will now give you valuable information on what has actually happened. No predictions but the truth.

By comparing the output from explain plan with the output from tkprof, you are able to identify the possible problem areas.

So before rushing into possible solutions, always post the output of explain plan and tkprof with your question and don't forget to post them between the tags [pre] and [/pre] for readability.

Tuesday, 18 December 2007

SQL & PL/QL : Deleting Table with Millions Rows

Here are the case:
You have to compare 2 tables, both having around million rows, and delete data based on single column.

SQL>DELETE FROM TABLE_A WHERE COLUMN_A NOT IN
(SELECT COLUMN_A FROM TABLE_B)

when you execute the above command, it takes a long time. So what is the best way to achieve it?
Here are some solutions:

1. If the rows percentage to be deleted is large then you can try the following step:
* Create a new table where COLUMN_A NOT IN
(SELECT COLUMN_A FROM TABLE_B)
* TRUNCATE table_A
* Insert in to table_A (select * from new table)
* If you have any constraints then may be it can be disabled.
2. If the query is properly indexed you might get better performance from a correlated subquery instead of NOT IN, something like

DELETE FROM TABLE_A
WHERE NOT EXISTS(
SELECT COLUMN_A
FROM TABLE_B
WHERE b.column_a = a.column_a
)

Sunday, 16 September 2007

Character Functions

Character Functions accept character input.
The input may come from a column in a table or from any expression.
Character Functions List

  1. ASCII(x) returns the ASCII value of the character x.
  2. CHR(x) returns the character with the ASCII value of x.
  3. CONCAT(x, y) concatenates y to x and return the appended string.
  4. INITCAP(x) converts the initial letter of each word in x to uppercase and returns that string.
  5. INSTR(x, find_string [, start] [, occurrence]) searches for find_string in x and returns the position at which find_string occurs.
  6. INSTRB(x) returns the location of a string within another string, but returns the value in bytes for a single-byte character system.
  7. LENGTH(x) returns the number of characters in x.
  8. LENGTHB(x) returns the length of a character string in bytes, except that the return value is in bytes for single-byte character sets.
  9. LOWER(x) converts the letters in x to lowercase and returns that string.
  10. LPAD(x, width [, pad_string]) pads x with spaces to left, to bring the total length of the string up to width characters.
  11. LTRIM(x [, trim_string]) trims characters from the left of x.
  12. NANVL(x, value) returns value if x matches the NaN special value (not a number), otherwise x is returned.
  13. NLS_INITCAP(x) Same as the INITCAP function except that it can use a different sort method as specified by NLSSORT.
  14. NLS_LOWER(x) Same as the LOWER function except that it can use a different sort method as specified by NLSSORT.
  15. NLS_UPPER(x) Same as the UPPER function except that it can use a different sort method as specified by NLSSORT.
  16. NLSSORT(x) Changes the method of sorting the characters. Must be specified before any NLS function; otherwise, the default sort will be used.
  17. NVL(x, value) returns value if x is null; otherwise, x is returned.
  18. NVL2(x, value1, value2) returns value1 if x is not null; if x is null, value2 is returned.
  19. REPLACE(x, search_string, replace_string) searches x for search_string and replaces it with replace_string.
  20. RPAD(x, width [, pad_string]) pads x to the right.
  21. RTRIM(x [, trim_string]) trims x from the right.
  22. SOUNDEX(x) returns a string containing the phonetic representation of x.
  23. SUBSTR(x, start [, length]) returns a substring of x that begins at the position specified by start. An optional length for the substring may be supplied.
  24. SUBSTRB(x) Same as SUBSTR except the parameters are expressed in bytes instead of characters to handle single-byte character systems.
  25. TRIM([trim_char FROM) x) trims characters from the left and right of x.
  26. UPPER(x) converts the letters in x to uppercase and returns that string.

Sunday, 9 September 2007

Cursors

Implicit cursors
Whenever a SQL statement is issued the Database server opens an area of memory in which the command is parsed and executed. This area is called a cursor. Microsoft tends to refer to cursors as datasets throughout much of their databse product documentation..

When the executable part of a PL/SQL block issues a SQL command, PL/SQL creates an implicit cursor which has the identifier SQL. PL/SQL manages this cursor for you.

PL/SQL provides some attributes which allow you to evaluate what happened when the implicit cursor was last used. You can use these attributes in PL/SQL statements like functions but you cannot use then within SQL statements.
The SQL cursor attributes are :

  • %ROWCOUNT:The number of rows processed by a SQL statement.
  • %FOUND: TRUE if at least one row was processed.
  • %NOTFOUND: TRUE if no rows were processed.
  • %ISOPEN:TRUE if cursor is open or FALSE if cursor has not been opened or has been closed. Only used with explicit cursors
Example:
DECLARE
ROW_DEL_NO NUMBER;
BEGIN
DELETE * FROM JD11.SECTION;
ROW_DEL_NO := SQL%ROWCOUNT;
END;


Explicit cursors
SELECT statements that occur within PL/SQL blocks are known as embedded, they must return one row and may only return one row. To get around this you can define a SELECT statement as a cursor (an area of memory), run the query and then manipulate the returned rows within the cursor. Cursors are controlled via four command statements.
They are :
  • DECLARE:Defines the name and structure of the cursor together with the SELECT statement that will populate the cursor with data. The query is validated but not executed.
  • OPEN: Executes the query that populates the cursor with rows.
  • FETCH:Loads the row addressed by the cursor pointer into variables and moves the cursor pointer on to the next row ready for the next fetch.
  • CLOSE:Releases the data within the cursor and closes it. The cursor can be reopened to refresh its data.
Cursors are defined within a DECLARE section of a PL/SQL block. For example:
DECLARE
CURSOR MYCUR IS SELECT ISBN, COST FROM JD11.BOOK;


The cursor is defined by the CURSOR keyword followed by the cursor identifier (MYCUR in this case) and then the SELECT statement used to populate it, the SELECT statement can be any legal query.

Cursors are opened with the OPEN statement, this populates the cursor with data.
DECLARE
CURSOR MYCUR IS SELECT ISBN, COST FROM JD11.BOOK;
BEGIN
OPEN MYCUR;
END;


To access the rows of data within the cursor we use the FETCH statement.

DECLARE
CURSOR MYCUR IS SELECT ISBN, COST FROM JD11.BOOK;
THISISBN NUMBER(10);
THISCOST NUMBER(10,2);
BEGIN
OPEN MYCUR;
FETCH MYCUR INTO THISISBN, THISCOST;
END;


The FETCH statement reads the column values for the current cursor row and puts them into the specified variables. The cursor pointer is updated to point at the next row. If the cursor has no more rows the variables will be set to null on the first FETCH attempt, subsequent FETCH attempts will raise an exception.

The CLOSE statement releases the cursor and any rows within it, you can open the cursor again to refresh the data in it.

DECLARE
CURSOR MYCUR IS SELECT ISBN, COST FROM JD11.BOOK;
THISISBN NUMBER(10);
THISCOST NUMBER(10,2);
BEGIN
OPEN MYCUR;
FETCH MYCUR INTO THISISBN, THISCOST;
CLOSE MYCUR;
END;


To process all the rows within a cursor we simply need to place the FETCH statement in a loop and check the cursor NOTFOUND attribute to see if we successfully fetched a row or not.

DECLARE
CURSOR MYCUR IS SELECT ISBN, COST FROM JD11.BOOK;
THISISBN NUMBER(10);
THISCOST NUMBER(10,2);
BEGIN
OPEN MYCUR;
LOOP
FETCH MYCUR INTO THISISBN, THISCOST;
EXIT WHEN MYCUR%NOTFOUND;
END LOOP;
CLOSE MYCUR;
END;


PL/SQL records may be based on a cursor. This is very convenient for processing rows from the active data set. An example follows :

DECLARE
CURSOR MYCUR IS SELECT ISBN, COST FROM JD11.BOOK;
PARTBOOK MYCUR%ROWTYPE;
BEGIN
OPEN MYCUR;
LOOP
FETCH MYCUR INTO PARTBOOK;
EXIT WHEN MYCUR%NOTFOUND;
IF PARTBOOK.ISBN = 21 THEN
PARTBOOK.COST = 19.10;
END IF;
END LOOP;
CLOSE MYCUR;
END;


You can use the WHERE CURRENT OF clause to execute DML commands against the current row of a cursor, this makes it easier to update rows. An example is below :

DECLARE
CURSOR MYCUR IS SELECT ISBN, COST FROM JD11.BOOK;
PARTBOOK MYCUR%ROWTYPE;
BEGIN
OPEN MYCUR;
LOOP
FETCH MYCUR INTO PARTBOOK;
EXIT WHEN MYCUR%NOTFOUND;
IF PARTBOOK.ISBN = 21 THEN
DELETE FROM JD11.BOOK WHERE CURRENT OF MYCUR;
END IF;
END LOOP;
CLOSE MYCUR;
END;

Note that I didn’t need to explicitly specify the row that I want deleted, PL/SQL supplies the required row identifier from the current record in the cursor ensuring that only the correct row is deleted.

It’s possible to vary the returned result set by using parameters, parameters allow you to specify the query selection criteria when you open the cursor.

DECLARE
CURSOR MYCUR (PARAM1 NUMBER) IS SELECT ISBN, COST FROM JD11.BOOK WHERE ISBN = PARAM1;
PARTBOOK MYCUR%ROWTYPE;
BEGIN
OPEN MYCUR(21);
FETCH MYCUR INTO PARTBOOK;
CLOSE MYCUR;
OPEN MYCUR(101);
FETCH MYCUR INTO PARTBOOK;
CLOSE MYCUR;
END;

Thursday, 6 September 2007

PL/SQL Records

A PL/SQL record is a variable that contains a collection of separate fields. Each field is individually addressable. You can reference the field names in both assignments and expressions. The fields within a record may have different datatypes and sizes, like the columns of a database table. Records are a convenient way of storing a complete fetched row from a database table.

Use the %ROWTYPE attribute to declare a record based upon a collection of database columns from a table or view. The fields within the record take their names and datatypes from the columns of the table or view.

Declare the record in the DECLARE section along with any other required variables and constants. An example follows :
DECLARE
REC1 JD11.BOOK%ROWTYPE;
REC4 JD11.BOOK%ROWTYPE;

The above declaration sets the object REC1 to be a record object holding fields that match the columns in the BOOK table. It doesn't hold any values until it is populated.

Assign values into a PL/SQL record by naming the record after the INTO keyword of a SELECT statement. The INTO keyword defines the name specification for the storage area(s) of queried value(s).
BEGIN
SELECT * FROM JD11.BOOK INTO REC1 WHERE ISBN = 21;
END;

You can assign all the record values from one record to another provided that the record definitions are the same.

BEGIN
REC4 := REC1;
END;

Reference the field values within a PL/SQL record like this :

BEGIN
REC4 := REC1;
IF REC4.COST > 0 THEN
REC4.SECTION_ID := 10;
ELSE
REC4.SECTION_ID := 7;
END IF;
END;

Discovering PL/SQL Errors

PL/SQL does not always tell you about compilation errors. Instead, it gives you a cryptic message such as "procedure created with compilation errors". If you don't see what is wrong immediately, try issuing the command

show errors procedure ;

Alternatively, you can type, SHO ERR (short for SHOW ERRORS) to see the most recent compilation error.

Note that the location of the error given as part of the error message is not always accurate!

Control Flow in PL/SQL

PL/SQL allows you to branch and create loops in a fairly familiar way.
An IF statement looks like:

IF THEN ELSE END IF;

The ELSE part is optional. If you want a multiway branch, use:

IF THEN ...

ELSIF THEN ...
... ...
ELSIF THEN ...

ELSE ...

END IF;

Loops are created with the following:

LOOP
/* A list of statements. */
END LOOP;

At least one of the statements in should be an EXIT statement of the form

EXIT WHEN ;

The loop breaks if is true.
Some other useful loop-forming statements are:

  • EXIT by itself is an unconditional loop break. Use it inside a conditional if you like.
  • A WHILE loop can be formed with
    WHILE LOOP

    END LOOP;
  • A simple FOR loop can be formed with:
    FOR IN .. LOOP

    END LOOP;
    Here, can be any variable; it is local to the for-loop and need not be declared. Also, and are constants.

Wednesday, 5 September 2007

Variables and Types

Information is transmitted between a PL/SQL program and the database through variables. Every variable has a specific type associated with it. That type can be :

  • One of the types used by SQL for database columns
  • A generic type used in PL/SQL such as NUMBER
  • Declared to be the same as the type of some database column
The most commonly used generic type is NUMBER. Variables of type NUMBER can hold either an integer or a real number. The most commonly used character string type is VARCHAR(n), where n is the maximum length of the string in bytes. This length is required, and there is no default. For example, we might declare:

DECLARE
price NUMBER;
myBeer VARCHAR(20);

Note that PL/SQL allows BOOLEAN variables, even though Oracle does not support BOOLEAN as a type for database columns.
Types in PL/SQL can be tricky. In many cases, a PL/SQL variable will be used to manipulate data stored in a existing relation. In this case, it is essential that the variable have the same type as the relation column. If there is any type mismatch, variable assignments and comparisons may not work the way you expect. To be safe, instead of hard coding the type of a variable, you should use the %TYPE operator. For example:

DECLARE
myBeer Beers.name%TYPE;

gives PL/SQL variable myBeer whatever type was declared for the name column in relation Beers.
A variable may also have a type that is a record with several fields. The simplest way to declare such a variable is to use %ROWTYPE on a relation name. The result is a record type in which the fields have the same names and types as the attributes of the relation. For instance:

DECLARE
beerTuple Beers%ROWTYPE;

makes variable beerTuple be a record with fields name and manufacture, assuming that the relation has the schema Beers(name, manufacture).
The initial value of any variable, regardless of its type, is NULL. We can assign values to variables, using the ":=" operator. The assignment can occur either immediately after the type of the variable is declared, or anywhere in the executable portion of the program. An example:

DECLARE
a NUMBER := 3;
BEGIN
a := a + 1;
END;
.

run;

This program has no effect when run, because there are no changes to the database.

Scope of Block Objects

The scope of an object defines where it is visible, it is the area of the program logic that can legally refer to a given object.

A variable or object's (cursor, constant Etc.) scope is determined by the block that it is declared in. It is only available until the block it is defined in ends execution. Remember that block variables are defined under the DECLARE keyword.

For nested blocks an object defined in a parent block is available within all its child (nested blocks). The reverse is not true, objects defined in a child block are not visible to the parent.

If a nested block defines an object with the same name as an object in its parent block then only the local object is visible. Unlike Java there is no method provided to get at the parent object in this circumstance.