Basic Structure of PL/SQL
PL/SQL stands for Procedural Language/SQL. PL/SQL extends
SQL by adding constructs found in procedural languages,
resulting in a structural language that is more powerful than
SQL. The basic unit in PL/SQL is a block. All PL/SQL programs
are made up of blocks, which can be nested within each other.
Typically, each block performs a logical action in he program. A
block has the following structure:
DECLARE
/* Declarative section: variables, types, and local
subprograms. */
BEGIN
/* Executable section: procedural and SQL statements go
here. */
/* This is the only section of the block that is required. */
EXCEPTION
/* Exception handling section: error handling statements go
here. */
END;
Only the executable section is required. The other sections are
optional. The only SQL statements allowed in a PL/SQL
program are SELECT, INSERT, UPDATE, DELETE and several
other data manipulation statements plus some transaction
control. However, the SELECT statement has a special form in
which a single tuple is placed in variables; more on this later.
Data definition statements like CREATE, DROP, or ALTER are not
allowed. The executable section also contains constructs such
as assignments, branches, loops, procedure calls, and triggers,
which are all described below (except triggers). PL/SQL is not
case sensitive. C style comments (/* ... */) may be used.
To execute a PL/SQL program, we must follow the program
text itself by
* A line with a single dot ("."), and then
* A line with run;
As with Oracle SQL programs, we can invoke a PL/SQL program
either by typing in sqlplus.
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.
Simple Programs in PL/SQL
The simplest form of program has some declarations followed
by an executable section consisting of one or more of the SQL
statements with which we are familiar. The major nuance is
that the form of the SELECT statement is different from its SQL
form. After the SELECT clause, we must have an INTO clause
listing variables, one for each attribute in the SELECT clause,
into which the components of the retrieved tuple must be
placed.
Notice we said "tuple" rather than "tuples", since
the SELECT
statement in PL/SQL only works if the result of the query
contains a single tuple. The situation is essentially the same as
that of the "single-row select" discussed in Section 7.1.5
of the
text, in connection with embedded SQL. If the query returns
more than one tuple, you need to use a cursor. Here is an
example:
CREATE TABLE T1(
e INTEGER,
f INTEGER
);
DELETE FROM T1;
INSERT INTO T1 VALUES(1, 3);
INSERT INTO T1 VALUES(2, 4);
/* Above is plain SQL; below is the PL/SQL program. */
DECLARE
a NUMBER;
b NUMBER;
BEGIN
SELECT e,f INTO a,b FROM T1 WHERE e>1;
INSERT INTO T1 VALUES(b,a);
END;
run;
Fortuitously, there is only one tuple of T1 that has first
component greater than 1, namely (2,4). The INSERT
statement thus inserts (4,2) into T1.
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 <condition> THEN <statement_list> ELSE
<statement_list>
END IF;
The ELSE part is optional. If you want a multiway branch, use:
IF <condition_1> THEN ...
ELSIF <condition_2> THEN ...
... ...
ELSIF <condition_n> THEN ...
ELSE ...
END IF;
The following is an example, slightly modified from the
previous one, where now we only do the insertion if the second
component is 1. If not, we first add 10 to each component and
then insert:
DECLARE
a NUMBER;
b NUMBER;
BEGIN
SELECT e,f INTO a,b FROM T1 WHERE e>1;
IF b=1 THEN
INSERT INTO T1 VALUES(b,a);
ELSE
INSERT INTO T1 VALUES(b+10,a+10);
END IF;
END;
run;
Loops are created with the following:
LOOP
<loop_body> /* A list of statements. */
END LOOP;
At least one of the statements in <loop_body> should be an
EXIT statement of the form
EXIT WHEN <condition>;
The loop breaks if <condition> is true. For example, here is a
way to insert each of the pairs (1, 1) through (100, 100) into T1
of the above two examples:
DECLARE
i NUMBER := 1;
BEGIN
LOOP
INSERT INTO T1 VALUES(i,i);
i := i+1;
EXIT WHEN i>100;
END LOOP;
END;
.run;
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 <condition> LOOP
· <loop_body>
END LOOP;
* A simple FOR loop can be formed with:
· FOR <var> IN <start>..<finish> LOOP
· <loop_body>
·
END LOOP;
Here, <var> can be any variable; it is local to the for-loop and
need not be declared. Also, <start> and <finish> are
constants.
Cursors
A cursor is a variable that runs through the tuples of some
relation. This relation can be a stored table, or it can be the
answer to some query. By fetching into the cursor each tuple
of the relation, we can write a program to read and process the
value of each such tuple. If the relation is stored, we can also
update or delete the tuple at the current cursor position.
The example below illustrates a cursor loop. It uses our
example relation T1(e,f) whose tuples are pairs of integers.
The program will delete every tuple whose first component is
less than the second, and insert the reverse tuple into T1.
1) DECLARE
/* Output variables to hold the result of the query: */
2) a T1.e%TYPE;
3) b T1.f%TYPE;
/* Cursor declaration: */
4) CURSOR T1Cursor IS
5) SELECT e, f
6) FROM T1
7) WHERE e < f
8) FOR UPDATE;
9) BEGIN
10) OPEN T1Cursor;
11) LOOP
/* Retrieve each row of the result of the above query
into PL/SQL variables: */
12) FETCH T1Cursor INTO a, b;
/* If there are no more rows to fetch, exit the loop: */
13) EXIT WHEN T1Cursor%NOTFOUND;
/* Delete the current tuple: */
14) DELETE FROM T1 WHERE CURRENT OF T1Cursor;
/* Insert the reverse tuple: */
15) INSERT INTO T1 VALUES(b, a);
16) END LOOP;
/* Free cursor used by the query. */
17) CLOSE T1Cursor;
18) END;
19) .
20) run;
Here are explanations for the various lines of this program:
* Line (1) introduces the declaration section.
* Lines (2) and (3) declare variables a and b to have types
equal to the types of attributes e and f of the relation T1.
Although we know these types are INTEGER, we wisely make
sure that whatever types they may have are copied to the
PL/SQL variables (compare with the previous example, where
we were less careful and declared the corresponding variables
to be of type NUMBER).
* Lines (4) through (8) define the cursor T1Cursor. It ranges
over a relation defined by the SELECT-FROM-WHERE query.
That query selects those tuples of T1 whose first component is
less than the second component. Line (8) declares the cursor
FOR UPDATE since we will modify T1 using this cursor later on
Line (14). In general, FOR UPDATE is unnecessary if the cursor
will not be used for modification.
* Line (9) begins the executable section of the program.
* Line (10) opens the cursor, an essential step.
* Lines (11) through (16) are a PL/SQL loop. Notice that such a
loop is bracketed by LOOP and END LOOP. Within the loop we
find:
o On Line (12), a fetch through the cursor into the local
variables. In general, the FETCH statement must provide
variables for each component of the tuple retrieved. Since the
query of Lines (5) through (7) produces pairs, we have
correctly provided two variables, and we know they are of the
correct type.
o On Line (13), a test for the loop-breaking condition. Its
meaning should be clear: %NOTFOUND after the name of a
cursor is true exactly when a fetch through that cursor has
failed to find any more tuples.
o On Line (14), a SQL DELETE statement that deletes the
current tuple using the special WHERE condition CURRENT OF
T1Cursor.
o On Line (15), a SQL INSERT statement that inserts the
reverse tuple into T1.
* Line (17) closes the cursor.
* Line (18) ends the PL/SQL program.
* Lines (19) and (20) cause the program to execute.
Procedures
PL/SQL procedures behave very much like procedures in other
programming language. Here is an example of a PL/SQL
procedure addtuple1 that, given an integer i, inserts the tuple
(i, 'xxx') into the following example relation:
CREATE TABLE T2 (
a INTEGER,
b CHAR(10)
);
CREATE PROCEDURE addtuple1(i IN NUMBER) AS
BEGIN
INSERT INTO T2 VALUES(i, 'xxx');
END addtuple1;
run;
A procedure is introduced by the keywords CREATE PROCEDURE
followed by the procedure name and its parameters. An option
is to follow CREATE by OR REPLACE. The advantage of doing so
is that should you have already made the definition, you will
not get an error. On the other hand, should the previous
definition be a different procedure of the same name, you will
not be warned, and the old procedure will be lost.
There can be any number of parameters, each followed by a
mode and a type. The possible modes are IN (read-only), OUT
(write-only), and INOUT (read and write). Note: Unlike the type
specifier in a PL/SQL variable declaration, the type specifier in
a parameter declaration must be unconstrained. For example,
CHAR(10) and VARCHAR(20) are illegal; CHAR or VARCHAR
should be used instead. The actual length of a parameter
depends on the corresponding argument that is passed in
when the procedure is invoked.
Following the arguments is the keyword AS (IS is a synonym).
Then comes the body, which is essentially a PL/SQL block. We
have repeated the name of the procedure after the END, but
this is optional. However, the DECLARE section should not start
with the keyword DECLARE. Rather, following AS we have:
... AS
<local_var_declarations>
BEGIN
<procedure_body>
END;
.
run;
The run at the end runs the statement that creates the
procedure; it does not execute the procedure. To execute the
procedure, use another PL/SQL statement, in which the
procedure is invoked as an executable statement. For
example:
BEGIN addtuple1(99); END;
.
run;
The following procedure also inserts a tuple into T2, but it takes
both components as arguments:
CREATE PROCEDURE addtuple2(
x T2.a%TYPE,
y T2.b%TYPE)
AS
BEGIN
INSERT INTO T2(a, b)
VALUES(x, y);
END addtuple2;
.
run;
Now, to add a tuple (10, 'abc') to T2:
BEGIN
addtuple2(10, 'abc');
END;
.
run;
The following illustrates the use of an OUT parameter:
CREATE TABLE T3 (
a INTEGER,
b INTEGER
);
CREATE PROCEDURE addtuple3(a NUMBER, b OUT NUMBER)
AS
BEGIN
b := 4;
INSERT INTO T3 VALUES(a, b);
END;
.
run;
DECLARE
v NUMBER;
BEGIN
addtuple3(10, v);
END;
run;
Note that assigning values to parameters declared as OUT or
INOUT causes the corresponding input arguments to be
written. Because of this, the input argument for an OUT or
INOUT parameter should be something with an "lvalue", such
as a variable like v in the example above. A constant or a
literal argument should not be passed in for an OUT/INOUT
parameter.
We can also write functions instead of procedures. In a
function declaration, we follow the parameter list by RETURN
and the type of the return value:
CREATE FUNCTION <func_name>(<param_list>) RETURN
<return_type> AS ...
In the body of the function definition, "RETURN
<expression>;"
exits from the function and returns the value of <expression>.
To find out what procedures and functions you have created,
use the following SQL query:
select object_type, object_name
from user_objects
where object_type = 'PROCEDURE'
or object_type = 'FUNCTION';
To drop a stored procedure/function:
drop procedure <procedure_name>;
drop function <function_name>;
Discovering 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 <procedure_name>;
Alternatively, you can type, SHO ERR (short for SHOW ERRORS)
to see the most recent compilation error.
Printing Variables
Sometimes we might want to print the value of a PL/SQL local
variable. A ``quick-and-dirty'' way is to store it as the sole
tuple of some relation and after the PL/SQL statement print
the relation with a SELECT statement. A more couth way is to
define a bind variable, which is the only kind that may be
printed with a print command. Bind variables are the kind that
must be prefixed with a colon in PL/SQL statements.
The steps are as follows:
1. We declare a bind variable as follows:
VARIABLE <name> <type>
where the type can be only one of three things: NUMBER,
CHAR, or CHAR(n).
2. We may then assign to the variable in a following PL/SQL
statement, but we must prefix it with a colon.
3. Finally, we can execute a statement
PRINT :<name>;
outside the PL/SQL statement
Here is a trivial example, which prints the value 1.
VARIABLE x NUMBER
BEGIN
:x := 1;
END;
.
run;
PRINT :x;
Looking for more information of PL SQL tutorials:
1. PLSQL Tutorial
2. PL/SQL Tutorial
What is SQL?
The Structured Query Language is used in manipulating data stored in Relational
Database Management Systems (RDBMS). SQL provides commands through which data
can be extracted, sorted, updated, deleted and inserted. SQL has the full
support of ANSI (American National Standards Institute), which has laid down
certain rules for the language.
SQL can be used with any RDBMS such as MySQL, mSQL, PostgresSQL, Oracle,
Microsoft SQL Server, Access, Sybase, Ingres etc. All the important and common
sql statements are supported by these RDBMS, however, each has its own set of
proprietary statements and extensions.
SQL
Data Manipulation Language (DM): SQL (Structured Query Language) is a syntax for
executing queries. But the
SQL Data Manipulation Language (DM): SQL (Structured Query
Language) is a syntax for executing queries. But the SQL language also includes
a syntax to update, insert, and delete records.
Joins and Keys
Sometimes we have to select data from two
tables to make our result complete. We have to perform a join.
Tables in a database can be related to
each other with keys. A primary key is a column with a unique value for each
row. The purpose is to bind data together, across tables, without repeating all
of the data in every table.
In the "Employees" table below,
the "Employee_ID" column is the primary key, meaning that no
two rows can have the same Employee_ID. The Employee_ID distinguishes two persons
even if they have the same name.
When you look at the example tables
below, notice that:
- The
"Employee_ID" column is the primary key of the
"Employees" table
- The
"Prod_ID" column is the primary key of the "Orders"
table
- The
"Employee_ID" column in the "Orders" table is used to
refer to the persons in the "Employees" table without using
their names
Employees:
Employee_ID
|
Name
|
01
|
Hansen, Ola
|
02
|
Svendson, Tove
|
03
|
Svendson, Stephen
|
04
|
Pettersen, Kari
|
Orders:
Prod_ID
|
Product
|
Employee_ID
|
234
|
Printer
|
01
|
657
|
Table
|
03
|
865
|
Chair
|
03
|
Referring to
Two Tables
We can select data from two tables by
referring to two tables, like this:
Example
Who has ordered a product, and what did
they order?
SELECT Employees.Name,
Orders.Product
FROM Employees, Orders
WHERE Employees.Employee_ID=Orders.Employee_ID
|
Result
Name
|
Product
|
Hansen, Ola
|
Printer
|
Svendson, Stephen
|
Table
|
Svendson, Stephen
|
Chair
|
Example
Who ordered a printer?
SELECT Employees.Name
FROM Employees, Orders
WHERE Employees.Employee_ID=Orders.Employee_ID
AND Orders.Product='Printer'
|
Result
Using Joins
OR we can select data from two tables
with the JOIN keyword, like this:
Example INNER JOIN
Syntax
SELECT field1, field2, field3
FROM first_table
INNER JOIN second_table
ON first_table.keyfield = second_table.foreign_keyfield
|
Who has ordered a product, and what did
they order?
SELECT Employees.Name,
Orders.Product
FROM Employees
INNER JOIN Orders
ON Employees.Employee_ID=Orders.Employee_ID
|
The INNER JOIN returns all rows from both
tables where there is a match. If there are rows in Employees that do not have
matches in Orders, those rows will not be listed.
Result
Name
|
Product
|
Hansen, Ola
|
Printer
|
Svendson, Stephen
|
Table
|
Svendson, Stephen
|
Chair
|
Example LEFT JOIN
Syntax
SELECT field1, field2, field3
FROM first_table
LEFT JOIN second_table
ON first_table.keyfield = second_table.foreign_keyfield
|
List all employees, and their orders - if
any.
SELECT Employees.Name,
Orders.Product
FROM Employees
LEFT JOIN Orders
ON Employees.Employee_ID=Orders.Employee_ID
|
The LEFT JOIN returns all the rows from
the first table (Employees), even if there are no matches in the second table
(Orders). If there are rows in Employees that do not have matches in Orders,
those rows also will be listed.
Result
Name
|
Product
|
Hansen, Ola
|
Printer
|
Svendson, Tove
|
|
Svendson, Stephen
|
Table
|
Svendson, Stephen
|
Chair
|
Pettersen, Kari
|
|
Example RIGHT JOIN
Syntax
SELECT field1, field2, field3
FROM first_table
RIGHT JOIN second_table
ON first_table.keyfield = second_table.foreign_keyfield
|
List all orders, and who has ordered - if
any.
SELECT Employees.Name,
Orders.Product
FROM Employees
RIGHT JOIN Orders
ON Employees.Employee_ID=Orders.Employee_ID
|
The RIGHT JOIN returns all the rows from
the second table (Orders), even if there are no matches in the first table
(Employees). If there had been any rows in Orders that did not have matches in
Employees, those rows also would have been listed.
Result
Name
|
Product
|
Hansen, Ola
|
Printer
|
Svendson, Stephen
|
Table
|
Svendson, Stephen
|
Chair
|
Example
Who ordered a printer?
SELECT Employees.Name
FROM Employees
INNER JOIN Orders
ON Employees.Employee_ID=Orders.Employee_ID
WHERE Orders.Product = 'Printer'
|
Result
Create a Database
CREATE DATABASE database_name
Create a Table
CREATE TABLE Person
(
LastName varchar,
FirstName varchar,
Address varchar,
Age int
)
The data type specifies what type of data
the column can hold. The table below contains the most common data types in
SQL:
Data Type
|
Description
|
integer(size)
int(size)
smallint(size)
tinyint(size)
|
Hold integers only. The
maximum number of digits are specified in parenthesis.
|
decimal(size,d)
numeric(size,d)
|
Hold numbers with fractions.
The maximum number of digits are specified in "size". The maximum
number of digits to the right of the decimal is specified in "d".
|
char(size)
|
Holds a fixed length string
(can contain letters, numbers, and special characters). The fixed size is
specified in parenthesis.
|
varchar(size)
|
Holds a variable length
string (can contain letters, numbers, and special characters). The maximum
size is specified in parenthesis.
|
date(yyyymmdd)
|
Holds a date
|
Create Index
Indices are created in an existing table
to locate rows more quickly and efficiently. It is possible to create an index
on one or more columns of a table, and each index is given a name. The users
cannot see the indexes, they are just used to speed up queries.
Note: Updating a table
containing indexes takes more time than updating a table without, this is
because the indexes also need an update. So, it is a good idea to create
indexes only on columns that are often used for a search.
A Unique Index
Creates a unique index on a table. A
unique index means that two rows cannot have the same index value.
CREATE UNIQUE INDEX
index_name
ON table_name (column_name)
|
The "column_name" specifies the
column you want indexed.
A Simple Index
Creates a simple index on a table. When
the UNIQUE keyword is omitted, duplicate values are allowed.
CREATE INDEX index_name
ON table_name (column_name)
|
The "column_name" specifies the
column you want indexed.
Example
This example creates a simple index,
named "PersonIndex", on the LastName field of the Person table:
CREATE INDEX PersonIndex
ON Person (LastName)
|
If you want to index the values in a
column in descending order, you can add the reserved word DESC
after the column name:
CREATE INDEX PersonIndex
ON Person (LastName DESC)
|
If you want to index more than one column
you can list the column names within the parentheses, separated by commas:
CREATE INDEX PersonIndex
ON Person (LastName, FirstName)
|
Drop Index
You can delete an existing index in a
table with the DROP statement.
DROP INDEX
table_name.index_name
|
Delete a Database or Table
To delete a database:
DROP DATABASE database_name
|
To delete a table (the table structure,
attributes, and indexes will also be deleted):
Alter Table
The ALTER TABLE statement is used to add
or drop columns in an existing table.
ALTER TABLE table_name
ADD column_name datatype
ALTER TABLE table_name
DROP COLUMN column_name
Note: Some database systems don't allow
the dropping of a column in a database table (DROP COLUMN column_name).
Person:
LastName
|
FirstName
|
Address
|
Pettersen
|
Kari
|
Storgt 20
|
Example
To add a column named "City" in
the "Person" table:
ALTER TABLE Person
ADD City varchar(30)
|
Result:
LastName
|
FirstName
|
Address
|
City
|
Pettersen
|
Kari
|
Storgt 20
|
|
Example
To drop the "Address" column in
the "Person" table:
ALTER TABLE Person DROP
COLUMN Address
|
Result:
LastName
|
FirstName
|
City
|
Pettersen
|
Kari
|
|
Function Syntax
The syntax for built-in SQL functions is:
SELECT function(column) FROM table
Types of Functions
There are several basic types and
categories of functions in SQL. The basic types of functions are:
- Aggregate
Functions
- Scalar
functions
Aggregate functions
Aggregate functions operate against a
collection of values, but return a single value.
Note: If used among many other
expressions in the item list of a SELECT statement, the SELECT must have a
GROUP BY clause!!
"Persons" table (used in most examples)
Name
|
Age
|
Hansen, Ola
|
34
|
Svendson, Tove
|
45
|
Pettersen, Kari
|
19
|
Aggregate functions in MS Access
Function
|
Description
|
AVG(column)
|
Returns the average value of
a column
|
COUNT(column)
|
Returns the number of rows
(without a NULL value) of a column
|
COUNT(*)
|
Returns the number of
selected rows
|
FIRST(column)
|
Returns the value of the
first record in the specified field
|
LAST(column)
|
Returns the value of the last
record in the specified field
|
MAX(column)
|
Returns the highest value of
a column
|
MIN(column)
|
Returns the lowest value of a
column
|
STDEV(column)
|
|
STDEVP(column)
|
|
SUM(column)
|
Returns the total sum of a
column
|
VAR(column)
|
|
VARP(column)
|
|
Aggregate functions in SQL Server
Function
|
Description
|
AVG(column)
|
Returns the average value of
a column
|
BINARY_CHECKSUM
|
|
CHECKSUM
|
|
CHECKSUM_AGG
|
|
COUNT(column)
|
Returns the number of rows
(without a NULL value) of a column
|
COUNT(*)
|
Returns the number of
selected rows
|
COUNT(DISTINCT
column)
|
Returns the number of
distinct results
|
FIRST(column)
|
Returns the value of the
first record in the specified field
|
LAST(column)
|
Returns the value of the last
record in the specified field
|
MAX(column)
|
Returns the highest value of
a column
|
MIN(column)
|
Returns the lowest value of a
column
|
STDEV(column)
|
|
STDEVP(column)
|
|
SUM(column)
|
Returns the total sum of a
column
|
VAR(column)
|
|
VARP(column)
|
|
Scalar functions
Scalar functions operate against a single
value, and return a single value based on the input value.
Useful Scalar Functions in MS Access
Function
|
Description
|
UCASE(c)
|
Converts a field to upper
case
|
LCASE(c)
|
Converts a field to lower
case
|
MID(c,start[,end])
|
Extract characters from a
text field
|
LEN(c)
|
Returns the length of a text
field
|
INSTR(c)
|
Returns the numeric position
of a named character within a text field
|
LEFT(c,number_of_char)
|
Return the left part of a
text field requested
|
RIGHT(c,number_of_char)
|
Return the right part of a
text field requested
|
ROUND(c,decimals)
|
Rounds a numeric field to the
number of decimals specified
|
MOD(x,y)
|
Returns the remainder of a
division operation
|
NOW()
|
Returns the current system
date
|
FORMAT(c,format)
|
Changes the way a field is
displayed
|
DATEDIFF(d,date1,date2)
|
Used to perform date
calculations
|
SELECT AVG(column) FROM table
SELECT AVG(Age) FROM Persons WHERE Age>20
SELECT COUNT(Age) FROM Persons
SELECT FIRST(Age) AS lowest_age
FROM Persons
ORDER BY Age
GROUP BY
GROUP BY... was added to SQL because
aggregate functions (like SUM) return the aggregate of all column values every
time they are called, and without the GROUP BY function it was impossible to
find the sum for each individual group of column values.
The syntax for the GROUP BY function is:
SELECT column,SUM(column)
FROM table GROUP BY column
|
GROUP BY Example
This "Sales" Table:
Company
|
Amount
|
W3Schools
|
5500
|
IBM
|
4500
|
W3Schools
|
7100
|
And This SQL:
SELECT Company, SUM(Amount)
FROM Sales
|
Returns this result:
Company
|
SUM(Amount)
|
W3Schools
|
17100
|
IBM
|
17100
|
W3Schools
|
17100
|
The above code is invalid because the
column returned is not part of an aggregate. A GROUP BY clause will solve this
problem:
SELECT Company,SUM(Amount)
FROM Sales
GROUP BY Company
|
Returns this result:
Company
|
SUM(Amount)
|
W3Schools
|
12600
|
IBM
|
4500
|
HAVING CLAUSE
HAVING... was added to SQL because the
WHERE keyword could not be used against aggregate functions (like SUM), and
without HAVING... it would be impossible to test for result conditions.
The syntax for the HAVING function is:
SELECT column,SUM(column)
FROM table
GROUP BY column
HAVING SUM(column) condition value
|
This "Sales" Table:
Company
|
Amount
|
W3Schools
|
5500
|
IBM
|
4500
|
W3Schools
|
7100
|
This SQL:
SELECT Company,SUM(Amount)
FROM Sales
GROUP BY Company
HAVING SUM(Amount)>10000
|
Returns this result
Company
|
SUM(Amount)
|
W3Schools
|
12600
|
The SELECT INTO Statement
The SELECT INTO statement is most often
used to create backup copies of tables or for archiving records.
Syntax
SELECT column_name(s) INTO
newtable [IN externaldatabase]
FROM source
|
Make a Backup Copy
The following example makes a backup copy
of the "Persons" table:
SELECT * INTO Persons_backup
FROM Persons
|
The IN clause can be used to copy tables
into another database:
SELECT Persons.* INTO Persons
IN 'Backup.mdb'
FROM Persons
|
If you only want to copy a few fields,
you can do so by listing them after the SELECT statement:
SELECT LastName, FirstName
INTO Persons_backup
FROM Persons
|
You can also add a where clause. The
following example creates a "Persons_backup" table with two columns
(FirstName and LastName) by extracting the persons who lives in
"Sandnes" from the "Persons" table:
SELECT LastName, Firstname
INTO Persons_sandnes
FROM Persons
WHERE City='Sandnes'
|
Selecting data from more than one table
is also possible. The following example creates a new table
"Empl_Ord_backup" that contains data from the two tables Employees
and Orders:
SELECT
Employees.Name,Orders.Product
INTO Empl_Ord_backup
FROM Employees
INNER JOIN Orders
ON Employees.Employee_ID=Orders.Employee_ID
|
Basic Structure of PL/SQL
PL/SQL
stands for Procedural Language/SQL. PL/SQL extends SQL by adding constructs
found in procedural languages, resulting in a structural language that is more
powerful than SQL. The basic unit in PL/SQL is a block. All PL/SQL programs are
made up of blocks, which can be nested within each other. Typically, each block
performs a logical action in he program. A block has the following structure:
DECLARE
/* Declarative section: variables, types, and local subprograms. */
BEGIN
/* Executable section: procedural and SQL statements go here. */
/* This is the only section of the block that is required. */
EXCEPTION
/* Exception handling section: error handling statements go here. */
END;
Only the executable section is required. The other sections are optional. The
only SQL statements allowed in a PL/SQL program are SELECT, INSERT, UPDATE,
DELETE and several other data manipulation statements plus some transaction
control. However, the SELECT statement has a special form in which a single
tuple is placed in variables; more on this later. Data definition statements
like CREATE, DROP, or ALTER are not allowed. The executable section also
contains constructs such as assignments, branches, loops, procedure calls, and
triggers, which are all described below (except triggers). PL/SQL is not case
sensitive. C style comments (/* ... */) may be used.
To execute a PL/SQL program, we must follow the program text itself by
* A line with a single dot ("."), and then
* A line with run;
As with Oracle SQL programs, we can invoke a PL/SQL program either by typing in
sqlplus.
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.
Simple Programs in PL/SQL
The simplest form of program has some declarations followed by an
executable section consisting of one or more of the SQL statements with which
we are familiar. The major nuance is that the form of the SELECT statement is
different from its SQL form. After the SELECT clause, we must have an INTO
clause listing variables, one for each attribute in the SELECT clause, into
which the components of the retrieved tuple must be placed.
Notice we said "tuple" rather than "tuples", since the
SELECT statement in PL/SQL only works if the result of the query contains a
single tuple. The situation is essentially the same as that of the
"single-row select" discussed in Section 7.1.5 of the text, in
connection with embedded SQL. If the query returns more than one tuple, you
need to use a cursor. Here is an example:
CREATE TABLE T1(
e INTEGER,
f INTEGER
);
DELETE FROM T1;
INSERT INTO T1 VALUES(1, 3);
INSERT INTO T1 VALUES(2, 4);
/* Above is plain SQL; below is the PL/SQL program. */
DECLARE
a NUMBER;
b NUMBER;
BEGIN
SELECT e,f INTO a,b FROM T1 WHERE e>1;
INSERT INTO T1 VALUES(b,a);
END;
run;
Fortuitously, there is only one tuple of T1 that has first component greater
than 1, namely (2,4). The INSERT statement thus inserts (4,2) into T1.
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 <condition> THEN <statement_list> ELSE <statement_list>
END IF;
The ELSE part is optional. If you want a multiway branch, use:
IF <condition_1> THEN ...
ELSIF <condition_2> THEN ...
... ...
ELSIF <condition_n> THEN ...
ELSE ...
END IF;
The following is an example, slightly modified from the previous one, where now
we only do the insertion if the second component is 1. If not, we first add 10
to each component and then insert:
DECLARE
a NUMBER;
b NUMBER;
BEGIN
SELECT e,f INTO a,b FROM T1 WHERE e>1;
IF b=1 THEN
INSERT INTO T1 VALUES(b,a);
ELSE
INSERT INTO T1 VALUES(b+10,a+10);
END IF;
END;
run;
Loops are created with the following:
LOOP
<loop_body> /* A list of statements. */
END LOOP;
At least one of the statements in <loop_body> should be an EXIT statement
of the form
EXIT WHEN <condition>;
The loop breaks if <condition> is true. For example, here is a way to
insert each of the pairs (1, 1) through (100, 100) into T1 of the above two
examples:
DECLARE
i NUMBER := 1;
BEGIN
LOOP
INSERT INTO T1 VALUES(i,i);
i := i+1;
EXIT WHEN i>100;
END LOOP;
END;
.run;
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 <condition> LOOP
· <loop_body>
END LOOP;
* A simple FOR loop can be formed with:
· FOR <var> IN <start>..<finish> LOOP
· <loop_body>
·
END LOOP;
Here, <var> can be any variable; it is local to the for-loop and need not
be declared. Also, <start> and <finish> are constants.
Cursors
A cursor is a variable that runs through the tuples of some relation. This
relation can be a stored table, or it can be the answer to some query. By
fetching into the cursor each tuple of the relation, we can write a program to
read and process the value of each such tuple. If the relation is stored, we
can also update or delete the tuple at the current cursor position.
The example below illustrates a cursor loop. It uses our example
relation T1(e,f) whose tuples are pairs of integers. The program will delete
every tuple whose first component is less than the second, and insert the
reverse tuple into T1.
1) DECLARE
/* Output variables to hold the result of the query: */
2) a T1.e%TYPE;
3) b T1.f%TYPE;
/* Cursor declaration: */
4) CURSOR T1Cursor IS
5) SELECT e, f
6) FROM T1
7) WHERE e < f
8) FOR UPDATE;
9) BEGIN
10) OPEN T1Cursor;
11) LOOP
/* Retrieve each row of the result of the above query
into PL/SQL variables: */
12) FETCH T1Cursor INTO a, b;
/* If there are no more rows to fetch, exit the loop: */
13) EXIT WHEN T1Cursor%NOTFOUND;
/* Delete the current tuple: */
14) DELETE FROM T1 WHERE CURRENT OF T1Cursor;
/* Insert the reverse tuple: */
15) INSERT INTO T1 VALUES(b, a);
16) END LOOP;
/* Free cursor used by the query. */
17) CLOSE T1Cursor;
18) END;
19) .
20) run;
Here are explanations for the various lines of this program:
* Line (1) introduces the declaration section.
* Lines (2) and (3) declare variables a and b to have types equal to the types
of attributes e and f of the relation T1. Although we know these types are
INTEGER, we wisely make sure that whatever types they may have are copied to
the PL/SQL variables (compare with the previous example, where we were less careful
and declared the corresponding variables to be of type NUMBER).
* Lines (4) through (8) define the cursor T1Cursor. It ranges over a relation
defined by the SELECT-FROM-WHERE query. That query selects those tuples of T1
whose first component is less than the second component. Line (8) declares the
cursor FOR UPDATE since we will modify T1 using this cursor later on Line (14).
In general, FOR UPDATE is unnecessary if the cursor will not be used for
modification.
* Line (9) begins the executable section of the program.
* Line (10) opens the cursor, an essential step.
* Lines (11) through (16) are a PL/SQL loop. Notice that such a loop is
bracketed by LOOP and END LOOP. Within the loop we find:
o On Line (12), a fetch through the cursor into the local variables. In
general, the FETCH statement must provide variables for each component of the
tuple retrieved. Since the query of Lines (5) through (7) produces pairs, we
have correctly provided two variables, and we know they are of the correct
type.
o On Line (13), a test for the loop-breaking condition. Its meaning should be
clear: %NOTFOUND after the name of a cursor is true exactly when a fetch
through that cursor has failed to find any more tuples.
o On Line (14), a SQL DELETE statement that deletes the current tuple using the
special WHERE condition CURRENT OF T1Cursor.
o On Line (15), a SQL INSERT statement that inserts the reverse tuple into T1.
* Line (17) closes the cursor.
* Line (18) ends the PL/SQL program.
* Lines (19) and (20) cause the program to execute.
Procedures
PL/SQL procedures behave very much like procedures in other programming
language. Here is an example of a PL/SQL procedure addtuple1 that, given an
integer i, inserts the tuple (i, 'xxx') into the following example relation:
CREATE TABLE T2 (
a INTEGER,
b CHAR(10)
);
CREATE PROCEDURE addtuple1(i IN NUMBER) AS
BEGIN
INSERT INTO T2 VALUES(i, 'xxx');
END addtuple1;
run;
A procedure is introduced by the keywords CREATE PROCEDURE followed by the
procedure name and its parameters. An option is to follow CREATE by OR REPLACE.
The advantage of doing so is that should you have already made the definition,
you will not get an error. On the other hand, should the previous definition be
a different procedure of the same name, you will not be warned, and the old
procedure will be lost.
There can be any number of parameters, each followed by a mode and a type. The
possible modes are IN (read-only), OUT (write-only), and INOUT (read and
write). Note: Unlike the type specifier in a PL/SQL variable declaration, the
type specifier in a parameter declaration must be unconstrained. For example,
CHAR(10) and VARCHAR(20) are illegal; CHAR or VARCHAR should be used instead.
The actual length of a parameter depends on the corresponding argument that is
passed in when the procedure is invoked.
Following the arguments is the keyword AS (IS is a synonym). Then comes the
body, which is essentially a PL/SQL block. We have repeated the name of the
procedure after the END, but this is optional. However, the DECLARE section
should not start with the keyword DECLARE. Rather, following AS we have:
... AS
<local_var_declarations>
BEGIN
<procedure_body>
END;
.
run;
The run at the end runs the statement that creates the procedure; it does not
execute the procedure. To execute the procedure, use another PL/SQL statement,
in which the procedure is invoked as an executable statement. For example:
BEGIN addtuple1(99); END;
.
run;
The following procedure also inserts a tuple into T2, but it takes both components
as arguments:
CREATE PROCEDURE addtuple2(
x T2.a%TYPE,
y T2.b%TYPE)
AS
BEGIN
INSERT INTO T2(a, b)
VALUES(x, y);
END addtuple2;
.
run;
Now, to add a tuple (10, 'abc') to T2:
BEGIN
addtuple2(10, 'abc');
END;
.
run;
The following illustrates the use of an OUT parameter:
CREATE TABLE T3 (
a INTEGER,
b INTEGER
);
CREATE PROCEDURE addtuple3(a NUMBER, b OUT NUMBER)
AS
BEGIN
b := 4;
INSERT INTO T3 VALUES(a, b);
END;
.
run;
DECLARE
v NUMBER;
BEGIN
addtuple3(10, v);
END;
run;
Note that assigning values to parameters declared as OUT or INOUT causes the
corresponding input arguments to be written. Because of this, the input
argument for an OUT or INOUT parameter should be something with an
"lvalue", such as a variable like v in the example above. A constant
or a literal argument should not be passed in for an OUT/INOUT parameter.
We can also write functions instead of procedures. In a function declaration,
we follow the parameter list by RETURN and the type of the return value:
CREATE FUNCTION <func_name>(<param_list>) RETURN
<return_type> AS ...
In the body of the function definition, "RETURN <expression>;"
exits from the function and returns the value of <expression>.
To find out what procedures and functions you have created, use the following
SQL query:
select object_type, object_name
from user_objects
where object_type = 'PROCEDURE'
or object_type = 'FUNCTION';
To drop a stored procedure/function:
drop procedure <procedure_name>;
drop function <function_name>;
Discovering 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 <procedure_name>;
Alternatively, you can type, SHO ERR (short for SHOW ERRORS) to see the most
recent compilation error.
Printing Variables
Sometimes we might want to print the value of a PL/SQL local variable.
A ``quick-and-dirty'' way is to store it as the sole tuple of some relation and
after the PL/SQL statement print the relation with a SELECT statement. A more
couth way is to define a bind variable, which is the only kind that may be
printed with a print command. Bind variables are the kind that must be prefixed
with a colon in PL/SQL statements.
The steps are as follows:
1. We declare a bind variable as follows:
VARIABLE <name> <type>
where the type can be only one of three things: NUMBER, CHAR, or CHAR(n).
2. We may then assign to the variable in a following PL/SQL statement, but we
must prefix it with a colon.
3. Finally, we can execute a statement
PRINT :<name>;
outside the PL/SQL statement
Here is a trivial example, which prints the value 1.
VARIABLE x NUMBER
BEGIN
:x := 1;
END;
.
run;
PRINT :x;
PL/SQL BLOCK
The pl/sql block contains the following section:--
-----The DECLARE section.
-----The Master BEGIN and END section that contains the EXCEPTION section.
The declare section contains declaration of memory variables, constants,
cursors etc. The begin section contains sql executable statements and pl/sql
executable statements. The exception section contains code to handle errors
that may arise during the execution of the code block. The end declares the end
of pl/sql block.
A bit about it's working. When you typed out the pl/sql block for execution. It
is sent to the pl/sql engine, where procedural statements are executed; and sql
statements are sent to the sql executor in the oracle engine. Since pl/sql
engine resides in the oracle engine, the codes executes smoothly and
efficiently.
PL/SQL DATA-TYPE
This is easy since it includes almost all the data types which u have used in
sql such as date, varchar, number, char etc etc... Some of the attributes such
as %TYPE is also used. This attribute automatically takes in the default data
type of the sql table from which u have passed the query. We will discuss this
later.
Remember in pl/sql a variable name must begin with a character and can be
followed by maximum of 29 other characters. Reserved words can't be used unless
enclosed within double quotes. Variables must be separated from each other by
at least one space or by a punctuation mark. You can assign values of operator
using := operator. I won't discuss about logical comparisons operators such as
<, > , >=, NOT, TRUE, AND, OR, NULL etc since they r quite easy to
understand.
HOW TO DISPLAY MESSAGES ON SCREEN ---
DBMS_OUTPUT : is a package that includes a number of procedure and functions
that accumulate information in a buffer so that it can be retrieved later.
These functions can also be used to display messages to the user.
PUT_LINE : Put a piece of information in the package buffer followed by an
end-of-line marker. It can also be used to display message to the user.
Put_line expects a single parameter of character data type. If used to display
a message, it is the message 'string'.
EG: dbms_output.put_line(x);
REMEMBER: To display messages to the user the SERVEROUTPUT should be set to ON.
SERVEROUTPUT is a sql*plus environment parameter that displays the information
pased as a parameter to the PUT_LINE function.
EG: SET SERVEROUTPUT ON
A bit about comments. A comment can have 2 forms i.e.
-- The comment line begins with a double hyphen (--). The entire line will be
treated as a comment.
-- The C style comment such as /* i am a comment */
CONDITIONAL CONTROL AND ITERATIVE CONTROL AND SEQUENTIAL CONTROL
IF and else.....
IF --Condition THEN
--Action
ELSEIF --Condition THEN
--Action
ELSE
--Action
END IF;
SIMPLE LOOP
loop
-- Sequence of statements;
end loop;
the loop ends when u use EXIT WHEN statement --condition
WHILE LOOP
While --condition
loop
--sequence of statements
end loop;
FOR LOOP
FOR i in 1..10
loop
--sequence of statements
end loop;
GOTO (sequential control)
GOTO X;
<< X >>
EXAMPLES
--ADDITION
declare
a number;
b number;
c number;
begin
a:=&a;
b:=&b;
c:=a+b;
dbms_output.put_line('Sum of ' || a || ' and ' || b || ' is ' || c);
Here & is used to take user input at runtime.....
--SUM OF 100 NUMBERS
Declare
a number;
s1 number default 0;
Begin
a:=1;
loop
s1:=s1+a;
exit when (a=100);
a:=a+1;
end loop;
dbms_output.put_line('Sum between 1 to 100 is '||s1);
End;
--SUM OF odd NUMBERS USING USER INPUT...for loop
declare
n number;
sum1 number default 0;
endvalue number;
begin
endvalue:=&endvalue;
n:=1;
for n in 1.. endvalue
loop
if mod(n,2)=1
then
sum1:=sum1+n;
end if
end loop;
dbms_output.put_line('sum = ' || sum1);
end;
--SUM OF 100 ODD NUMBER .. WHILE LOOP
declare
n number;
endvalue number;
sum1 number default 0;
begin
endvalue:=&endvalue;
n:=1;
while (n < endvalue)
loop
sum1:=sum1+n;
n:=n+2;
end loop;
dbms_output.put_line('Sum of odd numbers between 1 and ' || endvalue || ' is '
|| sum1);
end;
--CALCULATION OF NET SALARY
declare
ename varchar2(15);
basic number;
da number;
hra number;
pf number;
netsalary number;
begin
ename:=&ename;
basic:=&basic;
da:=basic * (41/100);
hra:=basic * (15/100);
if (basic < 3000)
then
pf:=basic * (5/100);
elsif (basic >= 3000 and basic <= 5000)
then
pf:=basic * (7/100);
elsif (basic >= 5000 and basic <= 8000)
then
pf:=basic * (8/100);
else
pf:=basic * (10/100);
end if;
netsalary:=basic + da + hra -pf;
dbms_output.put_line('Employee name : ' || ename);
dbms_output.put_line('Providend Fund : ' || pf);
dbms_output.put_line('Net salary : ' || netsalary);
end;
--MAXIMUM OF 3 NUMBERS
Declare
a number;
b number;
c number;
d number;
Begin
dbms_output.put_line('Enter a:');
a:=&a;
dbms_output.put_line('Enter b:');
b:=&b;
dbms_output.put_line('Enter c:');
c:=&b;
if (a>b) and (a>c) then
dbms_output.putline('A is Maximum');
elsif (b>a) and (b>c) then
dbms_output.putline('B is Maximum');
else
dbms_output.putline('C is Maximum');
end if;
End;
--QUERY EXAMPLE--IS SMITH EARNING ENOUGH
declare
s1 emp.sal %type;
begin
select sal into s1 from emp
where ename = 'SMITH';
if(no_data_found)
then
raise_application_error
(20001,'smith is not present');
end if;
if(s1 > 10000)
then
raise_application_error
(20002,'smith is earning enough');
end if;
update emp set sal=sal + 500
where ename='SMITH';
end;
--PRIME NO OR NOT
DECLARE
no NUMBER (3) := &no;
a NUMBER (4);
b NUMBER (2);
BEGIN
FOR i IN 2..no - 1
LOOP
a := no MOD i;
IF a = 0
THEN
GOTO out;
END IF;
END LOOP;
<>
IF a = 1
THEN
DBMS_OUTPUT.PUT_LINE (no || ' is a prime number');
ELSE
DBMS_OUTPUT.PUT_LINE (no || ' is not a prime number');
END IF;
END;
--SIMPLE EXAMPLE OF LOOP STATEMENT I.E. EXIT WHEN
Declare
a number:= 100;
begin
loop
a := a+25;
exit when a=250;
end loop;
dbms_output.put_line (to_Char(a));
end;
--EXAMPLE OF WHILE LOOP
Declare
i number:=0;
j number:= 0;
begin
while i <=100 loop
j := j+1;
i := i +2;
end loop;
dbms_output.put_line(to_char(i));
end;
--EXAMPLE OF FOR LOOP
Declare
begin
for i in 1..10
loop
dbms_output.put_line(to_char(i));
end loop;
end;
--SEQUENTIAL CONTROL GOTO
declare
--takes the default datatype of the column of the table price
cost price.minprice%type;
begin
select stdprice into cost from price where prodial in (Select prodid from
product where prodese = "shampoo");
if cost > 7000 then
goto Upd;
end if;
<< Upd >>
Update price set minprice = 6999 where prodid=111;
end;
--CALCULATE THE AREA OF A CIRCLE FOR A VALUE OF RADIUS VARYING FROM 3 TO 7.
STORE THE RADIUS AND THE CORRESPONDING VALUES OF CALCULATED AREA IN A TABLE
AREAS.
Declare
pi constant number(4,2) := 3.14;
radius number(5);
area number(14,2);
Begin
radius := 3;
While radius <=7
Loop
area := pi* power(radius,2);
Insert into areas values (radius, area);
radius:= radius+1;
end loop;
end;
--REVERSING A NUMBER 5639 TO 9365
Declare
given_number varchar(5) := '5639';
str_length number(2);
inverted_number varchar(5);
Begin
str_length := length(given_number);
For cntr in reverse 1..str_length
loop
inverted_number := inverted_number || substr(given_number, cntr, 1);
end loop;
dbms_output.put_line('The Given no is ' || given_number);
dbms_output.put_line('The inverted number is ' || inverted_number);
end;
EXCEPTION HANDLING IN PLSQL
Errors in pl/sql block can be handled...error handling refers to the way we
handle the errors in pl/sql block so that no crashing stuff of code takes
place...This is exactly the same as we do in C++ or java..right!!
There are two type:
===> predefined exceptions
===> user defined exceptions
The above 2 terms are self explanatory
predefined exceptions:
No-data-found == when no rows are returned
Cursor-already-open == when a cursor is opened in advance
Dup-val-On-index == for duplicate entry of index..
Storage-error == if memory is damaged
Program-error == internal problem in pl/sql
Zero-divide == divide by zero
invalid-cursor == if a cursor is not open and u r trying to close it
Login-denied == invalid user name or password
Invalid-number == if u r inserting a string datatype for a number datatype
which is already declared
Too-many-rows == if more rows r returned by select statement
SYNTAX
begin
sequence of statements;
exception
when --exception name then
sequence of statements;
end;
EXAMPLES
--When there is no data returned by row
declare
price item.actualprice%type;
begin
Select actual price into price from item where qty=888;
when no-data-found then
dbms_output.put_line('item missing');
end;
--EXAMPLE OF USER DEFINED EXCEPTION
DECLARE
e_rec emp%ROWTYPE;
e1 EXCEPTION;
sal1 emp.sal%TYPE;
BEGIN
SELECT sal INTO sal1 FROM emp WHERE deptno = 30 AND ename = 'John';
IF sal1 < 5000 THEN
RAISE e1;
sal1 := 8500;
UPDATE emp SET sal = sal1 WHERE deptno = 30 AND ename = 'John';
END IF;
EXCEPTION
WHEN no_data_found THEN
RAISE_APPLICATION_ERROR (-20001, 'John is not there.');
WHEN e1 THEN
RAISE_APPLICATION_ERROR (-20002, 'Less Salary.');
END;
--EXAMPLE OF RAISE-APPLICATION-ERROR... THIS IS YOUR OWN ERROR
STATEMENT...YOU RAISE YOUR OWN ERROR
Declare
s1 emp.sal %type;
begin
select sal into s1 from emp where ename='SOMDUTT';
if(no-data-found) then
raise_application_error(20001, 'somdutt is not there');
end if;
if(s1 > 10000) then
raise_application_error(20002, 'somdutt is earing a lot');
end if;
update emp set sal=sal+500 where ename='SOMDUTT';
end;
--INTERESTING EG OF USER DEFINED EXCEPTIONS
Declare
zero-price exception;
price number(8);
begin
select actualprice into price from item where ordid =400;
if price=0 or price is null then
raise zero-price;
end if;
exception
when zero-price then
dbms_output.put_line('raised xero-price exception');
end;
CURSORS
Cursor is a work area in pl/sql which is used by sql server used to store the
result of a query. Each column value is pointed using pointer. You can
independently manipulate cursor values. A bit about it's working..... suppose
you ask for a query stored in the server ... at first a cursor consisting of
query result is created in server...now the cursor is transferred to the client
where again cursor is created and hence the result is displayed......
Cursors are of 2 types: implicit and explicit.......implicit cursors are
created by oracle engine itself while explicit cursors are created by the
users......cursors are generally used in such a case when a query returns more
than one rows....normal pl/sql returning more than one rows givens error but
using cursor this limitation can be avoided....so cursors are used....
Cursor attributes
%ISOPEN == returns true if ursor is open, false otherwise
%FOUND == returns true if recod was fetched successfully, false otherwise
%NOTFOUND == returns true if record was not fetched successfully, false
otherwise
%ROWCOUNT == returns number of records processed from the cursor.
Very important: Cursor can be controlled using following 3 control statements.
They are Open, Fetch, Close.....open statement identifies the active set...i.e.
query returned by select statement...close statement closes the cursor...and
fetch statement fetches rows into the variables...Cursors can be made into use
using cursor for loop and fetch statement...we will see the corresponding
examples...
EXAMPLES
--EXAMPLE OF SQL%FOUND (IMPLICIT CURSORS)
begin
update employee set salary=salary *0.15
where emp_code = &emp_code;
if sql%found then
dbms_output.put_line('employee record modified successfully');
else
dbms_output.put_line('employee no does not exist');
end if;
end;
--EXAMPLE FOR SQL%NOTFOUND (IMPLICIT CURSORS)
begin
update employee set salary = salary*0.15 where emp_code = &emp_code;
if sql%notfound then
dbms_output.put_line('employee no . does not exist');
else
dbms_output.put_line('employee record modified successfully');
end if;
end;
--EXAMPLE FOR SQL%ROWCOUNT (IMPLICIT CURSORS)
declare
rows_affected char(4);
begin
update employee set salary = salary*0.15 where job='programmers';
rows_affected := to_char(sql%rowcount);
if sql%rowcount > 0 then
dbms_output.put_line(rows_affected || 'employee records modified
successfully');
else
dbms_output.put_line('There are no employees working as programmers');
end if;
end;
Syntax of explicit cursor: Cursor cursorname is sql select statement;
Syntax of fetch : fetch cursorname into variable1, variable2...;
Syntax of close; close cursorname;
Syntax of open cursor; open cursorname;
--EXPLICIT CURSOR EG
DECLARE
CURSOR c1 is SELECT * FROM emp;
str_empno emp.empno%type;
str_ename emp.ename%type;
str_job emp.job%type;
str_mgr emp.mgr%type;
str_hiredate emp.hiredate%type;
str_sal emp.sal%type;
str_comm emp.comm%type;
str_deptno emp.deptno%type;
rno number;
BEGIN
rno := &rno;
FOR e_rec IN c1
LOOP
IF c1%rowcount = rno THEN
DBMS_OUTPUT.PUT_LINE (str_empno || ' ' || str_ename || ' ' || str_job || ' ' ||
str_mgr || ' ' || str_hiredate || ' ' || str_sal || ' ' || str_comm || ' ' ||
str_deptno);
END IF;
END LOOP;
END;
--ANOTHER EG DISPLAYING VALUE OF A TABLE
DECLARE
CURSOR c1 IS SELECT * FROM emp;
e_rec emp%rowtype;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO e_rec;
DBMS_OUTPUT.PUT_LINE('Number: ' || ' ' || e_rec.empno);
DBMS_OUTPUT.PUT_LINE('Name : ' || ' ' || e_rec.ename);
DBMS_OUTPUT.PUT_LINE('Salary: ' || ' ' || e_rec.sal);
EXIT WHEN c1%NOTFOUND;
END LOOP;
CLOSE c1;
END;
-- Display details of Highest 10 salary paid employee
DECLARE
CURSOR c1 IS SELECT * FROM emp ORDER BY sal DESC;
e_rec emp%rowtype;
BEGIN
FOR e_rec IN c1
LOOP
DBMS_OUTPUT.PUT_LINE('Number: ' || ' ' || e_rec.empno);
DBMS_OUTPUT.PUT_LINE('Name : ' || ' ' || e_rec.ename);
DBMS_OUTPUT.PUT_LINE('Salary: ' || ' ' || e_rec.sal);
EXIT WHEN c1%ROWCOUNT >= 10;
END LOOP;
END;
-- EXAMPLE OF CURSOR FOR LOOP
declare cursor c1 is select * from somdutt;
begin
for outvariable in c1
loop
exit when c1%notfound;
if outvariable.age < 21 then
dbms_output.put_line(outvariable.age || ' ' || outvariable.name);
end if;
end loop;
end;
--ref STRONG CURSORS
DECLARE
TYPE ecursor IS REF CURSOR RETURN emp%ROWTYPE;
ecur ecursor;
e_rec emp%ROWTYPE;
dn NUMBER;
BEGIN
dn := &deptno;
OPEN ecur FOR SELECT * FROM emp WHERE deptno = dn;
FOR e_rec IN ecur
LOOP
DBMS_OUTPUT.PUT_LINE ('Employee No : ' || e_rec.empno);
DBMS_OUTPUT.PUT_LINE ('Employee Salary: ' || e_rec.salary);
END LOOP;
END;
--REF WEAK CURSORS
DECLARE
TYPE tcursor IS REF CURSOR;
tcur tcursor;
e1 emp%ROWTYPE;
d1 dept%ROWTYPE;
tname VARCHAR2(20);
BEGIN
tname := &tablename;
IF tname = 'emp' THEN
OPEN tcur FOR SELECT * FORM emp;
DBMS_OUTPUT.PUT_LINE ('Emp table opened.');
close tcur;
DBMS_OUTPUT.PUT_LINE ('Emp table closed.');
ELSE IF tname = 'dept' THEN
OPEN tcur FOR SELECT * FROM dept;
DBMS_OUTPUT.PUT_LINE ('Dept table opened.');
close tcur;
DBMS_OUTPUT.PUT_LINE ('Emp table closed.');
ELSE
RAISE_APPLICATION_ERROR (-20004, 'Table name is wrong');
END IF;
END;
--CURSOR FOR LOOP WITH PARAMETERS
Declare
Cursor c1(Dno number) is select * from emp where deptno = dno;
begin
for empree in c1(10) loop;
dbms_output.put_line(empree.ename);
end loop;
end;
TRIGGERS
Trigger is a stored procedure which is called implicitly by oracle
engine whenever a insert, update or delete statement is fired.
Advantages of database triggers:
---> Data is generated on it's own
---> Replicate table can be maintained
---> To enforce complex integrity contraints
---> To edit data modifications
---> To autoincrement a field
etc..
Syntax: Create or replace trigger --triggername-- [before/after]
[insert/pdate/delete] on --tablename-- [for each satement/ for each row] [when
--condition--] plus..begin.and exception
Triggers are of following type: before or after trigger ....and for each row and
for each statement trigger... before trigger is fired before
insert/update/delete statement while after trigger is fired after
insert/update/delete statement...for each row and for each statements triggers
are self explainatory..
EXAMPLE
-- A database trigger that allows changes to employee table only during the
business hours(i.e. from 8 a.m to 5.00 p.m.) from monday to saturday. There is
no restriction on viewing data from the table -CREATE OR REPLACE TRIGGER
Time_Check BEFORE INSERT OR UPDATE OR DELETE ON EMP BEGIN IF
TO_NUMBER(TO_CHAR(SYSDATE,'hh24')) < 10 OR
TO_NUMBER(TO_CHAR(SYSDATE,'hh24')) >= 17 OR TO_CHAR(SYSDATE,'DAY') = 'SAT'
OR TO_CHAR(SYSDATE,'DAY') = 'SAT' THEN RAISE_APPLICATION_ERROR (-20004,'YOU CAN
ACCESS ONLY BETWEEN 10 AM TO 5 PM ON MONDAY TO FRIDAY ONLY.'); END IF; END;
--YOU HAVE 2 TABLES WITH THE SAME STRUCTURE. IF U DELETE A RECORD FROM ONE
TABLE , IT WILL BE INSERTED IN 2ND TABLE ED TRIGGERNAME Create or replace
trigger backup after delete on emp fro each row begin insert into emp/values
(:old.ename,:old.job,:old.sal); end; save the file.. and then sql> @
triggername --To STICK IN SAL FIELD BY TRIGGER MEANS WHEN U ENTER GREATER THAN
5000, THEN THIS TRIGGER IS EXECUTED Create or replace trigger check before
insert on emp for each row when (New.sal > 5000); begin
raise_application_error(-20000, 'your no is greater than 5000'); end; --NO
CHANGES CAN BE DONE ON A PARTICULAR TABLE ON SUNDAY AND SATURDAY Create or
replace trigger change before on emp for each row when (to_char(sysdate,'dy')
in ('SAT','SUN')) begin raise_application_error(-200001, 'u cannot enter data
in saturnday and sunday'); end; --IF U ENTER IN EMP TABLE ENAME FIELD'S DATA IN
ANY CASE IT WILL BE INSERTED IN CAPITAL LETTERS'S ONLY Create or replace
trigger cap before insert on emp for each row begin :New.ename =
upper(:New.ename); end; --A TRIGGER WHICH WILL NOT ALLOW U TO ENTER DUPLICATE
VALUES IN FIELD EMPNO IN EMP TABLE Create or replace trigger dubb before insert
on emp for each row Declare cursor c1 is select * from emp; x emp%rowtype;
begin open c1; loop fetch c1 into x; if :New.empno = x.empno then
dbms_output.put_line('you entered duplicated no'); elseif :New.empno is null
then dbms_output.put_line('you empno is null'); end if; exit when c1%notfound;
end loop; close c1; end;
Remember trigger can be dropped using Drop Trigger triggername ; statement...
PROCEDURES AND FUNCTIONS
procedure is a subprogram...which consists of a set of sql statement.
Procedures are not very different from functions. A procedure or function is a
logically grouped set of SQL and PL/SQL statements that perform a specific
task. A stored procedure or function is a named pl/sql code block that have
been compiled and stored in one of the oracle engines's system tables.
To make a procedure or function dynamic either of them can be passed parameters
before execution. A procedure or function can then change the way it works
depending upon the parameters passed prior to its execution.
Procedures and function are made up of a declarative part, an executable part
and an optional exception-handling part
A declaration part consists of declarations of variables. A executable part
consists of the logic i.e. sql statements....and exception handling part
handles any error during run-time
The oracle engine performs the following steps to execute a procedure or
function....Verifies user access, Verifies procedure or function validity and
executes the procedure or function. Some of the advantages of using procedures
and functions are: security, performance, memory allocation, productivity,
integrity.
Most important the difference between procedures and functions: A function must
return a value back to the caller. A function can return only one value to the
calling pl/sql block. By defining multiple out parameters in a procedure,
multiple values can be passed to the caller. The out variable being global by
nature, its value is accessible by any pl/sql code block including the calling
pl/sql block.
Syntax for stored procedure:
CREATE OR REPLACE PROCEDURE [schema] procedure name (argument { IN, OUT, IN
OUT} data type, ..) {IS, AS}
variable declarations; constant declarations; BEGIN
pl/sql subprogram body;
EXCEPTION
exception pl/sql block;
END;
Syntax for stored function:
CREATE OR REPLACE FUNCTION [schema] functionname(argument IN data type, ..)
RETURN data type {IS, AS}
variable declarations; constant declarations; BEGIN
pl/sql subprogram body;
EXCEPTION
exception pl/sql block;
END;
The above syntax i think is self explanatory...but i will give you some details...IN
: specifies that a value for the argument must be specified when calling the
procedure or function. argument : is the name of an argument to the procedure
or function. parentheses can be omitted if no arguments are present. OUT :
specifies that the procedure passes a value for this argument back to its
calling environment after execution. IN OUT : specifies that a value for the
argument must be specified when calling the procedure and that the procedure
passes a value for this argument back to its calling environment after
execution. By default it takes IN. Data type : is the data type of an argument.
EXAMPLES
--PROCEDURE USING NO ARGUMENT..AND USING CURSOR
CREATE OR REPLACE PROCEDURE P2 IS
cursor cur1 is select * from emp;
begin
for erec in cur1
loop
dbms_output.put_line(erec.ename);
end loop;
end;
--PROCEDURE USING ARGUMENT
CREATE OR REPLACE PROCEDURE ME( X IN NUMBER) IS
BEGIN
dbms_output.put_line(x*x);
end;
sql> exec me(3);
--FUNCTION using argument
CREATE OR REPLACE FUNCTION RMT(X IN NUMBER) RETURN NUMBER IS
BEGIN
dbms_output.put_line(x*x);
--return (x*x);
end;
(make a block like this to run it.....)
begin
dbms_output.put_line(rmt(3));
end;
--CREATE A PROCEDURE THAT DELETE ROWS FROM ENQUIRY
--WHICH ARE 1 YRS BEFORE
Create or replace procedure myprocedure is begin
delete from enquiry where enquirydate <= sysdate - 1;
end;
--CREATE A PROCEDURE THAT TAKES ARGUMENT STUDENT NAME,
--AND FIND OUT FEES PAID BY THAT STUDENT
CREATE or REPLACE procedure me (namee in varchar) is
cursor c1 is select a.feespaiddate from feespaid a, enrollment b, enquiry c
where
c.enquiryno = b.enquiryno and
a.rollno = b.rollno and
c.fname = namee;
begin
for erec in c1
loop
dbms_output.put_line(erec.feespaiddate);
end loop;
end;
--SUM OF 2 Numbers
CREATE or replace procedure p1 is
Declare
a number;
b number;
c number;
Begin
a:=50;
b:=89;
c:=a+b;
dbms_output.put_line('Sum of '||a||' and '||b||' is '||c);
End;
--DELETION PROCEDURE
create or replace procedure myproc is
begin
delete from enquiry where fname='somdutt';
end;
--IN and OUT procedure example
Create or replace procedure lest ( a number, b out number) is
identify number;
begin
select ordid into identity from item where
itemid = a;
if identity < 1000 then
b := 100;
end if;
end l
--in out parameter
Create or replace procedure sample ( a in number, b in out number) is
identity number;
begin
select ordid, prodid into identity, b from item where itemid=a;
if b<600 then
b := b + 100;
end if;
end;
now procedure is called by passing parameter
declare
a number;
b number;
begin
sample(3000, b)
dbms_output.put_line(1th value of b is 11 b);
end ;
--SIMILAR EXAMPLE AS BEFORE
create or replace procedure getsal( sal1 in out number) is
begin
select sal into sal1 from emp
where empno = sal1;
end ;
now use the above in plsql block
declare
sal1 number := 7999;
begin
getsal(sal1);
dbms_output.put_line('The employee salary is' || sal1);
end ;
You can make a procedure and functions similarly.....also if u wanna drop a
function then use drop function functionname and for procedure use drop
procedure procedurename
PACKAGES
A package is an oracle object, which holds other objects within it. Objects
commonly held within a package are procedures, functions, variables, constants,
cursors and exceptions. Packages in plsql is very much similar to those
packages which we use in JAVA......yeah!! java packages holds numerous
classes..right!!!...
A package has 2 parts..... package specification and package body
A package specification part consists of all sort of declaration of functions
and procedures while package body consists of codings and logic of declared
functions and procedures...
EXAMPLE
--SIMPLEST EG
--specification
create or replace package pack2 is
function rmt(x in number) return number;
procedure rmt1(x in number);
end;
--body
create or replace package body pack2 is
function rmt(x in number) return number is
begin
return (x*x);
end;
procedure rmt1(x in number) is
begin
dbms_output.put_line(x*x);
end;
end;
(how to run.....)
exec packagename.procedurename
i.e.
exec pack2.rmt1(3);
As shown above u can put in complicated procedures and functions inside the
package...I have just shown a simple example...you can easily modify the above
code to fit your requirement......Just try out packages which includes cursors,
procedures and functions..etc..Remeber pl/sql supports overloading...i.e. you
can use the same function or procedure name in your application but with
different no or type of arguments.