Database Management Systems Raghu Ramakrishnan 185 192
Database Management Systems Raghu Ramakrishnan 185 192
We can specify complex constraints over a single table using table constraints, which
have the form CHECK conditional-expression. For example, to ensure that rating must
be an integer in the range 1 to 10, we could use:
To enforce the constraint that Interlake boats cannot be reserved, we could use:
CHAPTER 3
When a row is inserted into Reserves or an existing row is modified, the conditional
expression in the CHECK constraint is evaluated. If it evaluates to false, the command
is rejected.
A user can define a new domain using the CREATE DOMAIN statement, which makes use
of CHECK constraints.
INTEGER is the base type for the domain ratingval, and every ratingval value
must be of this type. Values in ratingval are further restricted by using a CHECK
constraint; in defining this constraint, we use the keyword VALUE to refer to a value
in the domain. By using this facility, we can constrain the values that belong to a
domain using the full power of SQL queries. Once a domain is defined, the name of
the domain can be used to restrict column values in a table; we can use the following
line in a schema declaration, for example:
rating ratingval
The optional DEFAULT keyword is used to associate a default value with a domain. If
the domain ratingval is used for a column in some relation, and no value is entered
for this column in an inserted tuple, the default value 0 associated with ratingval is
used. (If a default value is specified for the column as part of the table definition, this
takes precedence over the default value associated with the domain.) This feature can
be used to minimize data entry errors; common default values are automatically filled
in rather than being typed in.
INTEGER as a base type. The intent is to force a comparison of a Sailorid value with a
Boatclass value to always fail (since they are drawn from different domains); however,
since they both have the same base type, INTEGER, the comparison will succeed in SQL-
92. This problem is addressed through the introduction of distinct types in SQL:1999
(see Section 3.4).
Table constraints are associated with a single table, although the conditional expression
in the CHECK clause can refer to other tables. Table constraints are required to hold
only if the associated table is nonempty. Thus, when a constraint involves two or more
tables, the table constraint mechanism is sometimes cumbersome and not quite what
is desired. To cover such situations, SQL supports the creation of assertions, which
are constraints not associated with any one table.
As an example, suppose that we wish to enforce the constraint that the number of
boats plus the number of sailors should be less than 100. (This condition might be
required, say, to qualify as a ‘small’ sailing club.) We could try the following table
constraint:
This solution suffers from two drawbacks. It is associated with Sailors, although it
involves Boats in a completely symmetric way. More important, if the Sailors table is
empty, this constraint is defined (as per the semantics of table constraints) to always
hold, even if we have more than 100 rows in Boats! We could extend this constraint
specification to check that Sailors is nonempty, but this approach becomes very cum-
bersome. The best solution is to create an assertion, as follows:
Action: A procedure that is executed when the trigger is activated and its con-
dition is true.
A condition in a trigger can be a true/false statement (e.g., all employee salaries are
less than $100,000) or a query. A query is interpreted as true if the answer set is
nonempty, and false if the query has no answers. If the condition part evaluates to
true, the action associated with the trigger is executed.
A trigger action can examine the answers to the query in the condition part of the
trigger, refer to old and new values of tuples modified by the statement activating
the trigger, execute new queries, and make changes to the database. In fact, an
action can even execute a series of data-definition commands (e.g., create new tables,
change authorizations) and transaction-oriented commands (e.g., commit), or call host-
language procedures.
An important issue is when the action part of a trigger executes in relation to the
statement that activated the trigger. For example, a statement that inserts records
into the Students table may activate a trigger that is used to maintain statistics on how
many students younger than 18 are inserted at a time by a typical insert statement.
Depending on exactly what the trigger does, we may want its action to execute before
changes are made to the Students table, or after: a trigger that initializes a variable
used to count the number of qualifying insertions should be executed before, and a
trigger that executes once per qualifying inserted record and increments the variable
should be executed after each record is inserted (because we may want to examine the
values in the new record to determine the action).
SQL: Queries, Programming, Triggers
The examples shown in Figure 5.19, written using Oracle 7 Server syntax for defining
triggers, illustrate the basic concepts behind triggers. (The SQL:1999 syntax for these
triggers is similar; we will see an example using SQL:1999 syntax shortly.) The trigger
called init count initializes a counter variable before every execution of an INSERT
statement that adds tuples to the Students relation. The trigger called incr count
increments the counter for each inserted tuple that satisfies the condition age < 18.
One of the example triggers in Figure 5.19 executes before the activating statement,
and the other example executes after. A trigger can also be scheduled to execute
instead of the activating statement, or in deferred fashion, at the end of the transaction
containing the activating statement, or in asynchronous fashion, as part of a separate
transaction.
The example in Figure 5.19 illustrates another point about trigger execution: A user
must be able to specify whether a trigger is to be executed once per modified record
or once per activating statement. If the action depends on individual changed records,
for example, we have to examine the age field of the inserted Students record to decide
whether to increment the count, the triggering event should be defined to occur for
each modified record; the FOR EACH ROW clause is used to do this. Such a trigger is
called a row-level trigger. On the other hand, the init count trigger is executed just
once per INSERT statement, regardless of the number of records inserted, because we
have omitted the FOR EACH ROW phrase. Such a trigger is called a statement-level
trigger.
CHAPTER 3
In Figure 5.19, the keyword new refers to the newly inserted tuple. If an existing tuple
were modified, the keywords old and new could be used to refer to the values before
and after the modification. The SQL:1999 draft also allows the action part of a trigger
to refer to the set of changed records, rather than just one changed record at a time.
For example, it would be useful to be able to refer to the set of inserted Students
records in a trigger that executes once after the INSERT statement; we could count the
number of inserted records with age < 18 through an SQL query over this set. Such
a trigger is shown in Figure 5.20 and is an alternative to the triggers shown in Figure
5.19.
The definition in Figure 5.20 uses the syntax of the SQL:1999 draft, in order to il-
lustrate the similarities and differences with respect to the syntax used in a typical
current DBMS. The keyword clause NEW TABLE enables us to give a table name (In-
sertedTuples) to the set of newly inserted tuples. The FOR EACH STATEMENT clause
specifies a statement-level trigger and can be omitted because it is the default. This
definition does not have a WHEN clause; if such a clause is included, it follows the FOR
EACH STATEMENT clause, just before the action specification.
The trigger is evaluated once for each SQL statement that inserts tuples into Students,
and inserts a single tuple into a table that contains statistics on modifications to
database tables. The first two fields of the tuple contain constants (identifying the
modified table, Students, and the kind of modifying statement, an INSERT), and the
third field is the number of inserted Students tuples with age < 18. (The trigger in
Figure 5.19 only computes the count; an additional trigger is required to insert the
appropriate tuple into the statistics table.)
Triggers offer a powerful mechanism for dealing with changes to a database, but they
must be used with caution. The effect of a collection of triggers can be very complex,
SQL: Queries, Programming, Triggers
and maintaining an active database can become very difficult. Often, a judicious use
of integrity constraints can replace the use of triggers.
In an active database system, when the DBMS is about to execute a statement that
modifies the database, it checks whether some trigger is activated by the statement. If
so, the DBMS processes the trigger by evaluating its condition part, and then (if the
condition evaluates to true) executing its action part.
If a statement activates more than one trigger, the DBMS typically processes all of
them, in some arbitrary order. An important point is that the execution of the action
part of a trigger could in turn activate another trigger. In particular, the execution of
the action part of a trigger could again activate the same trigger; such triggers are called
recursive triggers. The potential for such chain activations, and the unpredictable
order in which a DBMS processes activated triggers, can make it difficult to understand
the effect of a collection of triggers.
On the other hand, triggers allow us to maintain database integrity in more flexible
ways, as the following examples illustrate.
Suppose that we have a table called Orders with fields itemid, quantity, customerid,
and unitprice. When a customer places an order, the first three field values are
filled in by the user (in this example, a sales clerk). The fourth field’s value can
be obtained from a table called Items, but it is important to include it in the
Orders table to have a complete record of the order, in case the price of the item
is subsequently changed. We can define a trigger to look up this value and include
it in the fourth field of a newly inserted record. In addition to reducing the number
of fields that the clerk has to type in, this trigger eliminates the possibility of an
entry error leading to an inconsistent price in the Orders table.
CHAPTER 3
Continuing with the above example, we may want to perform some additional
actions when an order is received. For example, if the purchase is being charged
to a credit line issued by the company, we may want to check whether the total
cost of the purchase is within the current credit limit. We can use a trigger to do
the check; indeed, we can even use a CHECK constraint. Using a trigger, however,
allows us to implement more sophisticated policies for dealing with purchases that
exceed a credit limit. For instance, we may allow purchases that exceed the limit
by no more than 10% if the customer has dealt with the company for at least a
year, and add the customer to a table of candidates for credit limit increases.
Many potential uses of triggers go beyond integrity maintenance. Triggers can alert
users to unusual events (as reflected in updates to the database). For example, we
may want to check whether a customer placing an order has made enough purchases
in the past month to qualify for an additional discount; if so, the sales clerk must be
informed so that he can tell the customer, and possibly generate additional sales! We
can relay this information by using a trigger that checks recent purchases and prints a
message if the customer qualifies for the discount.
Triggers can generate a log of events to support auditing and security checks. For
example, each time a customer places an order, we can create a record with the cus-
tomer’s id and current credit limit, and insert this record in a customer history table.
Subsequent analysis of this table might suggest candidates for an increased credit limit
(e.g., customers who have never failed to pay a bill on time and who have come within
10% of their credit limit at least three times in the last month).
As the examples in Section 5.12 illustrate, we can use triggers to gather statistics on
table accesses and modifications. Some database systems even use triggers internally
as the basis for managing replicas of relations (Section 21.10.1). Our list of potential
uses of triggers is not exhaustive; for example, triggers have also been considered for
workflow management and enforcing business rules.
A basic SQL query has a SELECT, a FROM, and a WHERE clause. The query answer
is a multiset of tuples. Duplicates in the query result can be removed by using
DISTINCT in the SELECT clause. Relation names in the WHERE clause can be fol-
lowed by a range variable. The output can involve arithmetic or string expressions
over column names and constants and the output columns can be renamed using
AS. SQL provides string pattern matching capabilities through the LIKE operator.
(Section 5.2)