This document was uploaded by user and they confirmed that they have the permission to share
it. If you are author or own the copyright of this book, please report to us by using this DMCA
report form. Report DMCA
Overview You will be using the Oracle database system to implement your PDA (Personal Database Application) this quarter. Important: As soon as your Oracle account is set up, you should log in to change the initial password.
Logging In to Oracle You should be logged onto one of the Leland Systems Sun Solaris machines. These machines include elaine, saga, myth, fable, and tree. Before using Oracle, execute the following line in your shell to set up the correct environment variables: source /afs/ir/class/cs145/all.env
You may wish to put this line in your shell initialization file instead (for example, .cshrc). Now, you can log in to Oracle by typing: sqlplus
Here, sqlplus is Oracle's generic SQL interface. refers to your leland login. You will be prompted for your password. This password is initially changemesoon and must be changed as soon as possible. For security reasons, we suggest that you not use your regular leland password, because as we shall see there are opportunities for this password to become visible under certain circumstances. After you enter the correct password, you should receive the prompt
SQL>
Changing Your Password In response to the SQL> prompt, type
ALTER USER IDENTIFIED BY ; where is again your leland login, and is
the password you would like to use in the future. This command, like all other SQL commands, should be terminated with a semicolon. Note that SQL is completely case-insensitive. Once you are in sqlplus, you can use capitals or not in keywords like ALTER; Even your password is case insensitive. We tend to capitalize keywords and not other things.
Creating a Table In sqlplus we can execute any SQL command. One simple type of command creates a table (relation). The form is CREATE TABLE ( <list of attributes and their types> );
You may enter text on one line or on several lines. If your command runs over several lines, you will be prompted with line numbers until you type the semicolon that ends any command. (Warning: An empty line terminates the command but does not execute it; see Editing Commands in the Buffer.) An example table-creation command is: CREATE TABLE test ( i int, s char(10) );
This command creates a table named test with two attributes. The first, named i, is an integer, and the second, named s, is a character string of length (up to) 10.
Creating a Table With a Primary Key To create a table that declares attribute a to be a primary key:
CREATE TABLE (..., a PRIMARY KEY, b, ...);
To create a table that declares the set of attributes (a,b,c) to be a primary key: CREATE TABLE (, PRIMARY KEY (a,b,c));
Inserting Tuples Having created a table, we can insert tuples into it. The simplest way to insert is with the INSERT command: For
INSERT INTO VALUES( <list of values for attributes, in order> ); instance, we can insert the tuple (10, 'foobar') into relation test by INSERT INTO test VALUES(10, 'foobar');
Getting the Value of a Relation
We can see the tuples in a relation with the command: SELECT * FROM ;
For instance, after the above create and insert statements, the command SELECT * FROM test;
produces the result
I S ---------- ---------10 foobar
Getting Rid of Your Tables To remove a table from your database, execute DROP TABLE ;
We suggest you execute
DROP TABLE test;
after trying out this sequence of commands to avoid leaving a lot of garbage around that will be still there the next time you use the Oracle system.
Getting Information About Your Database The system keeps information about your own database in certain system tables. The most important for now is USER_TABLES. You can recall the names of your tables by issuing the query: SELECT TABLE_NAME FROM USER_TABLES;
More information about your tables is available from USER_TABLES. To see all the attributes of USER_TABLES, try: SELECT * FROM USER_TABLES;
It is also possible to recall the attributes of a table once you know its name. Issue the command: DESCRIBE ;
to learn about the attributes of relation .
Quitting sqlplus To leave sqlplus, type quit;
in response to the SQL> prompt.
Executing SQL From a File Instead of executing SQL commands typed at a terminal, it is often more convenient to type the SQL command(s) into a file and cause the file to be executed. To run the file foo.sql, type: @foo sqlplus assumes by default the file extension ".sql" if there is no extension. So you could have entered @foo.sql at the SQL> prompt, but if you wanted to execute the file bar.txt, you would have to enter @bar.txt.
You can also run a file at connection by using a special form on the Unix command line. The form of the command is: sqlplus / @
For instance, if user sally, whose password is etaoinshrdlu, wishes to execute the file foo.sql, then she would say: sqlplus sally/etaoinshrdlu @foo Notice that this mode presents a risk that sally's
password will be discovered, so it should be
used carefully. NOTE: If you are getting an error of the form "Input truncated to 2 characters" when you try to run your file, try putting an empty line at the bottom of your .sql file. This seems to make the error go away.
Editing Commands in the Buffer If you end a command without a semicolon, but with an empty new line, the command goes into a buffer. You may execute the command in the buffer by either the command RUN or a single slash (/). You may also edit the command in the buffer before you execute it. Here are some useful editing commands. They are shown in upper case but may be either upper or lower. LIST
n LIST m n LIST
INPUT CHANGE /old/new APPEND text DEL
lists the command buffer, and makes the last line in the buffer the "current" line prints line n of the command buffer, and makes line n the current line prints lines m through n, and makes line n the current line enters a mode that allows you to input text following the current line; you must terminate the sequence of new lines with a pair of "returns" replaces the text "old" by "new" in the current line appends "text" to the end of the current line deletes the current line
All of these commands may be executed by entering the first letter or any other prefix of the command except for the DEL command. An alternative is to edit the file where your SQL is kept directly from sqlplus. If you say the say
edit foo.sql file foo.sql will be
passed to an editor of your choice. The default is vi. However, you may
DEFINE _EDITOR = "emacs"
if you prefer to use the emacs editor; other editor choices may be called for in the analogous way. In fact, if you would like to make emacs your default editor, there is a login file that you may create in the directory from which you call sqlplus. Put in the file called login.sql the above editor-defining command, or any other commands you would like executed every time you call sqlplus.
Recording Your Session There are several methods for creating a typescript to turn in for your programming assignments. The most primitive way is to cut and paste your terminal output and save it in a file (if you have
windowing capabilities). Another method is to use the Unix command script to record the terminal interaction. The script command records everything printed on your screen. The syntax for the command is script [ -a ] [ filename ]
The record is written to filename. If no file name is given, the record is saved in the file typescript. The -a option allows you to append the session record to filename, rather than overwrite it. To end the recording, type exit
For more information on how to run the script command, check out its man page. sqlplus
provides the command spool to save query results to a file. At the SQL> prompt, you
say: spool foo; a file called foo.lst
and will appear in your current directory and will record all user input and system output, until you exit sqlplus or type: spool off;
Note that if the file foo.lst existed previously, it will be overwritten, not appended. Finally, if you use Emacs, you can simply run sqlplus in a shell buffer and save the buffer to a file. To prevent your Oracle password from being echoed in the Emacs buffer, add the following lines to your .emacs file: (setq-default comint-output-filter-functions '(comint-watch-for-password-prompt)) (setq comint-password-prompt-regexp "\\(\\([Oo]ld \\|[Nn]ew \\|^\\)[Pp]assword\\|Enter password\\):\\s *\\'")
Help Facilities SQL*Plus provides internal help facilities for SQL*Plus commands. No help is provided for standard SQL keywords. To see a list of commands for which help is available, type help topics or help index in response to the SQL> prompt. To then look up help for a particular keyword (listed in the index), type help followed by the keyword. For example, typing help accept will print out the syntax for the accept command. The output from help, and in general, the results of many SQL commands, can be too long to display on a screen. You can use set pause on;
to activate the paging feature. When this feature is activated, output will pause at the end of each screen until you hit the "return" key. To turn this feature off, use set pause off;
This document was written originally for Prof. Jeff Ullman's CS145 class in Autumn, 1997; revised by Jun Yang for Prof. Jennifer Widom's CS145 class in Spring, 1998; further revisions by Jeff Ullman, Autumn, 1998; further revisions by Jennifer Widom, Spring 2000; further revisions by Nathan Folkert, Spring 2001; further revisions by Jim Zhuang, Summer 2005.
Using the Oracle Bulk Loader
•
Overview
•
Creating the Control File
•
Creating the Data File
•
Loading Your Data
•
Loading Without a Separate Data File
•
Loading DATE Data
•
Loading Long Strings
•
Entering NULL Values
Overview To use the Oracle bulk loader, you need a control file, which specifies how data should be loaded into the database; and a data file, which specifies what data should be loaded. You will learn how to create these files in turn.
Creating the Control File A simple control file has the following form: LOAD DATA INFILE APPEND INTO TABLE FIELDS TERMINATED BY '<separator>' (<list of all attribute names to load>)
•
is the name of the data file. If you did not give a file name extension for , Oracle will assume the default extension ".dat".
Therefore, it is a good idea to name every data file with an extension, and specify the complete file name with the extension. •
is the name of the table to which data will be loaded. Of course,
•
The optional keyword APPEND says that data will be appended to . If APPEND is omitted, the table must be empty before the bulk load operation or else an error will occur.
•
<separator> specifies the field separator for your data file. This can be any
•
Finally, list the names of attributes of that are set by your data file, separated by commas and enclosed in parentheses. This list need not be the complete list of attributes in the actual schema of the table, nor must it be arranged in the same order as the attributes when the table was created -- sqlldr will match attributes to by their names in the table schema. Any attributes unspecified in the list of attributes will be set to NULL.
it should have been created already before the bulk load operation.
string. It is a good idea to use a string that you know will never appear in the data, so the separator will not be confused with data fields.
As a concrete example, here are the contents of a control file test.ctl: LOAD DATA
INFILE test.dat INTO TABLE test FIELDS TERMINATED BY '|' (i, s)
Creating the Data File Each line in the data file specifies one tuple to be loaded into . It lists, in order, values for the attributes in the list specified in the control file, separated by <separator>. As a concrete example, test.dat might look like: 1|foo 2|bar 3| baz
Recall that the attribute list of test specified in test.ctl is (i, s), where i has the type int, and s has the type char(10). As the result of loading test.dat, the following tuples are inserted into test: (1, 'foo') (2, 'bar') (3, ' baz')
Some Notes of Warning •
Note that the third line of test.dat has a blank after "|". This blank is not ignored by the loader. The value to be loaded for attribute s is ' baz', a fourcharacter string with a leading blank. It is a common mistake to assume that 'baz', a three-character string with no leading blank, will be loaded instead. This can lead to some very frustrating problems that you will not notice until you try to query your loaded data, because ' baz' and 'baz' are different strings.
•
Oracle literally considers every single line to be one tuple, even an empty line! When it tries to load data from an empty line, however, an error would occur and the tuple will be rejected. Some text editors love to add multiple newlines to the end of a file; if you see any strange errors in your .log file about tuples with all NULL columns, this may be the cause. It shouldn't affect other tuples loaded.
•
If you are using a Microsoft text editor, such as MSWord, you will find that Bill Gates believes in ending lines with the sequence (carriage return) (line feed). The UNIX world uses only , so each becomes ^M, the last character of strings in your load file. That makes it impossible for you ever to match a stored string in an SQL query. Here's how you remove ^M symbols from your file. Let's say the file with ^M symbols is bad_myRel.dat. Then the following command will create myRel.dat without ^M symbols:
•
cat bad_myRel.dat | tr -d '\015' > myRel.dat
If you're an emacs fan, type in the following sequence to modify your current buffer: ESC-x replace-string CTRL-q CTRL-m ENTER ENTER
Loading Your Data The Oracle bulk loader is called sqlldr. It is a UNIX-level command, i.e., it should be issued directly from your UNIX shell, rather than within sqlplus. A bulk load command has the following form: sqlldr control= log= bad= Everything but sqlldr is optional -- you will be prompted for your username,
password, and control file. is the name of the control file. If no file name extension is provided, sqlldr will assume the default extension ".ctl". The name of the data file is not needed on the command line because it is specified within the control file. You may designate as the log file. If no file name extension is provided, ".log" will be assumed. sqlldr will fill the log file with relevant information about the bulk load operation, such as the number of tuples loaded, and a description of errors that may have occurred. Finally, you may designate as the file where bad tuples (any tuples for which an error occurs on an attempt to load them) are recorded (if they occur). Again, if no file extension is specified, Oracle uses ".bad". If no log file or bad file are specified, sqlldr will use the name of the control file with the .log and .bad extensions, respectively.
As a concrete example, if sally wishes to run the control file test.ctl and have the log output stored in test.log, then she should type sqlldr sally control=test.ctl log=test.log
Reminder: Before you run any Oracle commands such as sqlldr and sqlplus, make sure you have already set up the correct environment by sourcing /afs/ir/class/cs145/all.env (see Getting Started With Oracle).
Loading Without a Separate Data File It is possible to use just the control file to load data, instead of using a separate data file. Here is an example: LOAD DATA INFILE * INTO TABLE test FIELDS TERMINATED BY '|' (i, s) BEGINDATA 1|foo 2|bar 3| baz The trick is to specify "*" as the name of the data file, and use BEGINDATA to
start the data section in the control file.
Loading DATE Data
The DATE datatype can have its data loaded in a format you specify with considerable flexibility. First, suppose that you have created a relation with an attribute of type DATE: CREATE TABLE foo ( i int, d date );
In the control file, when you describe the attributes of foo being loaded, you follow the attribute d by its type DATE and a date mask. A date mask specifies the format your date data will use. It is a quoted string with the following conventions: ○
Sequences of d, m, or y, denote fields in your data that will be interpreted as days, months, years, respectively. As with almost all of SQL, capitals are equally acceptable, e.g., MM is a month field.
○
The lengths of these fields specify the maximum lengths for the corresponding values in your data. However, the data can be shorter.
○
Other characters, such as dash, are treated literally, and must appear in your data if you put them in the mask.
Here is an example control file: LOAD DATA INFILE * INTO TABLE foo FIELDS TERMINATED BY '|' (i, d DATE 'dd-mm-yyyy') BEGINDATA 1|01-01-1990 2|4-1-1998
Notice that, as illustrated by the second tuple above, a field can be shorter than the corresponding field in the date mask. The punctuation "-" tells the loader that the day and month fields of the second tuple terminate early.
Loading Long Strings String fields that may be longer than 255 characters, such as for data types CHAR(2000) or VARCHAR(4000), require a special CHAR(n) declaration in the control file. For example, if table foo was created as CREATE TABLE foo (x VARCHAR(4000));
Then a sample control file should look like: LOAD DATA INFILE INTO TABLE foo FIELDS TERMINATED BY '|' (x CHAR(4000))
Note that the declaration takes the form CHAR(n) regardless of whether the field type was declared as CHAR or VARCHAR.
Entering NULL Values You may specify NULL values simply by entering fields in your data file without content. For example, if we were entering integer values into a table with schema (a, b, c) specified in the .ctl file, the following lines in the data file: 3||5 |2|4 1||6 ||7
would result in inserting the following tuples in the relation: (3, NULL, 5) (NULL, 2, 4) (1, NULL, 6) (NULL, NULL, 7)
Keep in mind that any primary keys or other constraints requiring that values be non-NULL will reject tuples for which those attributes are unspecified.
Note:If the final field in a given row of your data file will be unspecified (NULL), you have to include the line TRAILING NULLCOLS after the FIELDS TERMINATED BY line in your control file, otherwise sqlldr will reject that tuple. sqlldr will also reject a tuple whose columns are all set to NULL in the data file. If you do not wish to enter values for any row of a given column, you can, as mentioned above, leave that column out of the attribute list altogether. This document was written originally for Prof. Jeff Ullman's CS145 class in Autumn, 1997; revised by Jun Yang for Prof. Jennifer Widom's CS145 class in Spring, 1998; further revisions by Jeff Ullman, Autumn, 1998; further revisions by Srinivas Vemuri for Prof. Jeff Ullman's CS145 class in Autumn, 1999; further revisions by Nathan Folkert for Prof. Jennifer Widom's CS145 class in Spring, 2001. Further revisions by Wang Lam for Prof. Widom's CS145 class in Spring, 2003.
Resources •
Database Systems: The Complete Book by Hector Garcia, Jeff Ullman, and Jennifer Widom.
•
A First Course in Database Systems by Jeff Ullman and Jennifer Widom.
•
Gradiance SQL Tutorial.
Oracle 9i SQL versus Standard SQL
This document highlights some of the differences between the SQL standard and the SQL dialect of Oracle 9i. Please share with us any additional differences that you may find. •
Basic SQL Features
•
Comments
•
Data Types
•
Indexes
•
Views
•
Constraints
•
Triggers
•
Transactions
•
Timing SQL Commands
•
PL/SQL Vs. SQL/PSM
•
Object-Relational Features
Basic SQL Features Oracle does not support AS in FROM clauses, but you can still specify tuple variables without AS: from Relation1 u, Relation2 v
On the other hand, Oracle does support AS in SELECT clauses, although the use of AS is completely optional.
The set-difference operator in Oracle is called MINUS rather than EXCEPT. There is no bag-difference operator corresponding to EXCEPT ALL. The bag-intersection operator INTERSECT ALL is not implemented either. However, the bag-union operator UNION ALLis supported.
In Oracle, you must always prefix an attribute reference with the table name whenever this attribute name appears in more than one table in the FROM clause. For example, suppose that we have tables R(A,B) and S(B,C). The following query does not work in Oracle, even though B is unambiguous because R.B is equated to S.B in the WHERE clause: select B from R, S where R.B = S.B;
Instead, you should use:
/* ILLEGAL! */
select R.B from R, S where R.B = S.B;
In Oracle, the negation logical operator (NOT) should go in front of the boolean expression, not in front of the comparison operator. For example, "NOT A = ANY (<subquery>)" is a valid WHERE condition, but "A NOT = ANY (<subquery>)" is not. (Note that "A <> ANY (<subquery>)" is also a valid condition, but means something different.) There is one exception to this rule: You may use either "NOT A IN (<subquery>)" or "A NOT IN (<subquery>)".
In Oracle, an aliased relation is invisible to a subquery's FROM clause. For example, SELECT * FROM R S WHERE EXISTS (SELECT * FROM S)
is rejected because Oracle does not find S in the subquery, but SELECT * FROM R S WHERE EXISTS (SELECT * FROM R WHERE R.a = S.a)
is accepted.
In Oracle, a query that includes 1. a subquery in the FROM clause, using GROUP BY; and 2. a subquery in the WHERE clause, using GROUP BY can cause the database connection to break with an error (ORA-03113: end-of-file on communication channel), even if the two GROUP BY clauses are unrelated.
Comments In Oracle, comments may be introduced in two ways: 1. With /*...*/, as in C. 2. With a line that begins with two dashes --. Thus: -- This is a comment SELECT * /* and so is this */ FROM R;
Data Types BIT type is not supported. There is a BOOLEAN type in PL/SQL (see Using Oracle
PL/SQL for details), but it cannot be used for a database column.
Domains (i.e., type aliases) are not supported.
Dates and times are supported differently in Oracle. For details, please refer to Oracle Dates and Times, available from the class web page.
CHAR(n) can be of length up to 2000. VARCHAR(n) can be of length up to 4000.
However, special treatment is required when bulk-loading strings longer than 255 characters. See The Oracle Bulk Loader for details.
Indexes To create an index in Oracle, use the syntax: create [unique] index on ();
In general, could contain more than one attribute. Such an index allows efficient retrieval of tuples with given values for . The optional keyword UNIQUE, if specified, declares to be duplicate-free, which in effect makes a key of .
To get rid of an index, use: drop index ;
Oracle automatically creates an index for each UNIQUE or PRIMARY KEY declaration. For example, if you create a table foo as follows: create table foo (a int primary key, b varchar(20) unique); Oracle will automatically create one index on foo.a and another on foo.b. Note that
you cannot drop indexes for UNIQUE and PRIMARY KEY attributes. These indexes are dropped automatically when you drop the table or the key constraints (see the section on Constraints).
To find out what indexes you have, use select index_name from user_indexes; USER_INDEXES is another system table just like USER_TABLES. This can become
especially helpful if you forget the names of your indexes and therefore cannot drop them. You might also see weird names of the indexes created by Oracle for UNIQUE and PRIMARY KEY attributes, but you will not be able to drop these indexes.
On the Stanford Oracle installation, there are two "tablespaces", one for data, the other for indexes. Every time you create an index (either explicitly with CREATE INDEX or implicitly with a UNIQUE or PRIMARY KEY declaration), you should (on the Stanford Oracle) follow the declaration by TABLESPACE INDX. In addition, if you are implicitly creating the index, you need the phrase USING INDEX before TABLESPACE INDX. For example: create index RAindex on R(A) tablespace indx; create table foo (a int primary key using index tablespace indx, b varchar(20) unique using index tablespace indx);
Views Oracle supports views as specified in SQL. To find out what views you have created, use: select view_name from user_views;
Constraints To find out what constraints are defined in your database, use: select constraint_name from user_constraints;
Oracle supports key constraints as specified in SQL. For each table, there can be only one PRIMARY KEY declaration, but many UNIQUE declarations. Each PRIMARY KEY (or UNIQUE) declaration can have multiple attributes, which means that these attributes together form a primary key (or a key, respectively) of the table.
Oracle supports referential integrity (foreign key) constraints, and allows an optional ON DELETE CASCADE or ON DELETE SET NULL after a REFERENCES clause in a table declaration. However, it does not allow ON UPDATE options.
Note that when declaring a foreign key constraint at the end of a table declaration it is always necessary to put the list of referencing attributes in parentheses: create table foo (... foreign key () references ());
Oracle supports attribute- and tuple-based constraints, but does not allow CHECK conditions to use subqueries. Thus, there is no way for an attribute- or tuple-based constraint to reference anything else besides the attribute or tuple that is being inserted or updated.
Domain constraints are not supported since domains are not supported.
As for general constraints, ASSERTION is not supported. However, a TRIGGER close to the SQL trigger is supported. See Constraints and Triggers for details.
In the ALTER TABLE statement, Oracle supports ADDing columns and table constraints, MODIFYing column properties and column constraints, and DROPping constraints. However, you cannot MODIFY an attribute-based CHECK constraint. Here are some examples: create table bar (x int, y int, constraint XYcheck check (x > y)); alter table bar add (z int, w int); alter table bar add primary key (x); alter table bar add constraint YZunique unique (y, z); alter table bar modify (w varchar(2) default 'AM' constraint Wnotnull not null); alter table bar add check (w in ('AM', 'PM')); alter table bar drop constraint YZunique; alter table bar drop constraint XYcheck; alter table bar drop constraint Wnotnull; alter table bar drop primary key cascade;
Dropping constraints generally requires knowing their names (only in the special case of primary or unique key constraints can you drop them without specifying their names). Thus, it is always a good idea to name all your constraints.
Triggers Triggers in Oracle differ in several ways from the SQL standard. Details are in a separate section Constraints and Triggers.
Transactions Oracle supports transactions as defined by the SQL standard. A transaction is a sequence of SQL statements that Oracle treats as a single unit of work. As soon as you connect to the database with sqlplus, a transaction begins. Once the transaction begins, every SQL DML (Data Manipulation Language) statement you
issue subsequently becomes a part of this transaction. A transaction ends when you disconnect from the database, or when you issue a COMMIT or ROLLBACK command.
makes permanent any database changes you made during the current transaction. Until you commit your changes, other users cannot see them. ROLLBACK ends the current transaction and undoes any changes made since the transaction began. COMMIT
After the current transaction has ended with a COMMIT or ROLLBACK, the first executable SQL statement that you subsequently issue will automatically begin another transaction. For example, the following SQL commands have the final effect of inserting into table R the tuple (3, 4), but not (1, 2): insert into R values (1, 2); rollback; insert into R values (3, 4); commit;
During interactive usage with sqlplus, Oracle also supports an AUTOCOMMIT option. With this option set to ON each individual SQL statement is treated as a transaction an will be automatically commited right after it is executed. A user can change the AUTOCOMMIT option by typing SET AUTOCOMMIT ON
or
SET AUTOCOMMIT OFF
whereas by typing SHOW ALL
a user can see the current setting for the option (including other ones).
The same rules for designating the end of a transaction (COMMIT/ROLLBACK) and the beginning of it (which is implied and starts just after the last COMMIT/ROLLBACK) apply to programmers interacting with Oracle using Pro*C or JDBC. Note though that Pro*C doesn't support the AUTOCOMMIT option whereas JDBC does and it has a default AUTOCOMMMIT option set to ON. Thus a programmer needs to execute COMMIT/ROLLBACK statements in Pro*C whereas in JDBC a user can make use of the AUTOCOMMIT and never specify explicitly where a transaction starts or ends. For more details, see the respective sections: Pro*C, JDBC. Oracle also supports the SAVEPOINT command. The command SAVEPOINT <sp_name> establishes a savepoint named <sp_name> which marks the current point in the processing of a transaction. This savepoint can be used in conjunction with the command ROLLBACK TO <sp_name> to undo parts of a transaction.
For example, the following commands have the final effect of inserting into table R tuples (5, 6) and (11, 12), but not (7, 8) or (9, 10): insert into R values (5, 6); savepoint my_sp_1; insert into R values (7, 8);
savepoint my_sp_2; insert into R values (9, 10); rollback to my_sp_1; insert into R values (11, 12); commit;
Oracle automatically issues an implicit COMMIT before and after any SQL DDL (Data Definition Language) statement (even if this DDL statement fails) .
Timing SQL Commands Oracle provides a TIMING command for measuring the running time of SQL commands. To activate this feature, type set timing on;
Then, Oracle will automatically display the elapsed wall-clock time for each SQL command you run subsequently. Note that timing data may be affected by external factors such as system load, etc. To turn off timing, type set timing off;
You can also create and control multiple timers; type HELP TIMING in sqlplus for details.
PL/SQL Vs. PSM Here are a few of the most common distinctions between Oracle's PL/SQL and the SQL standard PSM (persistent, stored modules):
In nested if-statements, PL/SQL uses ELSIF, while PSM calls for ELSEIF. Both are used where we would find ELSE IF in C, for example.
To leave a loop, PL/SQL uses EXIT, or EXIT WHEN(...) to exit conditionally. PSM uses LEAVE, and puts the leave-statement in an if-statement to exit conditionally.
Assignments in PL/SQL are with the := operator, as A := B. The corresponding PSM syntax is SET A = B.
Object-Relational Features
There is a great deal of difference between the Oracle and SQL-standard approaches to user-defined types. You should look at the on-line guide Object Relational Features of Oracle for details and examples of the Oracle approach. However, here are a few small places where the approaches almost coincide but differ in small ways:
When defining a user-defined type, Oracle uses CREATE TYPE ... AS OBJECT, while the word ``OBJECT'' is not used in the standard.
When accessing an attribute a of a relation R that is defined to have a user-defined type, the ``dot'' notation works in Oracle, as R.a. In the standard, a must be thought of as a method of the same name, and the syntax is R.a().
To define (not declare) a method, Oracle has you write the code for the method in a CREATE TYPE BODY statement for the type to which the method belongs. The standard uses a CREATE METHOD statement similar to the way functions are defined in PL/SQL or SQL/PSM.
This document was written originally for Prof. Jeff Ullman's CS145 class in Autumn, 1997; revised by Jun Yang for Prof. Jennifer Widom's CS145 class in Spring, 1998; further revisions by Jun Yang, Spring 1999; further revisions by Jennifer Widom, Spring 2000; minor revisions by Nathan Folkert, Spring 2001; Henry Hsieh, Autumn 2001; and Antonios Hondroulis, Spring 2002; further revisions by Wang Lam for Prof. Jennifer Widom's CS145 class in Spring 2003.
Resources •
Database Systems: The Complete Book by Hector Garcia, Jeff Ullman, and Jennifer Widom.
•
A First Course in Database Systems by Jeff Ullman and Jennifer Widom.
•
Gradiance SQL Tutorial.
mySQL versus Standard SQL This document highlights some of the differences between the SQL standard and the SQL dialect of mySQL. Please share with us any additional differences that you may find. •
HAVING Clauses
•
Views
•
Intersection and Set-Difference
•
ANY and ALL
HAVING Clauses mySQL has a very limited form of HAVING clause. Instead of evaluating the HAVING condition within each group, mySQL treats HAVING as a selection on the output tuples. Thus, you can only refer in the HAVING clause to attributes that appear in the SELECT clause. Recent versions of mySQL allow you to refer to aggregates in the SELECT clause by their formula [e.g., AVG(salary)] rather than by an alias established in the SELECT clause by (e.g.) AVG(salary)AS avgSalary.
Views mySQL does not support views. However, unlike some other SQL implementations, mySQL does support fully nested subqueries in the FROM clause. These subqueries can serve as views in many situations, although they do not provide the ability of a view to serve as a macro, with its definition reused in many queries.
Intersection and Set-Difference The INTERSECT and EXCEPT operators of SQL are not supported in mySQL. We suggest that instead, you use a join with all corresponding attributes equated in place of an intersection. For instance, to get the intersection of R(a,b) and S(a,b), write: SELECT DISTINCT * FROM R WHERE EXISTS (SELECT * FROM S WHERE R.a = S.a AND R.b = S.b);
To get the set difference, here is a similar approach using a subquery: SELECT DISTINCT * FROM R WHERE NOT EXISTS (SELECT * FROM S WHERE R.a = S.a AND R.b = S.b);
Note that both these expressions eliminate duplicates, but that is in accordance with the SQL standard.
ANY and ALL There are some discrepancies between the standard and how the ANY and ALL operators are used in SQL. Only "=" seems to be handled completely correctly. Here is a concrete example and the responses mySQL gives. The query, about a Sells(bar, beer, price) relation, is: SELECT * FROM Sells WHERE price Op Quant(SELECT price FROM Sells);
Here, Op is one of the comparisons, and Quant is either ANY or ALL.
O ANY p > (1) =
ALL Correc t
< Corre (1) = ct =
Corre Correc ct t
< Corre (1) > ct < (2)
(2)
> (2)
(2)
(1) mySQL gives an incorrect result, which in each of these cases is the same as what the other of ANY and ALL gives. (2) mySQL gives an incorrect result for both ANY and ALL. For each operator, the result is the same independent of whether ANY or ALL is used. For <, the result is several tuples with low, but different prices, and for > it is the other tuples in the relation Sells, i.e., some of the tuples with high, but different prices. This document was written originally by Jeff Ullman in the Winter of 2004.
Resources •
Database Systems: The Complete Book by Hector Garcia, Jeff Ullman, and Jennifer Widom.
•
A First Course in Database Systems by Jeff Ullman and Jennifer Widom.
•
Gradiance SQL Tutorial.
Oracle Dates and Times •
Overview
•
DATE Format
•
The Current Time
•
Operations on DATE
•
Further Information
Overview Oracle supports both date and time, albeit differently from the SQL2 standard. Rather than using two separate entities, date and time, Oracle only uses one, DATE. The DATE type is stored in a special internal format that includes not just the month, day, and year, but also the hour, minute, and second. The DATE type is used in the same way as other built-in types such as INT. For example, the following SQL statement creates a relation with an attribute of type DATE: create table x(a int, b date);
DATE
Format
When a DATE value is displayed, Oracle must first convert that value from the special internal format to a printable string. The conversion is done by a function TO_CHAR, according to a DATE format. Oracle's default format for DATE is "DD-MON-YY". Therefore, when you issue the query select b from x;
you will see something like: B --------01-APR-98
Whenever a DATE value is displayed, Oracle will call TO_CHAR automatically with the default DATE format. However, you may override the default behavior by calling TO_CHAR explicitly with your own DATE format. For example, SELECT TO_CHAR(b, 'YYYY/MM/DD') AS b FROM x;
returns the result:
B --------------------------------------------------------------------------1998/04/01 The general usage of TO_CHAR is: TO_CHAR(, '') where the string can be formed from over 40 options. Some of the more popular ones
include: MM MON MONTH DD DY YYYY YY RR AM (or PM)
Numeric month (e.g., 07) Abbreviated month name (e.g., JUL) Full month name (e.g., JULY) Day of month (e.g., 24) Abbreviated name of day (e.g., FRI) 4-digit year (e.g., 1998) Last 2 digits of the year (e.g., 98) Like YY, but the two digits are ``rounded'' to a year in the range 1950 to 2049. Thus, 06 is considered 2006 instead of 1906, for example. Meridian indicator
HH HH24 MI SS
Hour of day (1-12) Hour of day (0-23) Minute (0-59) Second (0-59)
You have just learned how to output a DATE value using TO_CHAR. Now what about inputting a DATE value? This is done through a function called TO_DATE, which converts a string to a DATE value, again according to the DATE format. Normally, you do not have to call TO_DATE explicitly: Whenever Oracle expects a DATE value, it will automatically convert your input string using TO_DATE according to the default DATE format "DD-MON-YY". For example, to insert a tuple with a DATE attribute, you can simply type: insert into x values(99, '31-may-98'); Alternatively, you may use TO_DATE explicitly: insert into x values(99, to_date('1998/05/31:12:00:00AM', 'yyyy/mm/dd:hh:mi:ssam')); The general usage of TO_DATE is: TO_DATE(<string>, '') where the string has the same options as in TO_CHAR.
Finally, you can change the default DATE format of Oracle from "DD-MON-YY" to something you like by issuing the following command in sqlplus: alter session set NLS_DATE_FORMAT='<my_format>'; The change is only valid for the current sqlplus session.
The Current Time The built-in function SYSDATE returns a DATE value containing the current date and time on your system. For example, select to_char(sysdate, 'Dy DD-Mon-YYYY HH24:MI:SS') as "Current Time" from dual;
returns
Current Time --------------------------------------------------------------------------Tue 21-Apr-1998 21:18:27 which is the time when I was preparing this document :-) Two interesting things to note here:
•
You can use double quotes to make names case sensitive (by default, SQL is case insensitive), or to force spaces into names. Oracle will treat everything inside the double quotes literally as a single name. In this example, if "Current Time" is not quoted, it would have been interpreted as two case insensitive names CURRENT and TIME, which would actually cause a syntax error.
•
DUAL
is built-in relation in Oracle which serves as a dummy relation to put in the FROM clause when nothing else is appropriate. For example, try "select 1+2 from dual;".
Another name for the built-in function SYSDATE is CURRENT_DATE. Be aware of these special names to avoid name conflicts.
Operations on DATE
You can compare DATE values using the standard comparison operators such as =, !=, >, etc. You can subtract two DATE values, and the result is a FLOAT which is the number of days between the two DATE values. In general, the result may contain a fraction because DATE also has a time component. For obvious reasons, adding, multiplying, and dividing two DATE values are not allowed. You can add and subtract constants to and from a DATE value, and these numbers will be interpreted as numbers of days. For example, SYSDATE+1 will be tomorrow. You cannot multiply or divide DATE values. With the help of TO_CHAR, string operations can be used on DATE values as well. For example, to_char(, 'DD-MON-YY') like '%JUN%' evaluates to true if is in June. This document was written originally by Kristian Widjaja for Prof. Jeff Ullman's CS145 class in Autumn, 1997; revised by Jun Yang for Prof. Jennifer Widom's CS145 class in Spring, 1998; further revisions by Prof. Ullman in Autumn, 1998.
Resources •
Database Systems: The Complete Book by Hector Garcia, Jeff Ullman, and Jennifer Widom.
•
A First Course in Database Systems by Jeff Ullman and Jennifer Widom.
•
Gradiance SQL Tutorial.
Using Oracle PL/SQL •
Basic Structure of PL/SQL
•
Variables and Types
•
Simple PL/SQL Programs
•
Control Flow in PL/SQL
•
Cursors
•
Procedures
•
Discovering Errors
•
Printing Variables
Note: The material on triggers that was formerly in this document has been moved to A New Document on Constraints and Triggers.
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 it in sqlplus or by putting the code in a file and invoking the file in the various ways we learned in Getting Started With Oracle.
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, as described in the next section. 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 THEN <statement_list> ELSE <statement_list> END IF; The ELSE part is optional. If you want a multiway branch, use: IF THEN ... ELSIF THEN ... ... ... ELSIF 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 /* A list of statements. */ END LOOP;
At least one of the statements in should be an EXIT statement of the form EXIT WHEN ; The loop breaks if is true. For example, here is 1) through (100, 100) into T1 of the above two examples:
a way to insert each of the pairs (1,
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
•
A WHILE loop can be formed with
• • • •
by itself is an unconditional loop break. Use it inside a conditional if you like. WHILE LOOP END LOOP;
•
A simple FOR loop can be formed with:
• • • •
FOR IN <start>.. LOOP END LOOP;
Here, can be any variable; it is local to the for-loop and need not be declared. Also, <start> and 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: ○
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.
○
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.
○
On Line (14), a SQL DELETE statement that deletes the current tuple using the special WHERE condition CURRENT OF T1Cursor.
○
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 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 (<param_list>) RETURN AS ... In the body of the function definition, "RETURN <expression>;" exits from the function returns the value of <expression>.
and
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 ;
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. Note that the location of the error given as part of the error message is not always accurate!
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, such as :new discussed in the section on triggers.
The steps are as follows: 1. We declare a bind variable as follows: VARIABLE
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 :;
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; This document was written originally by Yu-May Chang and Jeff Ullman for CS145, Autumn 1997; revised by Jun Yang for Prof. Jennifer Widom's CS145 class in Spring, 1998; additional material by Jeff Ullman, Autumn 1998; further revisions by Jun Yang, Spring 1999; minor revisions by Jennifer Widom, Spring 2000.
Resources •
Database Systems: The Complete Book by Hector Garcia, Jeff Ullman, and Jennifer Widom.
•
A First Course in Database Systems by Jeff Ullman and Jennifer Widom.
•
Gradiance SQL Tutorial.
Constraints and Triggers Constraints are declaractions of conditions about the database that must remain true. These include attributed-based, tuple-based, key, and referential integrity constraints. The system checks for the violation of the constraints on actions that may cause a violation, and aborts the action accordingly. Information on SQL
constraints can be found in the textbook. The Oracle implementation of constraints differs from the SQL standard, as documented in Oracle 9i SQL versus Standard SQL.
Triggers are a special PL/SQL construct similar to procedures. However, a procedure is executed explicitly from another block via a procedure call, while a trigger is executed implicitly whenever the triggering event happens. The triggering event is either a INSERT, DELETE, or UPDATE command. The timing can be either BEFORE or AFTER. The trigger can be either row-level or statement-level, where the former fires once for each row affected by the triggering statement and the latter fires once for the whole statement. •
•
Constraints: ○
Deferring Constraint Checking
○
Constraint Violations
Triggers: ○
Basic Trigger Syntax
○
Trigger Example
○
Displaying Trigger Definition Errors
○
Viewing Defined Triggers
○
Dropping Triggers
○
Disabling Triggers
○
Aborting Triggers with Error
○
Mutating Table Errors
Deferring Constraint Checking Sometimes it is necessary to defer the checking of certain constraints, most commonly in the "chicken-and-egg" problem. Suppose we want to say: CREATE TABLE chicken (cID INT PRIMARY KEY, eID INT REFERENCES egg(eID)); CREATE TABLE egg(eID INT PRIMARY KEY, cID INT REFERENCES chicken(cID));
But if we simply type the above statements into Oracle, we'll get an error. The reason is that the CREATE TABLE statement for chicken refers to table egg, which hasn't been created yet! Creating egg won't help either, because egg refers to chicken.
To work around this problem, we need SQL schema modification commands. First, create chicken and egg without foreign key declarations: CREATE TABLE chicken(cID INT PRIMARY KEY, eID INT); CREATE TABLE egg(eID INT PRIMARY KEY,
cID INT);
Then, we add foreign key constraints: ALTER TABLE chicken ADD CONSTRAINT chickenREFegg FOREIGN KEY (eID) REFERENCES egg(eID) INITIALLY DEFERRED DEFERRABLE; ALTER TABLE egg ADD CONSTRAINT eggREFchicken FOREIGN KEY (cID) REFERENCES chicken(cID) INITIALLY DEFERRED DEFERRABLE; INITIALLY DEFERRED DEFERRABLE tells Oracle to do deferred constraint checking. For
example, to insert (1, 2) into chicken and (2, 1) into egg, we use: INSERT INTO chicken VALUES(1, 2); INSERT INTO egg VALUES(2, 1); COMMIT;
Because we've declared the foreign key constraints as "deferred", they are only checked at the commit point. (Without deferred constraint checking, we cannot insert anything into chicken and egg, because the first INSERT would always be a constraint violation.)
Finally, to get rid of the tables, we have to drop the constraints first, because Oracle won't allow us to drop a table that's referenced by another table. ALTER TABLE egg DROP CONSTRAINT eggREFchicken; ALTER TABLE chicken DROP CONSTRAINT chickenREFegg; DROP TABLE egg; DROP TABLE chicken;
Constraint Violations In general, Oracle returns an error message when a constraint is violated. Specifically for users of JDBC, this means an SQLException gets thrown, whereas for Pro*C users the SQLCA struct gets updated to reflect the error. Programmers must use the WHENEVER statement and/or check the SQLCA contents (Pro*C users) or catch the exception SQLException (JDBC users) in order to get the error code returned by Oracle.
Some vendor specific error code numbers are 1 for primary key constraint violations, 2291 for foreign key violations, 2290 for attribute and tuple CHECK constraint violations. Oracle also provides simple error message strings that have a format similar to the following: ORA-02290: check constraint (YFUNG.GR_GR) violated
or ORA-02291: integrity constraint (HONDROUL.SYS_C0067174) violated - parent key not found
For more details on how to do error handling, please take a look at Pro*C Error handling or at the Retrieving Exceptions section of JDBC Error handling.
Basic Trigger Syntax
Below is the syntax for creating a trigger in Oracle (which differs slightly from standard SQL syntax): CREATE [OR REPLACE] TRIGGER {BEFORE|AFTER} {INSERT|DELETE|UPDATE} ON [REFERENCING [NEW AS ] [OLD AS ]] [FOR EACH ROW [WHEN ()]]
Some important points to note: •
You can create only BEFORE and AFTER triggers for tables. (INSTEAD OF triggers are only available for views; typically they are used to implement view updates.)
•
You may specify up to three triggering events using the keyword OR. Furthermore, UPDATE can be optionally followed by the keyword OF and a list of attribute(s) in . If present, the OF clause defines the event to be only an update of the attribute(s) listed after OF. Here are some examples:
• • • •
... INSERT ON R ... ... INSERT OR DELETE OR UPDATE ON R ... ... UPDATE OF A, B OR INSERT ON R ...
•
If FOR EACH ROW option is specified, the trigger is row-level; otherwise, the trigger is statement-level.
•
Only for row-level triggers:
•
○
The special variables NEW and OLD are available to refer to new and old tuples respectively. Note: In the trigger body, NEW and OLD must be preceded by a colon (":"), but in the WHEN clause, they do not have a preceding colon! See example below.
○
The REFERENCING clause can be used to assign aliases to the variables NEW and OLD.
○
A trigger restriction can be specified in the WHEN clause, enclosed by parentheses. The trigger restriction is a SQL condition that must be satisfied in order for Oracle to fire the trigger. This condition cannot contain subqueries. Without the WHEN clause, the trigger is fired for each row.
is a PL/SQL block, rather than sequence of SQL statements. Oracle has placed certain restrictions on what you can do in ,
in order to avoid situations where one trigger performs an action that triggers a second trigger, which then triggers a third, and so on, which could potentially create an infinite loop. The restrictions on include: ○
You cannot modify the same relation whose modification is the event triggering the trigger.
○
You cannot modify a relation connected to the triggering relation by another constraint such as a foreign-key constraint.
Trigger Example We illustrate Oracle's syntax for creating a trigger through an example based on the following two tables: CREATE TABLE T4 (a INTEGER, b CHAR(10)); CREATE TABLE T5 (c CHAR(10), d INTEGER);
We create a trigger that may insert a tuple into T5 when a tuple is inserted into T4. Specifically, the trigger checks whether the new tuple has a first component 10 or less, and if so inserts the reverse tuple into T5: CREATE TRIGGER trig1 AFTER INSERT ON T4 REFERENCING NEW AS newRow FOR EACH ROW WHEN (newRow.a <= 10) BEGIN INSERT INTO T5 VALUES(:newRow.b, :newRow.a); END trig1; . run; Notice that we end the CREATE TRIGGER statement with a dot and run, as for all
PL/SQL statements in general. Running the CREATE TRIGGER statement only creates the trigger; it does not execute the trigger. Only a triggering event, such as an insertion into T4 in this example, causes the trigger to execute.
Displaying Trigger Definition Errors As for PL/SQL procedures, if you get a message Warning: Trigger created with compilation errors.
you can see the error messages by typing
show errors trigger ; Alternatively, you can type, SHO ERR (short for SHOW ERRORS) to see the most recent
compilation error. Note that the reported line numbers where the errors occur are not accurate.
Viewing Defined Triggers To view a list of all defined triggers, use: select trigger_name from user_triggers;
For more details on a particular trigger:
select trigger_type, triggering_event, table_name, referencing_names, trigger_body from user_triggers where trigger_name = '';
Dropping Triggers To drop a trigger: drop trigger ;
Disabling Triggers To disable or enable a trigger: alter trigger {disable|enable};
Aborting Triggers with Error Triggers can often be used to enforce contraints. The WHEN clause or body of the trigger can check for the violation of certain conditions and signal an error accordingly using the Oracle built-in function RAISE_APPLICATION_ERROR. The action that activated the trigger (insert, update, or delete) would be aborted. For example, the following trigger enforces the constraint Person.age >= 0: create table Person (age int); CREATE TRIGGER PersonCheckAge AFTER INSERT OR UPDATE OF age ON Person FOR EACH ROW BEGIN IF (:new.age < 0) THEN RAISE_APPLICATION_ERROR(-20000, 'no negative age allowed'); END IF; END; . RUN;
If we attempted to execute the insertion: insert into Person values (-3);
we would get the error message: ERROR at line 1: ORA-20000: no negative age allowed ORA-06512: at "MYNAME.PERSONCHECKAGE", line 3 ORA-04088: error during execution of trigger 'MYNAME.PERSONCHECKAGE'
and nothing would be inserted. In general, the effects of both the trigger and the triggering statement are rolled back.
Mutating Table Errors Sometimes you may find that Oracle reports a "mutating table error" when your trigger executes. This happens when the trigger is querying or modifying a "mutating table", which is either the table whose modification activated the trigger, or a table that might need to be updated because of a foreign key constraint with a CASCADE policy. To avoid mutating table errors: •
A row-level trigger must not query or modify a mutating table. (Of course, NEW and OLD still can be accessed by the trigger.)
•
A statement-level trigger must not query or modify a mutating table if the trigger is fired as the result of a CASCADE delete.
This document was written originally by Yu-May Chang and Jeff Ullman for CS145 in Autumn, 1997; revised by Jun Yang for Prof. Jennifer Widom's CS145 class in Spring, 1998; further revisions by Jun Yang, Spring 1999; further revisions by Jennifer Widom, Spring 2000; minor revisions by Nathan Folkert, Spring 2001, Henry Hsieh, Autumn 2001, Antonios Hondroulis, Spring 2002, and Glen Jeh, Spring 2002.
Resources •
Database Systems: The Complete Book by Hector Garcia, Jeff Ullman, and Jennifer Widom.
•
A First Course in Database Systems by Jeff Ullman and Jennifer Widom.
•
Gradiance SQL Tutorial.
Introduction to Pro*C Embedded SQL •
Overview
•
Pro*C Syntax
•
○
SQL
○
Preprocessor Directives
○
Statement Labels
Host Variables ○
Basics
○
Pointers
○
Structures
○
Arrays
○
Indicator Variables
○
Datatype Equivalencing
•
Dynamic SQL
•
Transactions
•
Error Handling ○
SQLCA
○
WHENEVER Statement
•
Demo Programs
•
C++ Users
•
List of Embedded SQL Statements Supported by Pro*C
Overview Embedded SQL is a method of combining the computing power of a high-level language like C/C++ and the database manipulation capabilities of SQL. It allows you to execute any SQL statement from an application program. Oracle's embedded SQL environment is called Pro*C.
A Pro*C program is compiled in two steps. First, the Pro*C precompiler recognizes the SQL statements embedded in the program, and replaces them with appropriate calls to the functions in the SQL runtime library. The output is pure C/C++ code with all the pure C/C++ portions intact. Then, a regular C/C++ compiler is used to compile the code and produces the executable. For details, see the section on Demo Programs.
Pro*C Syntax SQL All SQL statements need to start with EXEC SQL and end with a semicolon ";". You can place the SQL statements anywhere within a C/C++ block, with the restriction that the declarative statements do not come after the executable statements. As an example: { int a; /* ... */ EXEC SQL SELECT salary INTO :a FROM Employee WHERE SSN=876543210; /* ... */ printf("The salary is %d\n", a); /* ... */ }
Preprocessor Directives The C/C++ preprocessor directives that work with Pro*C are #include and #if. Pro*C does not recognize #define. For example, the following code is invalid: #define THE_SSN 876543210 /* ... */ EXEC SQL SELECT salary INTO :a FROM Employee WHERE SSN = THE_SSN;
/* INVALID */
Statement Labels You can connect C/C++ labels with SQL as in: EXEC SQL WHENEVER SQLERROR GOTO error_in_SQL; /* ... */ error_in_SQL: /* do error handling */ We will come to what WHENEVER means later in the section on Error Handling.
Host Variables Basics Host variables are the key to the communication between the host program and the database. A host variable expression must resolve to an lvalue (i.e., it can be assigned). You can declare host variables according to C syntax, as you declare regular C variables. The host variable declarations can be placed wherever C variable declarations can be placed. (C++ users need to use a declare section; see the section on C++ Users.) The C datatypes that can be used with Oracle include: •
char
•
char[n]
•
int
•
short
•
long
•
float
•
double
•
VARCHAR[n] - This is a psuedo-type recognized by the Pro*C precompiler. It is
used to represent blank-padded, variable-length strings. Pro*C precompiler will convert it into a structure with a 2-byte length field and a n-byte character array.
You cannot use register storage-class specifier for the host variables.
A host variable reference must be prefixed with a colon ":" in SQL statements, but should not be prefixed with a colon in C statements. When specifying a string literal via a host variable, the single quotes must be omitted; Pro*C understands that you are specifying a string based on the declared type of the host variable. C function calls and most of the pointer arithmetic expressions cannot be used as host variable references even though they may indeed resolve to lvalues. The following code illustrates both legal and illegal host variable references: int int int int
deptnos[3] = { 000, 111, 222 }; get_deptno() { return deptnos[2]; } *get_deptnoptr() { return &(deptnos[2]); } main() { int x; char *y; int z; /* ... */ EXEC SQL INSERT INTO emp(empno, ename, deptno) VALUES(:x, :y, :z); /* LEGAL */ EXEC SQL INSERT INTO emp(empno, ename, deptno) VALUES(:x + 1, /* LEGAL: the reference is to x */ 'Big Shot', /* LEGAL: but not really a host var */ :deptnos[2]); /* LEGAL: array element is fine */ EXEC SQL INSERT INTO emp(empno, ename, deptno) VALUES(:x, :y, :(*(deptnos+2))); /* ILLEGAL: although it has an lvalue */ EXEC SQL INSERT INTO emp(empno, ename, deptno) VALUES(:x, :y, :get_deptno()); /* ILLEGAL: no function calls */ EXEC SQL INSERT INTO emp(empno, ename, deptno) VALUES(:x, :y, :(*get_depnoptr())); /* ILLEGAL: although it has an lvalue */ /* ... */ }
Pointers You can define pointers using the regular C syntax, and use them in embedded SQL statements. As usual, prefix them with a colon: int *x; /* ... */ EXEC SQL SELECT xyz INTO :x FROM ...; The result of this SELECT statement will be written into *x, not x.
Structures Structures can be used as host variables, as illustrated in the following example: typedef struct { char name[21]; int SSN; } Emp; /* ... */
Arrays Host arrays can be used in the following way: int emp_number[50]; char name[50][11]; /* ... */ EXEC SQL INSERT INTO emp(emp_number, name) VALUES (:emp_number, :emp_name);
which will insert all the 50 tuples in one go.
Arrays can only be single dimensional. The example char name[50][11] would seem to contradict that rule. However, Pro*C actually considers name a one-dimensional array of strings rather than a two-dimensional array of characters. You can also have arrays of structures. When using arrays to store the results of a query, if the size of the host array (say n) is smaller than the actual number of tuples returned by the query, then only the first n result tuples will be entered into the host array.
Indicator Variables Indicator variables are essentially "NULL flags" attached to host variables. You can associate every host variable with an optional indicator variable. An indicator variable must be defined as a 2-byte integer (using the type short) and, in SQL statements, must be prefixed by a colon and immediately follow its host variable. Or, you may use the keyword INDICATOR in between the host variable and indicator variable. Here is an example: short indicator_var; EXEC SQL SELECT xyz INTO :host_var:indicator_var FROM ...; /* ... */ EXEC SQL INSERT INTO R VALUES(:host_var INDICATOR :indicator_var, ...); You can use indicator variables in the INTO clause of a SELECT statement to detect NULL's or truncated values in the output host variables. The values Oracle can assign
to an indicator variable have the following meanings:
-1 The column value is NULL, so the value of the host variable is indeterminate. 0
Oracle assigned an intact column value to the host variable.
>0 Oracle assigned a truncated column value to the host variable. The integer
returned by the indicator variable is the original length of the column value. -2
Oracle assigned a truncated column variable to the host variable, but the original column value could not be determined.
You can also use indicator variables in the VALUES and SET clause of an INSERT or UPDATE statement to assign NULL's to input host variables. The values your program can assign to an indicator variable have the following meanings: -1
Oracle will assign a NULL to the column, ignoring the value of the host variable.
>=0
Oracle will assign the value of the host variable to the column.
Datatype Equivalencing Oracle recognizes two kinds of datatypes: internal and external. Internal datatypes specify how Oracle stores column values in database tables. External datatypes specify the formats used to store values in input and output host variables. At precompile time, a default Oracle external datatype is assigned to each host variable. Datatype equivalencing allows you to override this default equivalencing and lets you control the way Oracle interprets the input data and formats the output data.
The equivalencing can be done on a variable-by-variable basis using the VAR statement. The syntax is: EXEC SQL VAR IS [ () ];
For example, suppose you want to select employee names from the emp table, and then pass them to a routine that expects C-style '\0'-terminated strings. You need not explicitly '\0'-terminate the names yourself. Simply equivalence a host variable to the STRING external datatype, as follows: char emp_name[21]; EXEC SQL VAR emp_name IS STRING(21); The length of the ename column in the emp table is 20 characters, so you allot emp_name 21 characters to accommodate the '\0'-terminator. STRING is an Oracle
external datatype specifically designed to interface with C-style strings. When you select a value from the ename column into emp_name, Oracle will automatically '\0'terminate the value for you.
You can also equivalence user-defined datatypes to Oracle external datatypes using the TYPE statement. The syntax is: EXEC SQL TYPE <user_type> IS [ () ] [REFERENCE];
You can declare a user-defined type to be a pointer, either explicitly, as a pointer to a scalar or structure, or implicitly as an array, and then use this type in a TYPE
statement. In these cases, you need to use the REFERENCE clause at the end of the statement, as shown below: typedef unsigned char *my_raw; EXEC SQL TYPE my_raw IS VARRAW(4000) REFERENCE; my_raw buffer; /* ... */ buffer = malloc(4004);
Here we allocated more memory than the type length (4000) because the precompiler also returns the length, and may add padding after the length in order to meet the alignment requirement on your system.
Dynamic SQL While embedded SQL is fine for fixed applications, sometimes it is important for a program to dynamically create entire SQL statements. With dynamic SQL, a statement stored in a string variable can be issued. PREPARE turns a character string into a SQL statement, and EXECUTE executes that statement. Consider the following example. char *s = "INSERT INTO emp VALUES(1234, 'jon', 3)"; EXEC SQL PREPARE q FROM :s; EXEC SQL EXECUTE q; Alternatively, PREPARE and EXECUTE may be combined into one statement: char *s = "INSERT INTO emp VALUES(1234, 'jon', 3)"; EXEC SQL EXECUTE IMMEDIATE :s;
TransactionsOracle PRO*C supports transactions as defined by the SQL standard. A transaction is a sequence of SQL statements that Oracle treats as a single unit of work. A transaction begins at your first SQL statement. A transaction ends when you issue "EXEC SQL COMMIT" (to make permanent any database changes during the current transaction) or "EXEC SQL ROLLBACK" (to undo any changes since the current transaction began). After the current transaction ends with your COMMIT or ROLLBACK statement, the next executable SQL statement will automatically begin a new transaction.
If your program exits without calling EXEC SQL COMMIT, all database changes will be discarded.
Error HandlingAfter each executable SQL statement, your program can find the status of execution either by explicit checking of SQLCA, or by implicit checking using the WHENEVER statement. These two ways are covered in details below.
SQLCA SQLCA (SQL Communications Area) is used to detect errors and status changes in your program. This structure contains components that are filled in by Oracle at runtime after every executable SQL statement.
To use SQLCA you need to include the header file sqlca.h using the #include directive. In case you need to include sqlca.h at many places, you need to first undefine the macro SQLCA with #undef SQLCA. The relevant chunk of sqlca.h follows: #ifndef SQLCA #define SQLCA 1 struct sqlca { /* ub1 */ char sqlcaid[8]; /* b4 */ long sqlabc; /* b4 */ long sqlcode; struct { /* ub2 */ unsigned short sqlerrml; /* ub1 */ char sqlerrmc[70]; } sqlerrm; /* ub1 */ char sqlerrp[8]; /* b4 */ long sqlerrd[6]; /* ub1 */ char sqlwarn[8]; /* ub1 */ char sqlext[8]; }; /* ... */ The fields in sqlca have the following meaning:
sqlcaid This string component is initialized to "SQLCA" to identify the SQL
Communications Area. sqlcabc This integer component holds the length, in bytes, of the SQLCA structure. sqlcode This integer component holds the status code of the most recently
executed SQL statement: 0
No error. Statement executed but exception detected. This occurs when
>0 Oracle cannot find a row that meets your WHERE condition or
when a SELECT INTO or FETCH returns no rows. Oracle did not execute the statement because of an error. When <0 such errors occur, the current transaction should, in most cases,
be rolled back.
sqlerrm This embedded structure contains the following two components:
•
sqlerrml - Length of the message text stored in sqlerrmc.
•
sqlerrmc - Up to 70 characters of the message text corresponding to the error code stored in sqlcode.
sqlerrp Reserved for future use. sqlerrd This array of binary integers has six elements:
•
sqlerrd[0] - Future use.
•
sqlerrd[1] - Future use.
•
sqlerrd[2] - Numbers of rows processed by the most recent SQL
•
sqlerrd[3] - Future use.
•
sqlerrd[4] - Offset that specifies the character position at which a
•
sqlerrd[5] - Future use.
statement.
parse error begins in the most recent SQL statement.
sqlwarn This array of single characters has eight elements used as warning flags. Oracle sets a flag by assigning to it the character 'W'. sqlwarn[0] Set if any other flag is set. sqlwarn[1]
Set if a truncated column value was assigned to an output host variable.
sqlwarn[2]
Set if a NULL column value is not used in computing a SQL aggregate such as AVG or SUM.
sqlwarn[3]
Set if the number of columns in SELECT does not equal the number of host variables specified in INTO.
sqlwarn[4]
Set if every row in a table was processed by an UPDATE or DELETE statement without a WHERE clause. Set if a procedure/function/package/package body
sqlwarn[5] creation command fails because of a PL/SQL
compilation error. sqlwarn[6] No longer in use. sqlwarn[7] No longer in use.
sqlext
Reserved for future use.
SQLCA can only accommodate error messages up to 70 characters long in its sqlerrm component. To get the full text of longer (or nested) error messages, you need the sqlglm() function: void sqlglm(char *msg_buf, size_t *buf_size, size_t *msg_length);
where msg_buf is the character buffer in which you want Oracle to store the error message; buf_size specifies the size of msg_buf in bytes; Oracle stores the actual length of the error message in *msg_length. The maximum length of an Oracle error message is 512 bytes.
WHENEVER Statement This statement allows you to do automatic error checking and handling. The syntax is: EXEC SQL WHENEVER ; Oracle automatically checks SQLCA for , and if such condition is
detected, your program will automatically perform .
can be any of the following:
•
SQLWARNING - sqlwarn[0] is set because Oracle returned a warning
•
SQLERROR - sqlcode is negative because Oracle returned an error
•
NOT FOUND - sqlcode is positive because Oracle could not find a row that meets your WHERE condition, or a SELECT INTO or FETCH returned no rows
can be any of the following:
•
CONTINUE - Program will try to continue to run with the next statement if
•
DO - Program transfers control to an error handling function