Anda di halaman 1dari 49

What is SQL?

SQL (pronounced "ess-que-el") stands for Structured Query Language. SQL is used
to communicate with a database. According to ANSI (American National Standards
Institute), it is the standard language for relational database management systems.
SQL statements are used to perform tasks such as update data on a database, or
retrieve data from a database. Some common relational database management
systems that use SQL are: Oracle, Sybase, Microsoft SQL Server, Access, Ingres,
etc. Although most database systems use SQL, most of them also have their own
additional proprietary extensions that are usually only used on their system.
However, the standard SQL commands such as "Select", "Insert", "Update", "Delete",
"Create", and "Drop" can be used to accomplish almost everything that one needs to
do with a database. This tutorial will provide you with the instruction on the basics of
each of these commands as well as allow you to put them to practice using the SQL
Interpreter.

Table Basics
A relational database system contains one or more objects called tables. The data or
information for the database are stored in these tables. Tables are uniquely identified
by their names and are comprised of columns and rows. Columns contain the column
name, data type, and any other attributes for the column. Rows contain the records
or data for the columns. Here is a sample table called "weather".

city, state, high, and low are the columns. The rows contain the data for this table:

Weather

city state high low

Phoenix Arizona 105 90

Tucson Arizona 101 92

Flagstaff Arizona 88 69

San Diego California 77 60

New
Albuquerque 80 72
Mexico
Selecting Data
The select statement is used to query the database and retrieve selected data that
match the criteria that you specify. Here is the format of a simple select statement:

select "column1"
[,"column2",etc]
from "tablename"
[where "condition"];
[] = optional

The column names that follow the select keyword determine which columns will be
returned in the results. You can select as many column names that you'd like, or you
can use a "*" to select all columns.

The table name that follows the keyword from specifies the table that will be queried
to retrieve the desired results.

The where clause (optional) specifies which data values or rows will be returned or
displayed, based on the criteria described after the keyword where.

Conditional selections used in the where clause:

= Equal
> Greater than
< Less than
>= Greater than or equal
<= Less than or equal
<> Not equal to
LIKE *See note below

The LIKE pattern matching operator can also be used in the conditional selection of
the where clause. Like is a very powerful operator that allows you to select only rows
that are "like" what you specify. The percent sign "%" can be used as a wild card to
match any possible character that might appear before or after the characters
specified. For example:

select first, last, city


from empinfo
where first LIKE 'Er%';

This SQL statement will match any first names that start with 'Er'. Strings must be
in single quotes.

Or you can specify,

select first, last


from empinfo
where last LIKE '%s';
This statement will match any last names that end in a 's'.

select * from empinfo


where first = 'Eric';

This will only select rows where the first name equals 'Eric' exactly.

Sample Table: empinfo

first last id age city state

John Jones 99980 45 Payson Arizona

Mary Jones 99982 25 Payson Arizona

Eric Edwards 88232 32 San Diego California

Mary Ann Edwards 88233 32 Phoenix Arizona

Ginger Howell 98002 42 Cottonwood Arizona

Sebastian Smith 92001 23 Gila Bend Arizona

Gus Gray 22322 35 Bagdad Arizona

Mary Ann May 32326 52 Tucson Arizona

Erica Williams 32327 60 Show Low Arizona

Leroy Brown 32380 22 Pinetop Arizona

Elroy Cleaver 32382 22 Globe Arizona

Enter the following sample select statements in the SQL Interpreter Form at the
bottom of this page. Before you press "submit", write down your expected results.
Press "submit", and compare the results.

select first, last, city from empinfo;

select last, city, age from empinfo


where age > 30;

select first, last, city, state from empinfo


where first LIKE 'J%';

select * from empinfo;

select first, last, from empinfo


where last LIKE '%s';

select first, last, age from empinfo


where last LIKE '%illia%';

select * from empinfo where first = 'Eric';


Select statement exercises
Enter select statements to:

1. Display the first name and age for everyone that's in the table.
2. Display the first name, last name, and city for everyone that's not from
Payson.
3. Display all columns for everyone that is over 40 years old.
4. Display the first and last names for everyone whose last name ends in an
"ay".
5. Display all columns for everyone whose first name equals "Mary".
6. Display all columns for everyone whose first name contains "Mary".

Creating Tables
The create table statement is used to create a new table. Here is the format of a
simple create table statement:

create table "tablename"


("column1" "data type",
"column2" "data type",
"column3" "data type");

Format of create table if you were to use optional constraints:

create table "tablename"


("column1" "data type"
[constraint],
"column2" "data type"
[constraint],
"column3" "data type"
[constraint]);
[ ] = optional

Note: You may have as many columns as you'd like, and the constraints are
optional.

Example:

create table employee


(first varchar(15),
last varchar(20),
age number(3),
address varchar(30),
city varchar(20),
state varchar(20));

To create a new table, enter the keywords create table followed by the table name,
followed by an open parenthesis, followed by the first column name, followed by the
data type for that column, followed by any optional constraints, and followed by a
closing parenthesis. It is important to make sure you use an open parenthesis before
the beginning table, and a closing parenthesis after the end of the last column
definition. Make sure you seperate each column definition with a comma. All SQL
statements should end with a ";".

The table and column names must start with a letter and can be followed by letters,
numbers, or underscores - not to exceed a total of 30 characters in length. Do not
use any SQL reserved keywords as names for tables or column names (such as
"select", "create", "insert", etc).

Data types specify what the type of data can be for that particular column. If a
column called "Last_Name", is to be used to hold names, then that particular column
should have a "varchar" (variable-length character) data type.

Here are the most common Data types:

char(size) Fixed-length character string. Size is specified in parenthesis. Max


255 bytes.
varchar(size) Variable-length character string. Max size is specified in parenthesis.

number(size) Number value with a max number of column digits specified in


parenthesis.
date Date value

number(size,d) Number value with a maximum number of digits of "size" total, with
a maximum number of "d" digits to the right of the decimal.

number(size,d) Number value with a maximum number of digits of "size" total, with
a maximum number of "d" digits to the right of the decimal.

What are constraints? When tables are created, it is common for one or more
columns to have constraints associated with them. A constraint is basically a rule
associated with a column that the data entered into that column must follow. For
example, a "unique" constraint specifies that no two records can have the same
value in a particular column. They must all be unique. The other two most popular
constraints are "not null" which specifies that a column can't be left blank, and
"primary key". A "primary key" constraint defines a unique identification of each
record (or row) in a table. All of these and more will be covered in the future
Advanced release of this Tutorial. Constraints can be entered in this SQL interpreter,
however, they are not supported in this Intro to SQL tutorial & interpreter. They will
be covered and supported in the future release of the Advanced SQL tutorial - that
is, if "response" is good.

It's now time for you to design and create your own table. You will use this table
throughout the rest of the tutorial. If you decide to change or redesign the table, you
can either drop it and recreate it or you can create a completely different one. The
SQL statement drop will be covered later.

Create Table Exercise


You have just started a new company. It is time to hire some employees. You will
need to create a table that will contain the following information about your new
employees: firstname, lastname, title, age, and salary. After you create the table,
you should receive a small form on the screen with the appropriate column names. If
you are missing any columns, you need to double check your SQL statement and
recreate the table. Once it's created successfully, go to the "Insert" lesson.

IMPORTANT: When selecting a table name, it is important to select a unique name


that no one else will use or guess. Your table names should have an underscore
followed by your initials and the digits of your birth day and month. For example,
Tom Smith, who was born on November 2nd, would name his table
myemployees_ts0211 Use this convention for all of the tables you create. Your tables
will remain on a shared database until you drop them, or they will be cleaned up if
they aren't accessed in 4-5 days. If "support" is good, I hope to eventually extend
this to at least one week. When you are finished with your table, it is important to
drop your table (covered in last lesson).

Inserting into a Table


The insert statement is used to insert or add a row of data into the table.

To insert records into a table, enter the key words insert into followed by the table
name, followed by an open parenthesis, followed by a list of column names
separated by commas, followed by a closing parenthesis, followed by the keyword
values, followed by the list of values enclosed in parenthesis. The values that you
enter will be held in the rows and they will match up with the column names that you
specify. Strings should be enclosed in single quotes, and numbers should not.

insert into "tablename"


(first_column,...last_column)
values (first_value,...last_value);

In the example below, the column name first will match up with the value 'Luke',
and the column name state will match up with the value 'Georgia'.

Example:

insert into employee


(first, last, age, address, city, state)
values ('Luke', 'Duke', 45, '2130 Boars Nest',
'Hazard Co', 'Georgia');

Note: All strings should be enclosed between single quotes: 'string'

Insert statement exercises


It is time to insert data into your new employee table.

Your first three employees are the following:

Jonie Weber, Secretary, 28, 19500.00


Potsy Weber, Programmer, 32, 45300.00
Dirk Smith, Programmer II, 45, 75020.00
Enter these employees into your table first, and then insert at least 5 more of your
own list of employees in the table.

After they're inserted into the table, enter select statements to:

1. Select all columns for everyone in your employee table.


2. Select all columns for everyone with a salary over 30000.
3. Select first and last names for everyone that's under 30 years old.
4. Select first name, last name, and salary for anyone with "Programmer" in
their title.
5. Select all columns for everyone whose last name contains "ebe".
6. Select the first name for everyone whose first name equals "Potsy".
7. Select all columns for everyone over 80 years old.
8. Select all columns for everyone whose last name ends in "ith".

Create at least 5 of your own select statements based on specific information that
you'd like to retrieve.

Updating Records
The update statement is used to update or change records that match a specified
criteria. This is accomplished by carefully constructing a where clause.

update "tablename"
set "columnname" =
"newvalue"
[,"nextcolumn" =
"newvalue2"...]
where "columnname"
OPERATOR "value"
[and|or "column"
OPERATOR "value"];

[] = optional

[The above example was line wrapped for better viewing on this Web page.]

Examples:

update phone_book
set area_code = 623
where prefix = 979;

update phone_book
set last_name = 'Smith', prefix=555, suffix=9292
where last_name = 'Jones';

update employee
set age = age+1
where first_name='Mary' and last_name='Williams';

Update statement exercises


After each update, issue a select statement to verify your changes.

1. Jonie Weber just got married to Bob Williams. She has requested that her last
name be updated to Weber-Williams.
2. Dirk Smith's birthday is today, add 1 to his age.
3. All secretaries are now called "Administrative Assistant". Update all titles
accordingly.
4. Everyone that's making under 30000 are to receive a 3500 a year raise.
5. Everyone that's making over 33500 are to receive a 4500 a year raise.
6. All "Programmer II" titles are now promoted to "Programmer III".
7. All "Programmer" titles are now promoted to "Programmer II".

Create at least 5 of your own update statements and submit them.

Deleting Records
The delete statement is used to delete records or rows from the table.

delete from "tablename"

where "columnname"
OPERATOR "value"
[and|or "column"
OPERATOR "value"];

[ ] = optional

[The above example was line wrapped for better viewing on this Web page.]

Examples:

delete from employee;

Note: if you leave off the where clause, all records will be deleted!

delete from employee


where lastname = 'May';

delete from employee


where firstname = 'Mike' or firstname = 'Eric';

To delete an entire record/row from a table, enter "delete from" followed by the
table name, followed by the where clause which contains the conditions to delete. If
you leave off the where clause, all records will be deleted.

Delete statement exercises


(Use the select statement to verify your deletes):

1. Jonie Weber-Williams just quit, remove her record from the table.
2. It's time for budget cuts. Remove all employees who are making over 70000
dollars.

Create at least two of your own delete statements, and then issue a command to
delete all records from the table.

Drop a Table
The drop table command is used to delete a table and all rows in the table.

To delete an entire table including all of its rows, issue the drop table command
followed by the tablename. drop table is different from deleting all of the records in
the table. Deleting all of the records in the table leaves the table including column
and constraint information. Dropping the table removes the table definition as well as
all of its rows.

drop table "tablename"

Example:

drop table myemployees_ts0211;

Drop Table exercises


1. Drop your employee table.

SQLCourse2.com!
Welcome to SQLCourse2.com! This unique SQL Tutorial is the "sequel" to the highly
successful SQLCourse.com site and will provide you with more advanced easy-to-
follow SQL Instruction and the ability to practice what you learn on-line with
immediate feedback! You will receive immediate results on a web page after
submitting your SQL Commands.

This continuation course will provide you with critical need-to-know advanced
features and clauses of the SELECT statement that weren't supported in the previous
SQLCourse.com site. Everything you learn here will be ANSI SQL compliant and
should work with most SQL databases such as Oracle, SQL Server, mySQL, MS
Access, Informix, Sybase, or any other ANSI SQL compliant database.

If you're already familar with the basics of SQL, you can still use this as a refresher,
and practice some SQL statements
Start Here - Intro
This Intermediate/Advanced SQL Tutorial will cover the SELECT statement in great
detail. The SELECT statement is the core of SQL, and it is likely that the vast
majority of your SQL commands will be SELECT statements. Due to the enormous
amount of options available for the SELECT statement, this entire tutorial has been
dedicated to it.

When constructing SQL Queries (with the SELECT statement), it is very useful to
know all of the possible options and the best or more efficient way to do things. This
Tutorial will help provide you with those skills.

Although it is recommended that you go through SQLCourse.com (the original site)


or at least the SELECT statement on SQLCourse.com, it isn't required. YOu can start
with this site, and then proceed to SQLCourse.com when you are finished.
SQLCourse.com covers four other SQL Commands and allows you to practice those
as well.

Note: This Intermediate/Advanced Tutorial will allow you to practice all of the
advanced features coverd here on this site. Not all of these commands are available
at the original SQLCourse.com site. The four other commands covered at
SQLCourse.com are not supported with this Interpreter on this site.

You can submit all of your SQL commands in the Text submission box at the
bottom of each of the main pages.

SELECT Statement
The SELECT statement is used to query the database and retrieve selected data that
match the criteria that you specify.

The SELECT statement has five main clauses to choose from, although, FROM is the
only required clause. Each of the clauses have a vast selection of options,
parameters, etc. The clauses will be listed below, but each of them will be covered in
more detail later in the tutorial.

Here is the format of the SELECT statement:

SELECT [ALL | DISTINCT] column1[,column2]

FROM table1[,table2]
[WHERE "conditions"]

[GROUP BY "column-list"]

[HAVING "conditions]

[ORDER BY "column-list" [ASC | DESC] ]

FROM & WHERE clause quick review

Example:

SELECT name, age, salary

FROM employee

WHERE age > 50;

The above statement will select all of the values in the name, age, and salary
columns from the employee table whose age is greater than 50.

Note: Remember to put a semicolon at the end of your SQL statements. The ;
indicates that your SQL statment is complete and is ready to be interpreted.

Comparison Operators

= Equal
> Greater than
< Less than
>= Greater than or equal to
<= Less than or equal to
<> or != Not equal to
LIKE String comparison test

*Note about LIKE

Example:

SELECT name, title, dept

FROM employee

WHERE title LIKE 'Pro%';


The above statement will select all of the rows/values in the name, title, and dept
columns from the employee table whose title starts with 'Pro'. This may return job
titles including Programmer or Pro-wrestler.

ALL and DISTINCT are keywords used to select either ALL (default) or the "distinct"
or unique records in your query results. If you would like to retrieve just the unique
records in specified columns, you can use the "DISTINCT" keyword. DISTINCT will
discard the duplicate records for the columns you specified after the "SELECT"
statement: For example:

SELECT DISTINCT age

FROM employee_info;

This statement will return all of the unique ages in the employee_info table.

ALL will display "all" of the specified columns including all of the duplicates. The ALL
keyword is the default if nothing is specified.

Note: The following two tables will be used throughout this course. It is
recommended to have them open in another window or print them out.

Tutorial Tables
items_ordered
customers

Review Exercises

1. From the items_ordered table, select a list of all items purchased for
customerid 10449. Display the customerid, item, and price for this customer.
2. Select all columns from the items_ordered table for whoever purchased a
Tent.
3. Select the customerid, order_date, and item values from the items_ordered
table for any items in the item column that start with the letter "S".
4. Select the distinct items in the items_ordered table. In other words, display a
listing of each of the unique items from the items_ordered table.
5. Make up your own select statements and submit them.

Aggregate Functions
MIN returns the smallest value in a given column
MAX returns the largest value in a given column
SUM returns the sum of the numeric values in a given column
AVG returns the average value of a given column
COUNT returns the total number of values in a given column
COUNT(*) returns the number of rows in a table
Aggregate functions are used to compute against a "returned column of numeric
data" from your SELECT statement. They basically summarize the results of a
particular column of selected data. We are covering these here since they are
required by the next topic, "GROUP BY". Although they are required for the "GROUP
BY" clause, these functions can be used without the "GROUP BY" clause. For
example:

SELECT AVG(salary)

FROM employee;

This statement will return a single result which contains the average value of
everything returned in the salary column from the employee table.

Another example:

SELECT AVG(salary)

FROM employee;

WHERE title = 'Programmer';

This statement will return the average salary for all employees whose title is equal to
'Programmer'

Example:

SELECT Count(*)

FROM employees;

This particular statement is slightly different from the other aggregate functions
since there isn't a column supplied to the count function. This statement will return
the number of rows in the employees table.

Use these tables for the exercises


items_ordered
customers

Review Exercises

1. Select the maximum price of any item ordered in the items_ordered table.
Hint: Select the maximum price only.>
2. Select the average price of all of the items ordered that were purchased in the
month of Dec.
3. What are the total number of rows in the items_ordered table?
4. For all of the tents that were ordered in the items_ordered table, what is the
price of the lowest tent? Hint: Your query should return the price only.

GROUP BY clause
The GROUP BY clause will gather all of the rows together that contain data in the
specified column(s) and will allow aggregate functions to be performed on the one or
more columns. This can best be explained by an example:

GROUP BY clause syntax:

SELECT column1,
SUM(column2)

FROM "list-of-tables"

GROUP BY "column-list";

Let's say you would like to retrieve a list of the highest paid salaries in each dept:

SELECT max(salary), dept

FROM employee

GROUP BY dept;

This statement will select the maximum salary for the people in each unique
department. Basically, the salary for the person who makes the most in each
department will be displayed. Their, salary and their department will be returned.

Multiple Grouping Columns - What if I wanted to display their lastname too?

Use these tables for the exercises


items_ordered
customers

For example, take a look at the items_ordered table. Let's say you want to group
everything of quantity 1 together, everything of quantity 2 together, everything of
quantity 3 together, etc. If you would like to determine what the largest cost item is
for each grouped quantity (all quantity 1's, all quantity 2's, all quantity 3's, etc.), you
would enter:

SELECT quantity, max(price)


FROM items_ordered

GROUP BY quantity;

Enter the statement in above, and take a look at the results to see if it returned what
you were expecting. Verify that the maximum price in each Quantity Group is really
the maximum price.

Review Exercises

1. How many people are in each unique state in the customers table? Select the
state and display the number of people in each. Hint: count is used to count
rows in a column, sum works on numeric data only.
2. From the items_ordered table, select the item, maximum price, and minimum
price for each specific item in the table. Hint: The items will need to be
broken up into separate groups.
3. How many orders did each customer make? Use the items_ordered table.
Select the customerid, number of orders they made, and the sum of their
orders. Click the Group By answers link below if you have any problems.

HAVING clause
The HAVING clause allows you to specify conditions on the rows for each group - in
other words, which rows should be selected will be based on the conditions you
specify. The HAVING clause should follow the GROUP BY clause if you are going to
use it.

HAVING clause syntax:

SELECT column1,
SUM(column2)

FROM "list-of-tables"

GROUP BY "column-list"

HAVING "condition";

HAVING can best be described by example. Let's say you have an employee table
containing the employee's name, department, salary, and age. If you would like to
select the average salary for each employee in each department, you could enter:

SELECT dept, avg(salary)

FROM employee

GROUP BY dept;
But, let's say that you want to ONLY calculate & display the average if their salary is
over 20000:

SELECT dept, avg(salary)

FROM employee

GROUP BY dept

HAVING avg(salary) > 20000;


Use these tables for the exercises
items_ordered
customers

Review Exercises (note: yes, they are similar to the group by exercises, but
these contain the HAVING clause requirements

1. How many people are in each unique state in the customers table that have
more than one person in the state? Select the state and display the number
of how many people are in each if it's greater than 1.
2. From the items_ordered table, select the item, maximum price, and minimum
price for each specific item in the table. Only display the results if the
maximum price for one of the items is greater than 190.00.
3. How many orders did each customer make? Use the items_ordered table.
Select the customerid, number of orders they made, and the sum of their
orders if they purchased more than 1 item.

Click the HAVING exercise answers link below if you have any problems.

ORDER BY clause
ORDER BY is an optional clause which will allow you to display the results of your
query in a sorted order (either ascending order or descending order) based on the
columns that you specify to order by.

ORDER BY clause syntax:

SELECT column1, SUM(column2)

FROM "list-of-tables"

ORDER BY
"column-list" [ASC | DESC];

[ ] = optional
This statement will select the employee_id, dept, name, age, and salary from the
employee_info table where the dept equals 'Sales' and will list the results in
Ascending (default) order based on their Salary.

ASC = Ascending Order - default

DESC = Descending Order

For example:

SELECT employee_id, dept, name, age, salary

FROM employee_info

WHERE dept = 'Sales'

ORDER BY salary;

If you would like to order based on multiple columns, you must seperate the columns
with commas. For example:

SELECT employee_id, dept, name, age, salary

FROM employee_info

WHERE dept = 'Sales'

ORDER BY salary, age DESC;


Use these tables for the exercises
items_ordered
customers

Review Exercises

1. Select the lastname, firstname, and city for all customers in the customers
table. Display the results in Ascending Order based on the lastname.
2. Same thing as exercise #1, but display the results in Descending order.
3. Select the item and price for all of the items in the items_ordered table that
the price is greater than 10.00. Display the results in Ascending order based
on the price.

Click the ORDER BY Exercise Answers link below if you have any problems.

Combining conditions and Boolean


Operators
The AND operator can be used to join two or more conditions in the WHERE clause.
Both sides of the AND condition must be true in order for the condition to be met and
for those rows to be displayed.

SELECT column1,
SUM(column2)

FROM "list-of-tables"

WHERE "condition1" AND


"condition2";

The OR operator can be used to join two or more conditions in the WHERE clause
also. However, either side of the OR operator can be true and the condition will be
met - hence, the rows will be displayed. With the OR operator, either side can be
true or both sides can be true.

For example:

SELECT employeeid, firstname, lastname, title, salary

FROM employee_info

WHERE salary >= 50000.00 AND title = 'Programmer';

This statement will select the employeeid, firstname, lastname, title, and salary from
the employee_info table where the salary is greater than or equal to 50000.00 AND
the title is equal to 'Programmer'. Both of these conditions must be true in order for
the rows to be returned in the query. If either is false, then it will not be displayed.

Although they are not required, you can use paranthesis around your conditional
expressions to make it easier to read:

SELECT employeeid, firstname, lastname, title, salary

FROM employee_info

WHERE (salary >= 50000.00) AND (title = 'Programmer');

Another Example:

SELECT firstname, lastname, title, salary

FROM employee_info

WHERE (title = 'Sales') OR (title = 'Programmer');


This statement will select the firstname, lastname, title, and salary from the
employee_info table where the title is either equal to 'Sales' OR the title is equal to
'Programmer'.

Use these tables for the exercises


items_ordered
customers

Review Exercises

1. Select the customerid, order_date, and item from the items_ordered table for
all items unless they are 'Snow Shoes' or if they are 'Ear Muffs'. Display the
rows as long as they are not either of these two items.
2. Select the item and price of all items that start with the letters 'S', 'P', or 'F'.

Click the exercise answers link below if you have any problems.

IN and BETWEEN Conditional Operators

SELECT col1, SUM(col2)

FROM "list-of-tables"

WHERE col3 IN
(list-of-values);

SELECT col1, SUM(col2)

FROM "list-of-tables"

WHERE col3 BETWEEN value1


AND value2;

The IN conditional operator is really a set membership test operator. That is, it is
used to test whether or not a value (stated before the keyword IN) is "in" the list of
values provided after the keyword IN.

For example:

SELECT employeeid, lastname, salary

FROM employee_info

WHERE lastname IN ('Hernandez', 'Jones', 'Roberts', 'Ruiz');


This statement will select the employeeid, lastname, salary from the employee_info
table where the lastname is equal to either: Hernandez, Jones, Roberts, or Ruiz. It
will return the rows if it is ANY of these values.

The IN conditional operator can be rewritten by using compound conditions using the
equals operator and combining it with OR - with exact same output results:

SELECT employeeid, lastname, salary

FROM employee_info

WHERE lastname = 'Hernandez' OR lastname = 'Jones' OR lastname =


'Roberts'
OR lastname = 'Ruiz';

As you can see, the IN operator is much shorter and easier to read when you are
testing for more than two or three values.

You can also use NOT IN to exclude the rows in your list.

The BETWEEN conditional operator is used to test to see whether or not a value
(stated before the keyword BETWEEN) is "between" the two values stated after the
keyword BETWEEN.

For example:

SELECT employeeid, age, lastname, salary

FROM employee_info

WHERE age BETWEEN 30 AND 40;

This statement will select the employeeid, age, lastname, and salary from the
employee_info table where the age is between 30 and 40 (including 30 and 40).

This statement can also be rewritten without the BETWEEN operator:

SELECT employeeid, age, lastname, salary

FROM employee_info

WHERE age >= 30 AND age <= 40;

You can also use NOT BETWEEN to exclude the values between your range.
Use these tables for the exercises
items_ordered
customers

Review Exercises

1. Select the date, item, and price from the items_ordered table for all of the
rows that have a price value ranging from 10.00 to 80.00.
2. Select the firstname, city, and state from the customers table for all of the
rows where the state value is either: Arizona, Washington, Oklahoma,
Colorado, or Hawaii.

Click the exercise answers link below if you have any problems.

Mathematical Operators
Standard ANSI SQL-92 supports the following first four basic arithmetic operators:

+ addition
- subtraction
* multiplication
/ division
% modulo

The modulo operator determines the integer remainder of the division. This operator
is not ANSI SQL supported, however, most databases support it. The following are
some more useful mathematical functions to be aware of since you might need them.
These functions are not standard in the ANSI SQL-92 specs, therefore they may or
may not be available on the specific RDBMS that you are using. However, they were
available on several major database systems that I tested. They WILL work on this
tutorial.

ABS(x) returns the absolute value of x


returns the sign of input x as -1, 0, or 1 (negative, zero, or
SIGN(x)
positive respectively)
modulo - returns the integer remainder of x divided by y (same as
MOD(x,y)
x%y)
FLOOR(x) returns the largest integer value that is less than or equal to x
CEILING(x) or returns the smallest integer value that is greater than or equal to
CEIL(x) x
POWER(x,y) returns the value of x raised to the power of y
ROUND(x) returns the value of x rounded to the nearest whole integer
returns the value of x rounded to the number of decimal places
ROUND(x,d)
specified by the value d
SQRT(x) returns the square-root value of x

For example:

SELECT round(salary), firstname

FROM employee_info

This statement will select the salary rounded to the nearest whole value and the
firstname from the employee_info table.

Use these tables for the exercises


items_ordered
customers

Review Exercises

1. Select the item and per unit price for each item in the items_ordered table.
Hint: Divide the price by the quantity.

Table Joins, a must


All of the queries up until this point have been useful with the exception of one major
limitation - that is, you've been selecting from only one table at a time with your
SELECT statement. It is time to introduce you to one of the most beneficial features
of SQL & relational database systems - the "Join". To put it simply, the "Join" makes
relational database systems "relational".

Joins allow you to link data from two or more tables together into a single query
result--from one single SELECT statement.

A "Join" can be recognized in a SQL SELECT statement if it has more than one table
after the FROM keyword.

For example:

SELECT "list-of-columns"

FROM table1,table2

WHERE "search-condition(s)"

Joins can be explained easier by demonstrating what would happen if you worked
with one table only, and didn't have the ability to use "joins". This single table
database is also sometimes referred to as a "flat table". Let's say you have a one-
table database that is used to keep track of all of your customers and what they
purchase from your store:

id first last address city state zip date item


price

Everytime a new row is inserted into the table, all columns will be be updated, thus
resulting in unnecessary "redundant data". For example, every time Wolfgang
Schultz purchases something, the following rows will be inserted into the table:

id first last address city state zip date item price


10982 Wolfgang Schultz 300 N. 1st Ave Yuma AZ 85002 032299 snowboard 45.00
snow
10982 Wolfgang Schultz 300 N. 1st Ave Yuma AZ 85002 082899 35.00
shovel
10982 Wolfgang Schultz 300 N. 1st Ave Yuma AZ 85002 091199 gloves 15.00
10982 Wolfgang Schultz 300 N. 1st Ave Yuma AZ 85002 100999 lantern 35.00
10982 Wolfgang Schultz 300 N. 1st Ave Yuma AZ 85002 022900 tent 85.00

An ideal database would have two tables:

1. One for keeping track of your customers


2. And the other to keep track of what they purchase:

"Customer_info" table:

customer_number firstname lastname address city state zip

"Purchases" table:

customer_number date item price

Now, whenever a purchase is made from a repeating customer, the 2nd table,
"Purchases" only needs to be updated! We've just eliminated useless redundant data,
that is, we've just normalized this database!

Notice how each of the tables have a common "cusomer_number" column. This
column, which contains the unique customer number will be used to JOIN the two
tables. Using the two new tables, let's say you would like to select the customer's
name, and items they've purchased. Here is an example of a join statement to
accomplish this:

SELECT customer_info.firstname, customer_info.lastname, purchases.item


FROM customer_info, purchases

WHERE customer_info.customer_number = purchases.customer_number;

This particular "Join" is known as an "Inner Join" or "Equijoin". This is the most
common type of "Join" that you will see or use.

Notice that each of the colums are always preceeded with the table name and a
period. This isn't always required, however, it IS good practice so that you wont
confuse which colums go with what tables. It is required if the name column names
are the same between the two tables. I recommend preceeding all of your columns
with the table names when using joins.

Note: The syntax described above will work with most Database Systems
-including the one with this tutorial. However, in the event that this doesn't
work with yours, please check your specific database documentation.

Although the above will probably work, here is the ANSI SQL-92 syntax specification
for an Inner Join using the preceding statement above that you might want to try:

SELECT customer_info.firstname, customer_info.lastname, purchases.item

FROM customer_info INNER JOIN purchases

ON customer_info.customer_number = purchases.customer_number;

Another example:

SELECT employee_info.employeeid, employee_info.lastname,


employee_sales.comission

FROM employee_info, employee_sales

WHERE employee_info.employeeid = employee_sales.employeeid;

This statement will select the employeeid, lastname (from the employee_info table),
and the comission value (from the employee_sales table) for all of the rows where
the employeeid in the employee_info table matches the employeeid in the
employee_sales table.

Use these tables for the exercises


items_ordered
customers

Review Exercises

1. Write a query using a join to determine which items were ordered by each of
the customers in the customers table. Select the customerid, firstname,
lastname, order_date, item, and price for everything each customer
purchased in the items_ordered table.
2. Repeat exercise #1, however display the results sorted by state in descending

SQL Interpreter
Note: This SQL Interpreter/Database on this site will only support the commands
covered on this tutorial - specifically, the SELECT statement. If you are interested in
using the other SQL commands, please go to: sqlcourse.com

This site will support all of the advanced SELECT statement options that are not
supported on the original sqlcourse.com site.

You must use the two tables provided on each of the pages to select from:

Use these tables for the exercises


items_ordered
customers

Results-oriented marketers of technology products and services rely on the


internet.com Network to help build their brands, generate leads and make sales.

Our philosophy of doing business is a simple one: we succeed only when our clients
succeed. We do everything possible to ensure they do. Learn more about how we do
business by clicking here.

Our Internet media properties attract a highly qualified audience of technology


product and service buyers. They are affluent, educated and empowered by their
organizations to invest in IT and Internet solutions. View our User Profile
presentation by clicking here.

Sites that comprise the internet.com Network are some of the best known and highly
acclaimed in the technology publishing business. Our more than 200 journalists,
analysts and editors produce award winning and actionable information around the
clock and from around the world. Our sites are organized into 16 channels to enable
marketers to reach targeted audience segments easily and efficiently. See
descriptions of our 16 channels and the sites that make them up on the Channel
Description page.

The internet.com Network is comprised of technology-specific Web sites, e-mail


newsletters, double opt-in e-mail announcement lists and e-mail discussion lists. This
assortment of media ensures that our clients deliver their message in the medium
that yields optimal response. See our Media Options page for details.

The internet.com Network is a part of Jupiterweb, the online division of Jupitermedia


Corporation. See our Corporate Information page for more information about
Jupitermedia and its other divisions: Jupiter Research and Jupiter Events.
Other Database Site Links
SQLCourse.com - This is the original SQL Tutorial site that allows you to practice
what you learn on-line including: creating tables, inserting, updating, deleting, and
dropping.

dbaSupport.com - A complete guide for Oracle dbas, including study guides, Oracle
news, and online discussions

Technology & Programming Tutorials Includes: C/C++ - Pascal - Perl - Python - Ada
- Fortran - Java - JavaScript - Game Programming - MUDs - Assembly Language -
Linux - vi - GIMP - GTK+ - vi - HTML - Sound & Music - Electronics - Science

- List of SQL Tutorials from Database Central - Includes a very large collection of SQL
Tutorials plus specific Tutorials for a wide variety of DBMS's

An excellent collection of Oracle Specific Resources - Includes Oracle tips, tutorials,


add-ons, FAQs, Reviews, Books & Magazines, Discussion forums, and user groups -
from SearchDatabase.com

www.programmingtutorials.com - Includes a large collection of tutorials including:


Visual Basic - QBasic - C - C++ - CGI - JavaScript - Java - VRML - ADA - DELPHI -
SQL - PERL

www.findtutorials.com - tutorials search engine on the web, tutorials on any subject


listed in a searchable directory... And all of them are for free

A very popular/comprehensive SQL Tutorial

SQL Tutorials from AllTheWeb.com

Free Computer Training sites - Includes links to sites that offer courses such as:
MCSE - Novell - A+ Certification - Linux - Delphi - Visual Basic - WebMaster
Specialist - Java - Microsoft Office - Windows 95 - SQL

The Link Zone - Includes: Visual Basic - C - C++ - Java - JavaScript - SQL - CGI -
Active Server Pages - Web Development - Unix - ISDN - HTML

A Gentle Introduction to SQL

UnixTools's list of Useful Free & Shareware Tutorials. Includes: Unix, Systems
Administration, Perl, Java, SQL

Free Education on the Internet Free Education on everything including subjects such
as Accounting - Economics - Finance - Electronics - Construction - Engineering -
Computer Science - Mathematics - Science - Social Science - etc

Gary Beene's VISUAL BASIC WORLD This site contains over 250 URLs of Visual Basic
Information as well as a very nice on-line Visual Basic Tutorial
sqlwire.com - Excellent/Free Resources to the SQL Community

Buy your SQL Books at Fatbrain.com

www.trainingroup.com - you can find TONS of training resources at this site,


includuing: Java, Conferences, Jobs, Books, Unix, Sun, Tutorials, etc.

A practical Linux Portal - links to the most useful and practical Linux sites

www.CramSession.com is dedicated to the IT Professional interested in Certification -


Microsoft Certification - Novell - A+ Certification - Unix Certification - Cisco
Certification

Click here to continue your SQL/Oracle Training -- over 10 hours of FREE training
and Study Guides for Oracle's Certified Professional exams, as well as articles, tips,
utilities, etc.

Yahoo's Structured Query Language Club!

Java, C++, & other Tutorials including: Windows NT Administration - SQL - UNL
Library

Nerd World - Browse over 10,000 Nerd Sorted Categories

January 8, 2004
Installation Cookbooks: Installing Oracle Forms & Reports 6i
By Steve Callan

The Oracle9i installation guide included in last month's Installation Cookbook series established the concept of
applying an installation methodology to Oracle products. This cookbook article on installing Forms & Reports 6i
(on a Solaris system) illustrates the same methodology.

"But wait, I'm a DBA, not a software installer," so you say. True, you may be the DBA, but whom else to turn to
when it comes to anything having to do with Oracle? More often than not, the DBA owns everything related to
Oracle, whether or not the DBA has anything to do with that "other" Oracle stuff. Additionally, it is not uncommon
to find people working in DBA/developer positions, so shake off the narrow-minded view about what a DBA does
and expand your knowledge of Oracle's installation process.

The previous article included a suggested outline of the installation process. You will find that when it comes to
Oracle products, database or otherwise, the outline works just the same.

A general outline of installation steps

The seven steps shown below are the same steps from the Oracle9i installation cookbook.

1. Verify that hardware and software minimums are met or exceeded

2. Prepare or update the operating system

3. Create administrative/owner accounts and file structures

4. Reserve, specify, or designate disk space


5. Start the Oracle Universal Installer (OUI) and provide information as necessary

6. Perform post-installation tasks

7. Test your installation

The first four steps can be considered as pre-installation tasks, step five as THE installation task and steps six
and seven as post-installation tasks. Moreover, as I stated in the previous article, I like to include an introduction
along with a list of assumptions or standards, and a list of references from which the information was drawn.

As an added bonus, you can include a short troubleshooting section at the end of the installation guide. Including
this section begs the question of, "Why those particular problems and not others?" Part of that comes from
personal experience and observation. Experience, of course, comes from making mistakes, so that's part of the
answer, and the observation part may stem from having observed others (customers) make those mistakes or
face similar problems.

The next article in this series will discuss some additional planning considerations and ways to "dummy up" the
inventory location. This comes in handy when working with different versions of the Oracle Universal Installer.

Therefore, without further ado, here is a detailed, step-by-step installation cookbook for installing Oracle Forms &
Reports 6i on a Sun Solaris platform.

Installation Guide for Oracle Forms Developer and


Reports Developer - Release 6i for Sun SPARC
Solaris

Introduction
This document guides you through installing the server components of Oracle Forms & Reports for Sun SPARC Solaris, Release
6i, on the Solaris 8 operating system. The installation is performed using Oracle's Oracle Universal Installer (OUI), a GUI-based
tool similar to the OUI for Oracle9i and other Oracle products. This guide takes you through the pre-installation, installation, and
post-installation tasks referenced in the Oracle Forms Developer and Reports Developer Installation Guide dated March 2000
(Oracle Part No. A82817-01). Oracle Forms Server and Reports Server Installation Guide dated March 2000 (Oracle Part No.
A82818-01) can also be used.

The following conventions are used in this installation guide:

• "6i" refers to Oracle Forms Developer and Reports Developer Release 6i.
• oradev6 is the UNIX account owner of the 6i files.
• oradev6 belongs to two groups - oinstall and dba (dba is the primary group)
• The home directory created/used for oradev6 is /opt1/ora6i
• The ORACLE_HOME path used in examples is /opt1/ora6i/app/oracle/product/6.0
• ORACLE_HOME for oradev6 is separate from any other ORACLE_HOME
• ORACLE_HOME and the UNIX home directory for oradev6 is not the same thing. The home directory is where
oradev6's .cshrc file is located and is the working directory when oradev6 first logs in. ORACLE_HOME refers to the
directory path structure where the 6i program files are located.
• Text in courier, such as "cd $ORACLE_HOME" is text you explicitly type or enter, or identifies explicit values or names,
such as the UNIX user "root."
• <...> = anything between the angle brackets is user-supplied and varies from user to user. "#" represents the UNIX
prompt on your workstation.
• <mnt_pt> is a mount point where the start of the Oracle installation tree begins (e.g., /opt1/ora6i)
• All instructions to edit a file refer to using a text editor such as vi.
• A key post-installation task includes installing an Oracle patch for 6i. The instructions for doing this are also covered in
this guide. The 2.62GB disk space requirement includes the patch.
System Requirements

Solaris 8
128MB RAM
CPU that is SPARC-based
Swap space two to four times the size of physical RAM
Sun OpenWindows to display GUI tools
2.62GB of hard disk space
No Sun Motif patches are necessary for Solaris 8

Installation Cookbooks: Installing Oracle Forms & Reports 6i


By Steve Callan

Files edited or modified by the UNIX users root and


oradev6
The user oracle (or oracle9) may have been created for installation of Oracle9i. You can use the same Oracle user for both 9i
and 6i, but separating the users is a practical safeguard related to configuration issues and preventing the overwriting of important
files.

1. Create a user called oradev6 who is a member of the dba and oinstall groups. As root, edit your /etc/passwd file and
enter the following line:

oradev6:x:104:20002::/opt1/ora6i:/bin/csh

where:

• oradev6 is the name of the Oracle user


• x is related to a password value
• 104 is the userid of this UNIX account
• 20002 is the primary group ID to which oradev6 belongs
• /opt1/ora6i is the home directory (where .cshrc for oradev6 is located)
• /bin/csh is the default startup shell.

2. As root, edit the /etc/group file and add the user oradev6 to the list of users under the dba group. For example, the
entries for the dba and oinstall groups line could look like

dba::20002:oracle9,oradev6
oinstall::20001:oracle9,oradev6

3. When all changes have been made, execute pwconv as root at a UNIX prompt. Create the mount point and assign
ownership for oradev6 by entering the following commands:

mkdir -p /opt1/ora6i
chown oradev6:dba /opt1/ora6i

4. As root, edit root's .cshrc file and add the following environment variables:
setenv ORACLE_HOME <ex:
/opt1/ora6i/app/oracle/product/6.0>
setenv ORACLE_SID <name of a database>

The ORACLE_HOME environment variable should match the path you will be setting in oradev6's .cshrc file. The ORACLE_SID
value does not have to be the name of an actual database.

5. Login as oradev6. Check to see that your home directory is automatically set to /opt1/ora6i. Copy a generic .cshrc file
from /etc/skel by typing

cp /etc/skel/local.cshrc .cshrc

6. Using a text editor such as vi, edit the .cshrc file for oradev6. Modify the file so that the following settings and environment
variables are set, and source the file (source .cshrc) when finished with the editing.

umask 022
setenv DISPLAY <machinename>:0.0
setenv ORACLE_TERM sun5
setenv ORACLE_SID <database name>
setenv ORACLE_BASE /<mount point>/app/oracle
setenv ORACLE_HOME $ORACLE_BASE/product/6.0
setenv TNS_ADMIN <as stated in the previous step>
setenv LD_LIBRARY_PATH $ORACLE_HOME/lib: <img
src="/img/return.gif" width=14 height=9 border=0 alt="">

$ORACLE_HOME/network/jre11/lib/sparc/native_threads
setenv ORACLE_AUTOREG
$ORACLE_HOME/guicommon6/tk60/admin
setenv TK_PRINT_STATUS 'echo %n is valid'
setenv TK_PRINTER <enter printer name>
setenv PATH <should include $ORACLE_PATH,
/usr/ccs/bin,
/usr/bin, /etc, /usr/openwin,
/usr/local/bin and .>

Note: Place $ORACLE_HOME/lib at the beginning of LD_LIBRARY_PATH. Otherwise, the patch installation will fail.

You are now ready to install the CD-ROM and begin using the Oracle Universal Installer.

Performing the installation


Insert the CD-ROM. Solaris Volume Management should automatically mount the CD-ROM for you. Minimize the "File Manager -
oracle" window after the CD-ROM is mounted. Refer to the table below for actions to take in each window displayed by the Oracle
Universal Installer. As the UNIX user oradev6, start the installation by typing

/cdrom/oracle/orainst/orainst /m

You may encounter other windows during the installation process for a variety of reasons (improper system setup, lack of file
permissions, non-existent paths, etc.). The OUI windows are fairly self-explanatory as to what needs to be done to correct a
problem.
In the windows that appear below, take the action(s) indicated.

Window Name Action to take


Install Type Select Default Install. Click OK.
preamble.txt Click OK.
/cdrom/oracle/orainst/README.FIRST Click OK.
Installation Activity Choice Select Install, Upgrade, or De-Install Software (the
first button). Click OK.
Installation Options Select Install New Product - Do Not Create DB
Objects (second item in the list). Click OK.
Environment Variables Confirm the paths for ORACLE_BASE and
ORACLE_HOME. Click OK.
/<ORACLE_HOME path>/orainst/defaults Click OK.
Software Asset Manager Highlight/select the following in the "Products
available" list box on the left side of the window:

Oracle Forms Server and Forms Developer

Oracle Reports Server and Reports Developer

Single-click on each item. After BOTH have been


selected, Click Install.
X Libraries Verify the path for the files shown. Click OK.
Motif Library Verify the path for the file shown. Click OK.
GUI Choice Select Motif Bitmapped Interface. Click OK.
Component Selection De-select Forms Server for Web deployment and
select Motif Bitmapped Interface. Click OK.
Component Selection De-select Reports Multitier Server for Web
deployment and select Motif Bitmapped Interface.
(same title appears again) Click OK.
Oracle Progress bar appears. Wait for next window to
appear (>>20 minutes).
Installer Actions Completed Ignore instructions to run root.sh (you will do this
later). Click OK.
Software Asset Manager Installed components appear in the right pane. Click
Exit.
Confirmation Click Yes.

The command line in the UNIX window where you initiated orainst should show

Result: Success

Installation Cookbooks: Installing Oracle Forms & Reports 6i


By Steve Callan

Post-installation tasks
1. Forms and Reports executables need a tnsnames.ora file, or you can use the TNS_ADMIN environment variable to point to a
directory where the Oracle Net Services (or Net8) configuration files are located. If you already configured this file for an Oracle9i
installation, set TNS_ADMIN to the directory where the tnsnames.ora file for Oracle9i is located. Otherwise, Oracle products will
look for the tnsnames.ora file in the following order:

.tnsnames.ora in the user's home directory (note the dot before the file name)

$TNS_ADMIN (you can set this in the .cshrc file)

/var/opt/oracle

$ORACLE_HOME/network/admin (this is the default location)

2. As oradev6, edit the spoolcmd.sh file. Change the working directory to $ORACLE_HOME/reports60/admin/printer.

3. Beginning at line 36 in the file, where it says if test $3 = "delete" make the changes shown below (you are replacing
the "then" and "else" statements with what is shown below):

then
/usr/bin/lp d<printer name> n $4 $1
else
/usr/bin/lp d<printer name> n $4 $1

Without these changes, you will see error messages related to REP-3300/UI-9 and REP-1809 when trying to print reports from
forms. Oracle documentation refers to adding a printer definition in the
$ORACLE_HOME/guicommon6/tk60/admin/uiprint.txt file. This file does not need to be modified.

4. Log in as root and change your working directory to $ORACLE_HOME/orainst. Type root.sh at the UNIX prompt. If the
settings are correct, press Enter. Otherwise, exit and reset the variables to the appropriate values and run root.sh again.

5. Enter the pathname for the local bin directory (use /usr/bin) and press Enter.

6. Type Y to continue at the warning about ORACLE_HOME not matching the home directory for the Oracle user oradev6 (these
values do not have to match).

7. If your group ID is dba, press Enter at the line prompting for the UNIX group that has DBA permissions.

8. You can now eject/un-mount the CD-ROM.


Installing Patch 12 (Oracle patch
p2648892_600_SOLARIS.zip)
1. Log in as oradev6.

2. Insert the Forms & Reports 6i Patch 12 CD-ROM.

3. Navigate to the /cdrom/6i_patch12 directory.

4. Copy the patch file to the ORACLE_HOME directory

cp p* $ORACLE_HOME

5. Navigate to ORACLE_HOME. Unzip the patch file with

unzip p2648892_600_SOLARIS.zip

This step will create the patch directory under the ORACLE_HOME directory.

6. Navigate to the developer6i_patch12 directory.

7. As oradev6 at a UNIX prompt, start the patch installation process by executing

./patch_install.sh

If you want a log file of the installation, execute

./patch_install.sh | & tee patch_install_p12.log

8. As the patch is installed, you will see an Oracle installation window. No action on your part is required.

9. When the installation finishes, re-link the following:

cd $ORACLE_HOME/procbuilder60/lib
make -f ins_procbuilder.mk install

cd $ORACLE_HOME/forms60/lib
make -f ins_forms60w.mk install

cd $ORACLE_HOME/graphics60/lib
make -f ins_graphics60w.mk install

cd $ORACLE_HOME/reports60/lib
make -f ins_reports60w.mk install

This completes the patch installation process. You can delete the patch zip file to recover some disk space.

10. As the user oradev6, you can test the installation by typing f60runm at the UNIX command line and then see the "Forms
Runtime Options" window. If the "Forms Runtime Options" window does not appear, check the path statement in the .cshrc file
for oradev6. The path should include $ORACLE_HOME/bin.
January 22, 2004
The Trigger-Happy DBA - System Triggers
By Steve Callan

So far, the trigger-happy DBA series has dealt with triggers related to Data Manipulation Language (DML)
operations. As mentioned in the beginning of the series, another type of useful trigger is that of the system
trigger. System triggers can be delineated into two categories: those based on Data Definition Language (DDL)
statements, and those based upon database events. Use of system triggers can greatly expand a DBA's ability to
monitor database activity and events. Moreover, after having read this article, you'll be able to sharp shoot
someone who asks, "How many triggers does Oracle have?" Most people will seize upon the before/during/after
insert/update/delete on row/table easy-to-answer OCP test type of question (and answer), which is largely
correct where plain vanilla DML triggers are concerned. How would you count INSTEAD-OF triggers when it
comes to DML? So, how many other triggers does Oracle have or allow?

The syntax for creating a system trigger is very similar to the syntax used for creating DML triggers.

The number of system triggers available to the DBA is 11 under the short and simple plan. If you want the deluxe
version or variety, you can refer to the more than 20 system-defined event attributes shown in Chapter 16 of the
Oracle9i Application Developer's Guide. In this month's article, we will look at the 11 "simple" triggers related to
DDL and database events. Let's identify these 11 triggers before going further.

Event or DDL statement When allowed or applicable


STARTUP AFTER
SHUTDOWN BEFORE
SERVERERROR AFTER
LOGON AFTER
LOGOFF BEFORE

CREATE BEFORE and AFTER


DROP BEFORE and AFTER
ALTER BEFORE and AFTER

Some common sense is in order here. If you wait long enough and visit enough DBA-related question-and-answer
web sites, inevitably, and amusingly, you will see the question about, "I'm trying to create a trigger that does X
and Y before a user logs on - how do I do that?" That may be possible in some future version of Oracle - the
version that senses when you are about to log on? - but don't hold your breath waiting for that release date!

So, the BEFORE and AFTER timing part really matters for the system events shown above, and you have a bit
more flexibility with respect to the DDL statements. You will also notice there are no DURING's for the "when"
part, and perhaps not so obvious, there are no INSTEAD-OF triggers for system events. What about TRUNCATE
statements, you ask. TRUNCATE is a DDL statement, but unfortunately, Oracle does not capture this event (as
far as triggers are concerned).

Let's construct a simple auditing type of trigger that captures a user's logon information. With auditing in mind,
as with when to fire a DML type of trigger, timing matters. Maybe you have a sensitive database, one where you
need to capture a user's session information. It would be more appropriate to capture a user's access/logon to
the sensitive database immediately after that user logs on as opposed to capturing the session information before
logging off. Who says you have to log off gracefully? If a session is abnormally terminated, does a BEFORE
LOGOFF trigger fire?

As a side note, how can you obtain session information? One way is to use the SYS_CONTEXT function. Oracle's
SQL Reference manual lists 37 parameters you can use with SYS_CONTEXT to obtain session information. See
http://download-west.oracle.com/docs/cd/B10501_01/server.920/a96540/functions122a.htm#1038178 for more
information regarding this feature.

Here is our audit table:


SQL> CREATE TABLE session_info
2 (username VARCHAR2(30),
3 logon_date DATE,
4 session_id VARCHAR2(30),
5 ip_addr VARCHAR2(30),
6 hostname VARCHAR2(30),
7 auth_type VARCHAR2(30));

Table created.

Here is the code for the BEFORE LOGOFF trigger:

SQL> CREATE OR REPLACE TRIGGER trg_session_info


2 BEFORE LOGOFF
3 ON DATABASE
4 DECLARE
5 session_id VARCHAR2(30);
6 ip_addr VARCHAR2(30);
7 hostname VARCHAR2(30);
8 auth_type VARCHAR2(30);
9 BEGIN
10 SELECT sys_context ('USERENV', 'SESSIONID')
11 INTO session_id
12 FROM dual;
13
14 SELECT sys_context ('USERENV', 'IP_ADDRESS')
15 INTO ip_addr
16 FROM dual;
17
18 SELECT sys_context ('USERENV', 'HOST')
19 INTO hostname
20 FROM dual;
21
22 SELECT sys_context ('USERENV',
'AUTHENTICATION_TYPE')
23 INTO auth_type
24 FROM dual;
25
26 INSERT INTO session_info VALUES
27 (user, sysdate, session_id, ip_addr, hostname,
auth_type);
28 END;
29 /

Trigger created.

Let's connect as Scott then return as someone else:


SQL> select * from session_info;

USERNAME LOGON_DAT SESSION_ID IP_ADDR HOSTNAME


AUTH_TYPE
-------- --------- ---------- ------- -------------------
---------
STECAL 12-JAN-04 577 WORKGROUP\D2JW5027
DATABASE
SCOTT 12-JAN-04 578 WORKGROUP\D2JW5027
DATABASE

Looks like the trigger worked, twice in fact, because when I left to connect as Scott, the newly created
trg_session_info trigger captured my information as well.

Let's go back as Scott, and have Scott's session abnormally terminated. Does the BEFORE LOGOFF trigger
capture Scott's session information?

SQL> select * from session_info;

USERNAME LOGON_DAT SESSION_ID IP_ADDR HOSTNAME


AUTH_TYPE
-------- --------- ---------- -------- -------------------
----------
STECAL 12-JAN-04 577 WORKGROUP\D2JW5027
DATABASE
SCOTT 12-JAN-04 578 WORKGROUP\D2JW5027
DATABASE
STECAL 12-JAN-04 579 WORKGROUP\D2JW5027
DATABASE

The answer is no. In this particular case, Scott's session was terminated by clicking on the Windows close button.
Assuming Scott is a malicious user, he could have viewed salaries and other personal information with some
degree of obscurity. Use of an AFTER LOGON trigger would have immediately captured his session information. Of
course, this trigger, in of and by itself, is not sufficient to fully protect access to sensitive information, but it is a
means of letting potentially malicious (or snooping) users know they are being watched or monitored. Locks on
doors help keep honest people honest, so the saying goes. Same idea here.

Changing the trigger to AFTER LOGON yields the following results with Scott logging on, and his session being
terminated:

SQL> select * from session_info;

USERNAME LOGON_DAT SESSION_ID IP_ADDR HOSTNAME


AUTH_TYPE
-------- --------- ---------- -------- -------------------
----------
STECAL 12-JAN-04 577 WORKGROUP\D2JW5027
DATABASE
SCOTT 12-JAN-04 578 WORKGROUP\D2JW5027
DATABASE
STECAL 12-JAN-04 579 WORKGROUP\D2JW5027
DATABASE
STECAL 12-JAN-04 581 WORKGROUP\D2JW5027
DATABASE
STECAL 12-JAN-04 581 WORKGROUP\D2JW5027
DATABASE
SCOTT 12-JAN-04 582 WORKGROUP\D2JW5027
DATABASE
SCOTT 12-JAN-04 582 WORKGROUP\D2JW5027
DATABASE

7 rows selected.

Look at the last four rows - aside from the date (the times would be different per user), it looks like two rows per
user. This is an example of two things. First, do not forget to clean up after yourself (remove unnecessary
triggers), and second, maybe you will want to capture the AFTER LOGON and BEFORE LOGOFF times (of course,
it would be hard to change machines in the middle of a session!).

Another use of a system trigger may help you (as the DBA) identify users in need of some help when it comes to
forming SQL queries. The SERVERERROR system event, when combined with the CURRENT_SQL parameter in the
SYS_CONTEXT function, can flag or identify users who frequently make mistakes. The CURRENT_SQL parameter
"returns the current SQL that triggered the fine-grained auditing event. You can specify this attribute only inside
the event handler for the Fine-Grained Auditing feature." (From the SQL Reference manual) Even without FGAC,
you can set up a simple trigger-audit table relationship as follows:

SQL> CREATE TABLE error_info


2 (username VARCHAR2(30),
3 logon_date DATE,
4 session_id VARCHAR2(30),
5 sql_statement VARCHAR2(64));

Table created.

SQL> CREATE OR REPLACE TRIGGER trg_server_error


2 AFTER SERVERERROR
3 ON DATABASE
4 DECLARE
5 session_id VARCHAR2(30);
6 sql_statement VARCHAR2(64);
7 BEGIN
8 SELECT sys_context ('USERENV', 'SESSIONID')
9 INTO session_id
10 FROM dual;
11
12 SELECT sys_context ('USERENV', 'CURRENT_SQL')
13 INTO sql_statement
14 FROM dual;
15
16 INSERT INTO error_info VALUES
17 (user, sysdate, session_id, sql_statement);
18 END;
19 /

Trigger created.

SQL> select * from some_table;


select * from some_table
*
ERROR at line 1:
ORA-00942: table or view does not exist

SQL> select * from error_info;

USERNAME LOGON_DAT SESSION_ID SQL_STATEMENT


-------- --------- ---------- ---------------
STECAL 12-JAN-04 583

There are a great many things you can do with triggers, whether they are based on DML statements or system
events. As a developer or DBA (or both), there is no such thing as having too many tricks up your sleeve. In
terms of job or role separation, you can think of the DML triggers as being in the purview of the developer, and
the system event triggers being in the DBA's, but a good DBA should possess some decent programming skills of
his or her own, and that's where knowing how to avoid problems with DML triggers comes into play. Being and
staying well-informed on the use (and limitations) of triggers will make you a trigger-happy DBA.

December 26, 2003


The Trigger-Happy DBA - Part 2
By Steve Callan

Here is a problem many developers run into: ORA-04091 table owner.table_name is mutating,
trigger/function may not see it. In many cases, the cause of this error is due to code within a trigger that
looks at or touches the data within the table the trigger is being called or invoked from. The "look and touch"
refers to using select (the look) and DML statements (the touch). In other words, you need to take your DML
elsewhere. The reason Oracle raises this error is related to one of Oracle's primary strengths as a relational
database management system. The particular strength in question here is that of having a read consistent view
of data.

It is worthwhile to note that this error occurs not only in the "pure" database development environment (CREATE
or REPLACE trigger trigger_name... in a script or SQL*Plus session), but also in the Oracle tools type of
development environment such as Oracle Forms. An Oracle form relies on triggers for a great many things,
ranging from capturing user interaction with the form (when-button-pressed) to performing transaction
processing (on-commit). A forms trigger may do nothing more than change the focus to a new item or show a
new canvas. What a form trigger can do, and has in common with the "pure" development type of trigger, is
generate the ORA-04091 mutating table error.

One common solution to avoid the mutating table error is to use three other triggers. Tom Kyte, author of Expert
One-on-One Oracle and Effective Oracle by Design, two of the very best books on Oracle, provides an excellent
example of this technique at http://osi.oracle.com/~tkyte/Mutate/index.html (part of the Ask Tom series at
www.oracle.com). Another solution relies on using an INSTEAD-OF trigger instead of the trigger you meant to
use when you received the error. Another solution is actually more of a preventative measure, namely, using the
right type of trigger for the task at hand.

Here is a simple example of where a trigger can generate the mutating table error. The hapless Oracle user
named Scott wants to generate a statement telling him how many employees are left after an employee record is
deleted. This code for this example comes from Oracle's Application Developer's Guide.
Here is the data before the delete statement is issued:

SQL> select empno, ename


2 from emp_tab; -- a copy of the scott.emp table

EMPNO ENAME
---------- ----------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD
7934 MILLER

14 rows selected.

Here is the trigger code:

CREATE OR REPLACE TRIGGER Emp_count


AFTER DELETE ON Emp_tab
FOR EACH ROW
DECLARE
n INTEGER;
BEGIN
SELECT COUNT(*) INTO n FROM Emp_tab;
DBMS_OUTPUT.PUT_LINE(' There are now '|| n ||
' employees.');
END;

In a SQL*Plus session, here is what the coding looks like (same as above, but without the feedback statement):

SQL> CREATE OR REPLACE TRIGGER Emp_count


2 AFTER DELETE ON Emp_tab
3 FOR EACH ROW
4 DECLARE
5 n INTEGER;
6 BEGIN
7 SELECT COUNT(*) INTO n FROM Emp_tab;
8 DBMS_OUTPUT.PUT_LINE(' There are now '|| n ||
9 ' employees.');
10 END;
11 /

Trigger created.

Any hint so far that there may be a problem with the trigger? Not with the "Trigger created" feedback Oracle
provides. Looks like no errors and that the trigger should fire when the triggering condition (after delete on the
Emp_tab table) occurs.

Here is a DML statement that will trigger the ORA-04091 error:

SQL> DELETE FROM Emp_tab WHERE Empno = 7499;


DELETE FROM Emp_tab WHERE Empno = 7499
*
ERROR at line 1:
ORA-04091: table SCOTT.EMP_TAB is mutating,
trigger/function may not see it
ORA-06512: at "SCOTT.EMP_COUNT", line 4
ORA-04088: error during execution of trigger
'SCOTT.EMP_COUNT'

Let's modify the trigger code just a bit, and remove the FOR EACH ROW clause. If 'we are not "doing" each row,
the trigger becomes a statement-level trigger.

SQL> CREATE OR REPLACE TRIGGER Emp_count


2 AFTER DELETE ON Emp_tab
3 -- FOR EACH ROW
4 DECLARE
5 n INTEGER;
6 BEGIN
7 SELECT COUNT(*) INTO n FROM Emp_tab;
8 DBMS_OUTPUT.PUT_LINE(' There are now '|| n ||
9 ' employees.');
10 END;
11 /

Trigger created.

SQL> DELETE FROM Emp_tab WHERE Empno = 7499;


There are now 13 employees.

1 row deleted.

Note that the trigger successfully fired with this one modification. But was it really a modification or just a better
design and use of a trigger? As stated in the previous article, triggers can act on each row or act at the statement
level. In Scott's case, what he really needed was a statement-level trigger, not a row-level trigger. Mastering this
concept alone - knowing whether to base the trigger on the statement or on rows - can prevent many instances
of the mutating trigger error.

Suppose Scott has a table based on two other tables:


SQL> create table trigger_example_table
2 as
3 select empno, ename, a.deptno, dname, alter_date
4 from emp a, dept2 b
5 where a.deptno = b.deptno;

Table created.
SQL> select * from trigger_example_table;

EMPNO ENAME DEPTNO DNAME ALTER_DAT


---------- ---------- ---------- -------------- ---------
7782 CLARK 10 ACCOUNTING 09-DEC-03
7839 KING 10 ACCOUNTING 09-DEC-03
7934 MILLER 10 ACCOUNTING 09-DEC-03
7369 SMITH 20 RESEARCH 09-DEC-03
7876 ADAMS 20 RESEARCH 09-DEC-03
7902 FORD 20 RESEARCH 09-DEC-03
7788 SCOTT 20 RESEARCH 09-DEC-03
7566 JONES 20 RESEARCH 09-DEC-03
7499 ALLEN2 30 SALES 09-DEC-03
7698 BLAKE 30 SALES 09-DEC-03
7654 MARTIN 30 SALES 09-DEC-03
7900 JAMES 30 SALES 09-DEC-03
7844 TURNER 30 SALES 09-DEC-03
7521 WARD 30 SALES 09-DEC-03

14 rows selected.

And, for whatever reason, whenever an update is made on someone, the ALTER_DATE column is updated to
reflect the current date:

SQL> create or replace trigger


2 trig_trigger_example_table
3 after update on trigger_example_table
4 for each row
5 begin
6 update trigger_example_table
7 set alter_date = sysdate;
8 end;
9 /

Trigger created.

Issuing an update statement - does the trigger allow the ALTER_DATE to be updated?

SQL> update trigger_example_table


2 set ename = 'ALLEN_TRIG'
3 where empno = 7499;
update trigger_example_table
*
ERROR at line 1:
ORA-04091: table SCOTT.TRIGGER_EXAMPLE_TABLE is mutating,
trigger/function may not see it
ORA-06512: at "SCOTT.TRIG_TRIGGER_EXAMPLE_TABLE", line 2
ORA-04088: error during execution of trigger
'SCOTT.TRIG_TRIGGER_EXAMPLE_TABLE'

No, because it is the same problem as before (touching a table that is being updated). This
example just reinforces the idea that the mutating trigger error still occurs on a table based on
other tables (which is still just a table as far as Oracle is concerned). Another name for the
concept of presenting data based on a combination (i.e., a join) other tables? Straight out of
the Concepts Guide: "A view is a tailored presentation of the data contained in one or more
tables or other views." The Application Developer's Guide (yes, this is a plug for Oracle's
documentation) presents a good example of how to construct an INSTEAD-OF trigger. You can
copy the sample code shown in the guide and experiment with using various DML statements
against the view.

Perhaps the greatest strength or utility of an INSTEAD-OF trigger is its ability to update what
would normally appear to be non-updateable views. Simple views (pretty much based on a
single base table) generally are inherently updateable via DML statements issued against the
view. However, when a view becomes more complex (multiple tables or views used in various
join conditions to create the new single view), there is a good chance that many columns, as
referenced by the view, lose their "updateable-ness." So, being the data dictionary view/table
name trivia wizard that you are, you know to query the XXX_UPDATABLE_COLUMNS views,
substituting USER, ALL or DBA for XXX as applicable.

There are exceptions to this rule about views being inherently updateable. The exceptions (or
restrictions) include views that use aggregate functions; group functions; use of the DISTINCT
keyword; use of GROUP BY, CONNECT BY or START WITH clauses; and use of some joins. In
many cases, use of the INSTEAD-OF trigger feature allows you to work around these
restrictions.

INSTEAD-OF triggers are also useful for Forms developers because forms are commonly based
on views. The INSTEAD-OF trigger, being a "real" trigger, and not a true form trigger, is
stored on the server. This may require coordination between the DBA and developer, which, of
course, always happens in complete harmony (NOT! - but that is a separate issue).

November 26, 2003


The Trigger-Happy DBA
By Steve Callan

Who is the trigger-happy DBA? No, I do not mean the DBA who is pulling his hair out over a
developer's mistake ("I truncated all my tables. Why can't you simply do a rollback for me?"),
or the DBA fighting management's insistence on using export as the primary backup and
recovery strategy ("Just do one every hour if you're so worried about losing data."). The
trigger-happy DBA I have in mind is the DBA who has a good understanding of the use of
triggers and what they can and cannot do for him.

Triggers can be an extremely useful tool to manage and control data, and to enforce business
rules and logic. Some may argue that this usefulness can be managed just as well, if not
better, by the use of other functions such as procedures, constraints, and so on. And, no
doubt, there are some aspects of triggers that make using them unappealing. But on the
whole, triggers can be an effective tool in several areas. Some of the more commonly used
areas include auditing, enforcement of business rules, and behind the scenes DML-related
operations.

A quick review of what triggers are is in order. Oracle defines triggers as "procedures that are
stored in the database and implicitly run, or fired, when something happens." [Page 15-1,
Application Developer's Guide] What is the "something" that happens to make a trigger fire?
There are 12 "somethings" that can cause triggers to fire: Before/After during
Insert/Update/Delete on a Row/Table (which leads to the 2x3x2=12 types). To be more
precise in describing the number of triggers, the 12 just shown are referred to as DML
triggers, because insert, update and delete are data manipulation operations.

The DML triggers are the best known, but there are more available since Oracle8i was
released. The additional triggers, or types of triggers, are instead-of triggers, database event
triggers, and DDL triggers. Instead-of triggers are used when views are involved, and are a
good way of avoiding the mutating table trigger-related error. Database event triggers can be
divided into two categories: on the database, where triggers occur for all users, and on a
schema, where triggers occur for that user. Data definition types of triggers can be used to
capture events related to DDL statements such as create, drop, and alter.

The remainder of this article will focus on DML triggers, and later articles will cover the
Oracle8i-era newer ones.

So what are some design issues when considering the use of triggers? When considering which
type of trigger to use in a DML operation, you want to avoid having a hair trigger, that is,
firing a trigger when it is not the appropriate time to fire it. Conversely, you do not want the
correct trigger to fire late. Suppose you have a banking application with a trigger that checks
to see if the DML operation is occurring after business hours (assuming that it should only be
performed during business hours). The DML operation involves updating a million records. It
does not make sense to let Oracle perform the update, only to have it canceled or rolled back
because you used an AFTER trigger instead of a BEFORE trigger to check the time of day. The
general rule here would be to check or enforce the business logic BEFORE using database
resources. In this example, an AFTER trigger can be used to record what happened.

Using the banking example again (during business hours), say you want to pay interest to
customers with a balance over a certain amount. Which would be appropriate, given that your
user is trying to update data: a row-level or table-level (also referred to as statement-level)
trigger? Suppose you said "table-level," because you are trying to update a table. The DML
statement even suggests "table" as the answer because of how the DML statement is written:

SQL> update table_name set ...

Well, you'd be wrong! Only row-level triggers have access to data in the rows. Anything that
affects the actual data within a row requires a row-level trigger. A statement-level trigger
would be appropriate to record, for example, the fact that the update statement completed,
what time it completed, or the name of the user who performed the operation.

So far, the banking example has only considered an update. Suppose you wanted to use
triggers for insert and delete DML statements. Do you have to write separate triggers for each
type of DML statement? You could, but you do not have to. Your trigger code could have a
structure like the one shown below.

CREATE OR REPLACE TRIGGER your_trigger_name


BEFORE INSERT OR UPDATE OR DELETE ON YOUR_TABLE_NAME
FOR EACH ROW
BEGIN
IF INSERTING THEN...
--do whatever
END IF;
IF UPDATING THEN ...
--do something else
END IF;
IF DELETING THEN ...
--do something else
END IF;
END;

So far, we have seen what we can do with triggers. That begs the question of "Are there any
restrictions on using triggers?" Of course there are! This is Oracle, remember? There are
always some strings attached to functionality. For the most part, the restrictions are pretty
minor, and some are transparent to most people.

Oracle places a size limit of 32KB on a trigger statement. How "big" is 32KB of data? Up to the
question mark, this article used about 25KB, using around 870 words and over 4,000
characters, just to give you a rough idea of how much code you can write under 32KB. Is
there a way around the 32KB limit? Yes, you can call external procedures.

If you do not use LONG or LONG RAW data types, any restrictions concerning these are
transparent to you. Several other restrictions are listed in the Oracle9i Application Developer's
Guide. One interesting restriction has to do with the order in which triggers are fired.

The code snippet shown earlier contained each type of DML statement, and if you are using
more than one than one type of trigger, Oracle fires all triggers of a type, then all triggers of
another type, and so on. You have no control over which type is fired first, and whichever type
is fired after the first type will see any changes made by the first type, and that cascades
down (third type sees changes made by the second type, and so on).

Finally, the mutating table error, ORA-04091 table owner.table_name is mutating,


trigger/function may not see it has made DBAs trigger-happy in ways we would rather
they not be exposed to. Next month's article starts with the use of instead-of triggers, which
are quite handy in getting around the mutating table error (which will also be explained in
more detail).

November 13, 2003


Connecting with Oracle: Finding and Setting a Configuration File Location
By Steve Callan

Last month's "Connecting with Oracle" article dealt with properly configuring the tnsnames.ora
file. Using a statistics-related analogy for a minute, you can have Type I and Type II errors
with respect to your tnsnames.ora file. You can have a "good" tnsnames.ora file, but "reject"
its use due to not knowing where it is. You can have a "bad" tnsnames.ora file in a location
you know about, and try to "accept" its use. Both situations are bad, and fortunately, each is
easy to correct once armed with some knowledge about Oracle Net Services.

This article deals with knowing how to set a location for your tnsnames.ora file and where to
look for that location (having a "good" file and "accepting" its use because you know where it
is or how to find it). The ubiquitous Oracle user name Scott just received a promotion: he is a
newly anointed junior DBA who must set up his company's 100 computers to use Oracle. Scott
knows he has a good tnsnames.ora file, and he wants to use it as a template for everyone
else.

One huge misconception about using Oracle products that occurs among other-than-RDBMS
users (novice Forms developers in particular) has to do with the proper configuration of the
non-RDBMS Oracle product. This is probably the number one problem or misconception you
can easily find on numerous web sites: "I can connect to my database when using SQL*Plus. I
just installed Forms & Reports and my forms can't connect to the database. What gives?"

Well, the guiding principle here is that pretty much every Oracle product, database or
otherwise, has its own set of "Oracle network" configuration files. Each Oracle product has its
own ORACLE_HOME. Within those ORACLE_HOME locations is a directory named network
(net80 on Windows for some older products), and within the network directory, you will find a
directory named admin. This is where Oracle installs the Oracle Net configuration files,
including tnsnames.ora. Like many other things in Oracle, if Oracle puts it there, Oracle
expects to find it there, unless you happen to intervene. In this case, intervention can be a
good thing.

Where does Oracle expect to find the tnsnames.ora file? The table below shows some of the
possible locations.

UNIX platforms Windows platforms


ORACLE_HOME\network\admin ORACLE_HOME/network/admin
/var/opt/oracle No counterpart
A .tnsnames file in your home directory No counterpart

(this is not documented very well and is


infrequently used)
In the directory specified by the In the directory specified by the
TNS_ADMIN environment variable TNS_ADMIN environment variable or
registry setting

Coming back to Scott and his 100 Oracle users, some of whom use client version software,
some of whom are Forms developers, some of whom use UNIX boxes and some who use PCs,
Scott has two options to get everyone on the same sheet of music (using a properly
configured tnsnames.ora file). Probably the least efficient solution calls for Scott to go to each
and every machine and copy the tnsnames.ora file onto the machine in each ORACLE_HOME
location. That way, he knows everyone is on the same sheet of music. But what happens when
a database is added or deleted, or a host changes? Go back to each machine and copy or edit
the file? Send out a company email telling users how to edit the file? "Scott, this is Trish. I'm
doing WHAT with that tns-whatever file?"

Fortunately, Scott has a simple solution at hand. It may require a bit of work at first, but after
the system is in place, changes are extremely easy to implement. Scott's solution is to use the
TNS_ADMIN variable. The TNS_ADMIN environment variable points to a directory on the file
system. The idea here is to set the variable to a shared network drive or directory, whether it
is UNIX or Windows based. The setting can be an absolute path, such as F:\shared\oracle
(where F:\ is a mapped network drive), /usr/local/shared/oracle (where /usr/local/shared is
mounted to a remote server), or a path that uses UNC naming (where you see the "\\" in front
of a server or machine name) such as \\server2\share\oracle. You can find more information
about UNC naming in books about Windows 2000 administration (like the MCSE 70-210 exam
Microsoft Press book or search the web;
http://compnetworking.about.com/library/glossary/bldef-unc.htm is one such reference).
When Scott needs to add a new database (service identifier, etc.) for employees to use, or
change a host name, all he needs to do is edit one file: the tnsnames.ora file found in the
directory specified by the TNS_ADMIN variable. The change is immediately available to
everyone. UNIX users can place the environment variable in logon or resource files (for
example, C-shell users can use "setenv TNS_ADMIN /some/location" in the .cshrc file; other
shells can have it exported, and so on). Where do Windows users place or set the variable?

I mentioned the "R" word earlier, but only because I wanted to be technically correct. Oracle's
documentation even mentions placing the TNS_ADMIN variable in the "R" word location. But
really, what possible reason would you want to use the "Registry" for something like that?
There is no need to; and, in fact, you can find other documents from Oracle that discourage
the use of directly editing the registry. Which, by the way, is good advice, and it happens to
match Microsoft's admonition about directly editing the registry. So, the answer for Windows
users: set TNS_ADMIN as an environment variable, either at the system or user level.

Suppose you have a user who uses SQL*Plus and Form Builder from Forms 6i. Each product
creates its own set of configuration files in their respective ORACLE_HOME-network-admin
directories (substitute / or \ for the hyphen, depending on the platform). Simply "direct" both
Oracle products to use the tnsnames.ora file found in the directory specified by the
TNS_ADMIN environment variable. The solution to the "What gives?" question posted on
numerous web sites is that simple.

In closing, the other configuration file frequently used in conjunction with the tnsnames.ora
file (sqlnet.ora) can also be placed in the same directory specified by the TNS_ADMIN
environment variable. Whether you choose to use sqlnet.ora is a separate issue; there is no
general absolute requirement to use it, but there are many cases where it is useful and
necessary.

As a DBA, you can save time for more important matters ("What was that web site about
awful plastic surgery, including some pictures?") by striving for efficiency in areas that are
easy to manage. For the most part, Oracle networking is very easy to manage, and as pointed
out in the Scott scenario, efficiency is essential.

SQL Quick Reference


SQL Statement Syntax
AND / OR SELECT column_name(s)
FROM table_name
WHERE condition
AND|OR condition
ALTER TABLE ALTER TABLE table_name
ADD column_name datatype

or

ALTER TABLE table_name


DROP COLUMN column_name
AS (alias) SELECT column_name AS column_alias
FROM table_name
or

SELECT column_name
FROM table_name AS table_alias
BETWEEN SELECT column_name(s)
FROM table_name
WHERE column_name
BETWEEN value1 AND value2
CREATE DATABASE CREATE DATABASE database_name
CREATE TABLE CREATE TABLE table_name
(
column_name1 data_type,
column_name2 data_type,
column_name2 data_type,
...
)
CREATE INDEX CREATE INDEX index_name
ON table_name (column_name)

or

CREATE UNIQUE INDEX index_name


ON table_name (column_name)
CREATE VIEW CREATE VIEW view_name AS
SELECT column_name(s)
FROM table_name
WHERE condition
DELETE DELETE FROM table_name
WHERE some_column=some_value

or

DELETE FROM table_name


(Note: Deletes the entire table!!)

DELETE * FROM table_name


(Note: Deletes the entire table!!)
DROP DATABASE DROP DATABASE database_name
DROP INDEX DROP INDEX table_name.index_name (SQL Server)
DROP INDEX index_name ON table_name (MS Access)
DROP INDEX index_name (DB2/Oracle)
ALTER TABLE table_name
DROP INDEX index_name (MySQL)
DROP TABLE DROP TABLE table_name
GROUP BY SELECT column_name, aggregate_function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name
HAVING SELECT column_name, aggregate_function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name
HAVING aggregate_function(column_name) operator value
IN SELECT column_name(s)
FROM table_name
WHERE column_name
IN (value1,value2,..)
INSERT INTO INSERT INTO table_name
VALUES (value1, value2, value3,....)

or

INSERT INTO table_name


(column1, column2, column3,...)
VALUES (value1, value2, value3,....)
INNER JOIN SELECT column_name(s)
FROM table_name1
INNER JOIN table_name2
ON table_name1.column_name=table_name2.column_name
LEFT JOIN SELECT column_name(s)
FROM table_name1
LEFT JOIN table_name2
ON table_name1.column_name=table_name2.column_name
RIGHT JOIN SELECT column_name(s)
FROM table_name1
RIGHT JOIN table_name2
ON table_name1.column_name=table_name2.column_name
FULL JOIN SELECT column_name(s)
FROM table_name1
FULL JOIN table_name2
ON table_name1.column_name=table_name2.column_name
LIKE SELECT column_name(s)
FROM table_name
WHERE column_name LIKE pattern
ORDER BY SELECT column_name(s)
FROM table_name
ORDER BY column_name [ASC|DESC]
SELECT SELECT column_name(s)
FROM table_name
SELECT * SELECT *
FROM table_name
SELECT DISTINCT SELECT DISTINCT column_name(s)
FROM table_name
SELECT INTO SELECT *
INTO new_table_name [IN externaldatabase]
FROM old_table_name

or

SELECT column_name(s)
INTO new_table_name [IN externaldatabase]
FROM old_table_name
SELECT TOP SELECT TOP number|percent column_name(s)
FROM table_name
TRUNCATE TABLE TRUNCATE TABLE table_name
UNION SELECT column_name(s) FROM table_name1
UNION
SELECT column_name(s) FROM table_name2
UNION ALL SELECT column_name(s) FROM table_name1
UNION ALL
SELECT column_name(s) FROM table_name2
UPDATE UPDATE table_name
SET column1=value, column2=value,...
WHERE some_column=some_value
WHERE SELECT column_name(s)
FROM table_name
WHERE column_name operator value

Anda mungkin juga menyukai