Document
There isn't a catch either. SQL isn't low level machine code that you need a degree
in electronics to understand; it relies on simple English-like syntax, which is simple
to learn and very powerful once you've mastered it. As with all of the Instant
guides, this is for you if you don't want to be treated like an idiot. This book will
take you step-by-step through creating a database, inserting data into a database
and making complex queries of the database in SQL. By the end of the book you'll
have the confidence to create and run your own independent databases in SQL.
We welcome suggestions and take all of them to heart - your input is paramount in
creating the next great Wrox title. Use the reply card inside this book or mail us at:
feedback@[Link]
or
Compuserve 100063, 2152
Joe Celko
All rights reserved. No part of this book may be reproduced, stored in a retrieval
system or transmitted in any form or by any means - electronic, electro static,
mechanical, photocopying, recording or otherwise, without the prior written
permission of the publisher, except in the case of brief quotations embodied in critical
articles or reviews.
The author and publisher have made every effort in the preparation of this book to
ensure the accuracy of the information. However, the information contained in this book
is sold without warranty, either express or implied. Neither the author, Wrox Press nor its
dealers or distributors will be held liable for any damages caused or alleged to be caused
either directly or indirectly by this book.
Published by Wrox Press Ltd. Unit 16,20 James Road, Tyseley, Birmingham, Bl1 2BA
Printed in the USA
Library of Congress Catalog no. 95-60736
ISBN 1-874416-50-8
Trademark Acknowledgements
Wrox has endeavored to provide trademark information about all the companies and
products mentioned in this book by the appropriate use of capitals. However, Wrox
cannot guarantee the accuracy of this information.
Credits
Cover Design
Third Wave
For more information on Third Wave, contact Ross Alderson on 44-121 236 6616
Cover photo supplied by The Image Bank
About the Author
Mr. Celko has been a member of the ANSI X3H2 Database Standards Committee
since 1987. He has had eight regular monthly or biweekly columns in the
past ten years in the computer trade and academic press. His current columns
are "SQL Explorer" in DBMS (M&T Publishing), "Celko on Software" in
COMPUTING (VNB Publications, UK) and "SQL Puzzle" in BOXES & ARROWS
(Frank Sweet Publishing).
Summary of Contents
Introduction ............................................................................................................... I
Chapter 1: The Nature of SQL .............................................................................. 11
Chapter 2: Defining a Database ............................................................................ 43
Chapter 3: SQL Arithmetic, Logic and the NULL .................................................. 81
Chapter 4: Keys and Referential Integrity ............................................................... 113
Chapter 5: Manipulating Database Information ...................................................... 143
Chapter 6: Querying the Database - the SELECT command ..................................... 157
Chapter 7: Joining Tables ....................................................................................... 183
Chapter 8: Grouped Tables and Aggregate Functions ............................................. 211
Chapter 9: Using Subqueries .................................................................................. 235
Chapter 10: Views .................................................................................................. 255
Chapter 11: Embedded SQL .................................................................................... 269
Chapter 12: Optimization and Performance Tips ..................................................... 289
Afterword: W here Do We Go From Here? ................................................................ 313
Appendix A: Conversion Notes for Different Vendor SQLs ........................................ 315
Appendix B: Example Databases ............................................................................ 339
Appendix C: Security and Control in Your Database ................................................ 359
Appendix D: Standards and Quasi-standards Groups Involved with SQL .................. 369
Appendix E: Table of Legal Conversions .................................................................. 373
Table of Contents
Introduction .............................................. I
What Can SQL Do for Me? ................................................... 2
Who Should Use This Book? .............................................................................................. 2
How To Use This Book ................................................................................................... 3
What You Should Know .................................................................................................... 3
Conventions Used ................................................................................................... 3
Program Code ................................................................................................................................... 4
Fonts and Styles .............................................................................................................................. 4
Watcom SQL engine ..................................................................................................... 5
Product Details ................................................................................................................................ 5
Requirements ................................................................................................................................... 5
Installation Instructions ................................................................................................................... 6
Screen Display ................................................................................................................................. 6
Error Trapping ................................................................................................................................... 8
Tell Us W hat You Think ........................................................................................... 8
References ......................................................................................................................... 64
Com ments ................................................................................................................ 66
The Example Database ......................................................................................... 67
Other Table M anipulation Commands ................................................................... 73
Drop Table .......................................................................................................................... 74
Alter Table .......................................................................................................................... 75
Summary .................................................................................................................. 78
xiii
Table of Contents
Xv
Table of Contents
xvii
Table of Contents
xviii
Tb o
xix
Instant
Introduction
Welcome to the latest Instant Guide from Wrox Press, Instant SQL. This
book has been designed to give you, as database developer and end-user, an
edge when designing and querying databases using the powerful features
that SQL has to offer. What is unusual about this book is that Watcom have
provided an actual SQL engine (included on the disk at the back of the
book) with which to query the database.
Please return the reply card at the back of the book and tell us what you
think. We are always interested in what you have to say and are more than
willing to answer your questions on a variety of subjects. We are also open
to constructive criticism (although we do prefer unadulterated adoration!)
Instant SQL
Still not convinced? Well, why should you have to learn another application
from scratch each time the whims of your workplace require you to pick up
another brand new cutting-edge database product, when you can learn one
language and master them all?
You need access to a SQL Server or database allowing you to directly type
in SQL code and interactively return results. You should also have a
working knowledge of the database application you intend to program on as
well. We're giving independent variations of the code used in the Example
Database for Sybase System 10, Oracle 7.1 and Microsoft Access in
Appendix A, but you'll need to know how to access the SQL query window
in each of these applications in order to do this.
Conventions Used
To enable you to find your way around this book easily, we have used
various different styles to highlight contrasting references. Each style has
been selected to enable the reader to successfully understand the content as
efficiently as possible.
3
Instant SQL
Program Code
All programs in the book are highlighted with a gray background so you
can easily find them. Keywords appear in upper-case, whilst variables
provided by the user or the author are stored in lower-case. Here is a
sample selection of code:
In some instances, we define parts of SQL syntax. In this case we will use
the following style, where entries in angle brackets, <>, are to be replaced
by the relevant entity:
Product Details
The runtime version of the database engine doesn't allow ALTER, CALL,
COMMENT, CREATE, DROP, user-defined trigger commands or stored procedures
to be performed. GRANT and REvorE allow you to add new users and
change passwords, but the runtime database engine prevents a user from
changing the permissions on the tables. In addition, to simplify database
administration, the runtime system has an integrated transaction log.
Requirements
Before you install the Watcom SQL runtime product, your system must
adhere to the following requirements:
5
Instant SQL
Installation Instructions
The installation of the software is very simple: it's contained within a self-
extracting archive. Firstly, we must insert Disk 1 and then we can install the
software in any one of three ways:
You can select the Run option from the File menu from the Program
manager window and type in:
A:\ [Link]
You can go to the File Manager, select the A: drive, and then double
click on [Link].
You can go to the Command Prompt DOS window and make sure
that the A: drive is selected. Now type in SETUP..EXE.
This installation program will ask you to create a new directory to place all
the files in and ask you for confirmation that you wish to copy the files
into the new directory. You should follow the on-screen instructions from
now on, and select the options appropriate for your own system. Once the
installation has been completed, you will find a new Watcom SQL 4.0
program group has been created. To run the engine click on the ISQL icon.
If you have any queries click on the comprehensive Watcom SQL manual
icon.
Screen Display
On starting the Watcom SQL engine, you should be greeted with this
startup screen:
Inrduto
wal-m Interadi- SM FF
File Edit Command Window Heip
2AW" ...
.....
....
There are three windows visible in the Interactive tool. In the screenshots
provided throughout the book you will find the following format:
The results or output to the SQL queries will be displayed in the top
Data window.
The execution details, which don't concern us, appear in the middle
Statistics window.
The code to be typed in appears in the bottom Command window.
Whenever you encounter a screenshot, you only need to type in the code
that appears in the Command window. You can press Return at the end of
each line as this doesn't send the message to the SQL engine - it just moves
you down a line. When you wish the statement to be executed, simply click
7
Instant SQL
on the Execute button, next to the Command window. The execution plan
will appear in the Statistics window, and the output will then appear in the
Data window. This should all take a matter of seconds.
Error Trapping
The code in this book has all been extensively tested on a Watcom SQL 4.0
engine, so if you encounter an error message while using the SQL engine
provided, please thoroughly check that you've typed the code in correctly A
punctuation mark in the wrong place can cause an error in SQL. If you
type in the code given throughout the book, you will gradually add to and
update the example database as intended by the text into a full application.
If you don't type in all of the examples, then you may encounter problems
later when certain structures or features haven't been created.
For full details of how you can obtain the full Watcom SQL
4.0 retail version see the information at the back of the book.
The runtime version of Watcom SQL 4.0 accompanying this book doesn't
support the CREATE, ALTER, DROP, CALL or COMMENT commands. This means
that you can't type in the text provided in chapters two to four. The code
necessary for these chapters already exists within the example database. You
can only begin to use the code in the book from Chapter 5 onwards. Happy
typing!
You can help us ensure that our future books are even better by simply
returning the reply card at the back of the book or by contacting us direct
at Wrox. For a quick response, you can also use the following e-mail
addresses:
feedback@[Link]
Compuserve: 100062,2152
Inroucio
9
Chapter
Now imagine that you go to a supply house and simply hand your
shopping list over to the clerk at the order counter. While you wait, he goes
to the warehouse and pulls your order. This is a declarative approach to
shopping. The shopping list is declarative - 'this is what I want!' - and the
clerk is your language processor who figures out how to do it, based on the
information he has about the warehouse, such as a map of the current
locations, the quantity on hand, and whatever else he needs to know to pull
your order.
13
Chapter 1 - The Nature of SQL
Declarative Approach
To carry the store analogy further, if the management at the supermarket re-
arranges the shelves, then your detailed set of shopping directions is useless.
Or worse, you could follow it and bring home a year's supply of boiled
buzzard eggs! However, dealing with pure commodity requests, the supply
house clerk will always be aware of the new shelving arrangement, and
won't get fazed when he gets your list from the order clerk.
SQL Basics
SQL is now so dominant because it has become the standard query
language across the board of database systems. It acts as a bridge between
the user, the database management system, the data tables and the
transactions involving all three. It allows you to formulate queries in
English-like syntax in order to specify exactly what is wanted from the
database. The queries that you make are created in short four or five line
statements, and the answers are quickly returned.
For programmers, SQL can be embedded into any source code, from C to
Pascal. It also allows the creation of total databases and can be used to
manage the consistency of the information contained within those databases.
15
Chapter 1 - The Nature of SQL
To talk politics for a moment, a lot of the blame for the lack of portability is
laid at the door of the standards. The ANSI SQL-89 standards in particular,
failed to address issues such as error codes, and failed to define popular
data types like MoNEY. This criticism isn't totally fair, as the SQL-92 standard
closed many of the pre-existing loopholes. Some of the problems couldn't be
dealt with by the standards as they were matters for the individual
implementation. It might also be said that quite a few of the concepts
specified in the SQL-92 standard have yet to be implemented in any SQL
product.
This book deals heavily with the SQL-92 standard All versions of SQL are
moving towards the standard, however gradually. It's, therefore, desirable
that you should learn the 'correct' way of doing things where possible. The
standards are there not only to provide a uniform way of doing things, but
also a method that will help prevent logical mistakes from occurring within
your database. Hence, the version of SQL on the accompanying disk is the
runtime version of Watcom SQL 4.0. This version of SQL, out of all vendor
versions, is the most compliant with the SQL-92 standard. However, if you
do choose to use your own preferred version of SQL, we won't ignore you!
aSQ
Most vendor products boast 'compatibility' with the ANSI SQL-92 standards
and this is what makes it possible to cover SQL as a 'general' language
concept. The concepts are the same for every product, and these are what
you'll need to learn to program in SQL.
These sets are called tables in SQL, and their elements are called rows.
Rows are made up of columns. You can carry the comparison one stage
further as SQL allows the same mathematical operations on tables, as you
can perform on sets (union and intersect). In fairness, an SQL table isn't
quite a true set, since duplicate rows are allowed. This is a mathematical
structure called a multi-set or bag (the latter term fits our shopping analogy
very nicely).
17
Chapter I - The Nature of SQL
Records Selected
From File:
Start
Selected 1st
Data Reader
Order Of Play
E4 ý, - n-=
Hey.
Jwroxe
IHeyJo
SQL connects the user to the whole database (which is made up of many
tables) all at once. Some SQL products, such as Sybase, use a single file for
the database and manage databases within that file. Some SQL products,
such as Quadbase, use one file for each table. Other products, such as DB2,
put multiple tables in a single disk storage allocation unit. Yet others keep
their data in exotic data structures, such as the compressed bit vectors used
by Nucleus International, which have no direct relation to the original data.
No two SQL vendors have products with the same management approach.
I T and
Going back to the issue of duplicates for a minute, a file treats duplicate
records as different from each other because they are in different locations
within the sequence of the file. SQL will maintain duplicate rows, but can't
easily tell one from the other.
This theme of all at once carries throughout SQL because of the underlying
set model. A query returns a whole result set all at once, rather than part
of the answer a little at a time. All the appropriate rows of a table are
updated, inserted or deleted, or none of the rows are - the whole set
involved in the transaction works, otherwise the whole transaction fails. And
those updates, insertions and deletions occur all at once in the table, not
one record at a time. We will discuss this in more detail later in the book.
The columns of a table are like the fields in a record, except that they are
referenced by name only, never by their relative position. If you re-arrange
the columns in a table, the SQL statements are not affected. If you re-arrange
the fields in a file, all the procedural programs must be changed.
You can write almost any data you like into an open file from a procedural
program in C, FORTRAN, BASIC and so on (COBOL, Pascal and PL/1 can
require files to contain only certain record types). In SQL, you can only
insert, update and delete rows that have the same structure as the rest of
the rows in the table.
19
Chapter 1 - The Nature of SQL
The following row has the correct structure, and could be inserted into the
table:
The following row, however, would be rejected as the structure differs from
the original table structure. SQL would reject putting characters into a
numeric column, and it would also reject putting four columns into a table of
three columns. If there were other constraints specified by you, which the
data in the row did not match, then the row would also be rejected:
20
Rw
Rows
SQL can have a table with zero rows, while procedural languages, or rather
programmers of procedural languages, tend to have trouble with empty files.
The empty table in SQL will still only accept properly constructed rows and
can be used anywhere that a table with rows can be used. This is like the
empty set, but it's empty of a particular kind of row:
X 1 100 Record/Row 1
Y 2 200 Record/Row 2
Z 3 300 Record/Row 3
The order of the fields in a record must match the order of the values in
the READ and WRITE statements of the procedural language. In SQL, there's
a default ordering of columns within a row, but columns are referenced by
name and not by position. The ordering that occurs in a table can be used
as a default, but ordering isn't critical since you can always use explicit
column names.
21
Chapter 1 - The Nature of SQL
Columns
The columns that make up a row in a table are always scalar, or atomic
data types. That is, they are complete in themselves and can't be broken
down into smaller parts.
Columns can have constraints placed on them when the table is created.
These constraints are not part of any program, but are part of the database
itself. Conversely, files would have to depend on procedural code in
application programs to enforce any such rules.
The cells of a spreadsheet can hold procedures as well as data. You usually
only see displayed the results of the procedure. A column in a table can
hold only data.
22
Su
Programmers and users of the database are usually restricted in their access
to these design commands. You don't want someone changing the structure of
the database while other people are using it - it would be like living in a
house where someone could remove your bedroom floor without telling you!
The first section of this book will deal in some detail with
creating a database vou'll find that many introductory SOL
books gloss over the DDL, launching straight into the SELECT
statement syntax. Whilst this is a good way to get quickly into
using SQL, it misses out a very important part of the language.
This book is not aimed just at the programmer who wants to
query existing data, but also at the administrator who has to
set up a database. We're sure that programmers will find the
following section useful too, as they often design or alter tables
in real projects. Besides, having DBA skills makes you more
promotable!
23
Chapter 1 - The Nature of SQL
The main basis for manipulating data is the querying structure, the SELECT...
FROM clause. It works in a logical English manner. You specify what it is you
want from the database, and from which area, and the query is returned to
you in tabular form.
Let's have a look at a purely hypothetical case. Here's a jobs database, with
a table for each type of job:
If you wanted to see the details of all the jobs working on the railroads
that paid over $5 per hour, you could phrase the SELECT statement needed
to get that output in English terminology. You'd roughly say "SELECT jobs
paying over $5 an hour FROM the table of railroad job". You'd get a table
looking like the following:
Results Table
24
Su
The power of SQL's DML structure is that you could format your original
command to change the sequence in which the answer is displayed. You
could compare the wages of the manager with the wages of managers in
other industries using just a few simple statements.
SQL programmers can easily spend 90% of their SQL programming time
doing queries. These days, insertions are usually handled by an input
program which was built with a front-screen painter. Deletions are usually
handled by a batch program or another program built with a screen painter.
But it takes a programmer to design a clever query for a report or an ad hoc
request! We will spend the bulk of this book talking about queries, but please
don't think that this is all there is to SQL
In some cases, it's the DBA who's in charge of security in a database, while
in other cases it's a separate job function for a security officer. The DCL in
SQL-89 was a weak language, and large companies usually add other
security products to their database. SQL-92 is somewhat better, but at the cost
of more complexity. No product has yet implemented all of this particular
standard.
Real security buffs may want to have ways to absolutely deny certain
people access to certain data, set up levels of security (public, confidential,
secret, top secret), provide encryption or even set up false information.
We won't look at the Data Control Language in detail at this point because
it is dealt with in Appendix C. There we offer some thoughts on how to set
up different levels of authorization.
25
Chapter I - The Nature of SQL
Transaction Control
A transaction in SQL is either completely finished or not done at all; there's
no way to do only part of the work. This is the principle of working with
sets again! The work can be committed, which means it's made a permanent
part of the database, or it can be rolled back, which means the database is
restored to the state it was in before the transaction began. Transaction
control is the part of the system that makes sure that the database is in a
valid state when a transaction is over - the stuff that needed updating is
updated, the stuff that needed deleting is deleted, the housekeeping is all in
place.
The part of transaction control that the SQL programmer needs to be aware
of is concurrency control, or transaction levels. The programmer has to set
these levels in his program, so it's the only part of transaction control that
he sees. These levels determine how the database is shared among
transactions - does one transaction get exclusive use of some objects, or can
other transactions change them while the first transaction is still running?
Integrity
Integrity is where most people will start turning off. It's an obscure term
which doesn't mean much, and when you see it in a book, your eyes begin
to glaze. Nevertheless, it's essential to the creation of any database that's
going to work. What does it mean? Well, its use is similar to the semantic
meaning of the word itself. A database has to have integrity before you can
trust the information inside it. Why shouldn't there be integrity in a
database? Surely, the user inputting the information isn't going to deliberately
or knowingly put false information into it? That's exactly the problem. They
can unwittingly corrupt the database at anytime.
Consider the Jobs example we looked at earlier. Imagine now, that two
people are involved in interviewing applicants for the one post. They both
have the same access to the database, but from different views. One view
table could be a global list of available jobs, and the other one a table of
local jobs. The first manager likes his applicant, offers him the job and takes
the job off the global table. However, it's still on the local table. Before he
can contact the other manager to tell him to stop interviewing, the other
26
S Ts
manager sees the job is still available on the database on the local table and
offers it to his applicant. Two people have the same job, down to the
database not having what is known as referential integrity.
If the database had referential integrity, it would mean that when the job was
removed from the global table, all the references to that job in other lists
would also be removed. SQL has a whole host of features to deal with
exactly these types of problems. These are constraints, which when properly
programmed, stop the users from making these scenarios into real problems.
The interactive tool is the best tool to use when you're learning SQL, fixing
a database or trying to be sure that a query will work before it goes into
production. They're not too hard to learn, since they're very much like other
27
Chapter 1 - The Nature of SQL
28
L n
Remember not to press Return or select OK until you've entered all the
information. If all goes well then you should be presented with a screen of
blank forms awaiting your instructions.
At this point, you might want to stop reading this book and
try to learn the basics of your interactive SQL tool. Just scroll
around and see where the buttons are located, so you can use
it in the upcoming chapters.
29
Chapter 1 - The Nature of SQL
In the next chapter we'll start writing SQL code, but first we'll give you a
brief description of the example database that we'll work with throughout
this book.
We could include a column for sales commission but this can be calculated
from the existing columns.
This is just a brief outline of what we may want to include in our example
database. A full description of the business problem could run to several
pages and could include many more tables and columns.
30
S t
Finally, we'll have a look at the type of syntax and grammar we'll be using
throughout this book.
Syntax Awareness
If you already know about the use of grammar in defining
computer syntax then you can skip this section and move on
to the summary at the end of the chapter.
Grammar is made from just two parts: terminals and non-terminals. Think of
it as being like a subway system. Imagine you want to get from the center
of town to the end of the line. Each station you go through is a non-
terminal, because you can go beyond it to the next station. Each station is
just a point on your journey. You want to get to the terminal at the end of
your route, and this corresponds to the keyword. You don't give a hoot for
the stations you're passing through - the same goes for the non-terminals. You
only need to know the code you're going to type in 'at the end of the day'.
The whole structure we're creating is termed a construct. Let's have a look
at the different styles and objects you'll encounter in the syntax.
31
Chapter 1 - The Nature of SQL
Terminal
KEYWORD
A keyword is what you can type in directly to the SQL engine. Terminals
can also be punctuation, such as commas, which is a vital part of syntax.
Missed commas and semi-colons can prevent an otherwise correct query from
functioning.
Non-Terminal
<expression name>
A non-terminal is an expression that can be decomposed (further expanded)
into expressions and/or keywords. The aim is to get a final set of keywords
which you can type in SQL.
OR
The '1' character is used to divide text up and it represents the English word
or. It represents the different options open to the SQL engine from one
specific point. You can only take one of these paths.
Optional Section
An optional extra to the construct is surrounded by square brackets. It can
contain any number of keywords and/or expressions. You only use this if
you have a part of the language that can be used but doesn't have to be. In
the English language an example might be a noun and a noun with an
adjective. A noun could be THE CAT, a noun with an adjective, THE BIG
CAT. The noun doesn't have to take an adjective, it just adds to the
description. The adjective in this case would be an optional extra encased
by the brackets:
[<adjective>] <noun>
32
S a
In SQL you can test data that goes into the database but you don't have to
- the test is optional. You decide whether you want to use it. If you come
across this in the SQL syntax, you know that you can add this code to a
definition but you don't have to. We might advise that you do it, but the
code will function just fine without it.
Definition
Example of Syntax
Instead of computer syntax we'll use an English language example. It's
much easier to relate to, and it functions in the same way in SQL, as SQL
has an 'English-like' feel. But beware, we're going to create our own
definition of the English language, so we can relate it to computer syntax.
It's going to be vastly simplified. In our version of the English language,
every sentence can be created from a noun, followed by a verb, followed by
a noun. We will decide that A and THE (the indefinite and definite articles)
are going to be included in our description of a noun. Therefore, a noun
might be THE CAT or A DOG. We're also going to allow adjectives in this
expression, but only as optional extras. The whole kitten caboodle now
reads A [<adjective>] <noun> or THE [<adjective>] noun shall be known
as a <noun expression>. A verb can be made only from a verb word
(there are no modifying prepositions or the like). This is the only way to
cut down on the sheer volume of sentences that could be created by the
syntax.
33
Chapter 1 - The Nature of SQL
In this example we'll now try to discover if A DOG BIT THE STUPID MAN is a
legal English sentence, according to our definition of syntax. Each
<expression> could be further decomposed. If you reach a keyword, this is
where you end. You then backtrack to the last <expression> you looked at.
You would then check to see if you had finished all of it, or if there were
any more expressions to expand to keywords. Otherwise you would move
on to the next expression.
Let's start with a sentence structure - A DOG BIT THE STUPID MAN is your
final target. Look up the first rule, which is that a <sentence> is made up
from <noun expression> followed by a <verb expression>, followed by a
<noun expression>. Break it up into a tree-like structure as you go along.
Start with the leftmost expression, the first one in the list. <noun
expression> can be expanded to one of two things:
A [<adjective>]<noun> or THE [<adjective>]<noun>. The sentence starts
with A so you use the former breakdown of <noun expression>.
34
Syta
I-
A is left on one side as this is now a keyword, however you still need to
expand the rest of the expression. You are left with [<adjective>] <noun>.
Intuition tells you immediately that the next word DOG is a noun. As the
adjective is only optional, you can ignore it and jump straight to the noun.
(The SQL engine is able to do the equivalent.) If you were to throw in two
command keywords, the equivalent of putting two verbs together say, the
engine would recognize this as incorrect syntax. What it wouldn't be able to
do is tell you whether the way the language is used is illogical. A sentence
such as THE SKY BIT THE DOG makes no sense if you take the words
literally but it is 'grammatically' correct. However, this doesn't interest you
because all you want to know is whether or not your statement will be
accepted by the SQL engine.
35
Chapter 1 - The Nature of SQL
We're left finally with the <noun> part to expand. This is expanded to the
word mAm to fit our statement. This has completed the syntax definition of a
statement and also completed our statement. Therefore, the statement has
been successfully parsed.
36
m0
We have now proved via our syntax that this is a legal statement. Fine, but
what has this to do with SQL? This is what the SQL engine will do (more
or less) when presented with an SQL statement such as SELECT
thingamajigs FROM wherever. Each statement in SQL has its own syntax
for determining whether the statement is or isn't valid. The CREATE TABLE
statement will have one syntax, the SELECT statement will have another.
This is equivalent to the <noun expression> having its own syntax, and the
<verb expression> having another. Everything goes back to the
<sentence>. This is equivalent to what you type in at the command line.
SQL will break down the syntax and label the different parts of the code.
You have just broken down the different parts of the sentence and labeled
them.
OK, now you might still wonder why this is relevant to the book. Why
can't you just get some example code and type it in? Well, you can, but in
this way with the syntax you can create your own example code and know
that it's syntactically correct. Instead of giving you 10 million examples of
what is right and what is wrong, you'll get a working knowledge of SQL
with the syntax and a couple of examples. SQL is quite different across the
different vendor products. What doesn't differ much is the syntax. It's of
much more use to you to learn SQL generally so that your knowledge is
37
Chapter 1 - The Nature of SQL
transferable across vendor products. We could give you example code for
only one product, and you may find you'll type it in on your product and
when it doesn't work you'll be none the wiser. With the syntax you'll be
able to create general SQL code, so if your product has a different keyword
or slightly different way of doing things, you'll be able to just change the
'wording' of things rather than the whole structure. For example, a function
for converting one data type to another is CAST () in one language but
CONVERTTO() in another yet the working is the same.
If we were to use the sentence THE DOG BIT MN, then it would have a
different effect upon our syntax. You would create the tree structure up to
the point where you have just expanded the verb to the word BIT:
38
I Exrie
Exercises
1 Would the following sentence A MAN BIT THE txm be legal in our
first syntax?
2 Would the sentence A MAN BIT THE DOG sTUPID be legal in our
first syntax?
39
Chapter 1 - The Nature of SQL
Summary
We've covered most of the basic structure of SQL in this chapter.
We've introduced you to the standards and mentioned how they affect SQL
in general, and how they affect this book.
We looked at how SQL can be logically subdivided into the three SQL 'sub-
languages': DDL, DML and DCL. We've also skimmed very quickly over
transaction control and integrity both of which we deal with in far greater
detail in later chapters.
Finally, we gave you a quick primer to computer syntax and the style of
syntax we will be using throughout this book.
In the next chapter we'll see how you set up a database using SQL code.
40
I Sumr
41
Chapter
Defining a Database
The creation of databases is one of the most powerful uses of SQL. It isn't
as simple, however, as merely setting up a table with the relevant column
and row names. There are many different things you will need to think
about. You will need to get to grips with thinking in 'sets' and agreeing
with SQL's constraints and data types. You may be familiar with setup
routines in SQL Access or dBase, whereas we will outline the basis for
safely distributing your data universally This will also ensure a secure
interrogation path in the future.
M
m The underlying model upon which SQL is based
m Table terminology
Creation of schema and tables
Data types and constraints
m Other table manipulation commands
Chapter 2 - Defining a Database
Let's start with the basics. Returned sequential files consist of records that
are ordered sequentially within the file. There is a first record when you
open a file, a series of next records as you use the data and a last record
to raise the end of file condition. You navigate these records and perform
actions one record at a time. These files model how people would handle
real-world paper forms.
RECORD 3
RECORD 2 !i!••iiiii!i!i•!
!!•ii
i¸i•!iii
RECORD I
44
I
On the other hand, sets are those mathematical abstractions you studied in
he
school. Sets are not ordered and their members are all of the same type.
When you do an operation on a set, the action happens 'all at once' and to
the entire membership. That is, if you ask for the subset of odd numbers
from the set of positive integers, you get all of them back as a set. I don't
build the set of odd numbers one element at a time. You define odd
numbers with a rule: 'if remainder is one when you divide the number by
two, it's odd'. This could test any integer and classify it.
SQL isn't a perfect set language any more than FORTRAN is a perfect
algebraic language, as we'll see, but when in doubt about something in SQL,
ask yourself how you would specify it in terms of sets and you'll probably
get the right answer.
The advantage of having only one data structure is that the results of all
operations are also tables - you never have to convert structures, write
special operators or deal with any irregularity in the language.
45
Chapter 2 - Defining a Database
SQL-92 is more orthogonal than the old SQL-89 standard. (That is, the
language allows you to nest expressions inside each other, but you should
always try to do so carefully so as to avoid undue complexity.) You can use
a result table with one value where you use a scalar value, a result table
with one row where you use a row literal and a result table with one
column where you could use a column expression.
Row 1
Row 2
If, of course, you're one of those people who take delight in the internal
particulars of how things are stored and organized, then it's possible to
46
Cocpta
specify some of the storage characteristics. Really that isn't what SQL is
used for and defeats a lot of its purpose. The ways things are stored is
individual to each package; use the manual for your own vendor product.
This is really only of use to a DBA (Database Administrator).
Fields within a file are defined by the program, while SQL defines its
columns in the schema. If you are a FORTRAN programmer, you use the
FORTRAN FORMAT statement, a COBOL programmer uses a Data Division and
"aC programmer uses the order that the variable names appear in the list of
"aRead statement.
You can reference the same data by a different name in each program. If
the file layout changes, you must rewrite all the programs which use that
file. When a file is empty, it looks exactly like all other empty files. When
you try to read an empty file, the EOF flag pops up and the program takes
some action.
Column names and data types in a table are defined within the database
schema. Within reasonable limits, the tables can be changed without the
knowledge of the host programs. The host program only worries about
transferring the values to its own variables from the database. Remember,
the empty set from your high school math class? It is still a valid set. When
a table is empty, it still has columns, but zero rows. There is no EOF flag to
signal an exception.
47
Chapter 2 - Defining a Database
A table isn't a spreadsheet, even though they look alike when you view
them on a screen or in a printout. You can access a row, a column, a cell
or collection of cells in a spreadsheet by navigating with a cursor. Also, in
terms of a spreadsheet, the position of a cell matters, whereas a table has
no concept of navigation.
Cells in a spreadsheet can also store instructions and not just data. There is
no real difference between a row and column in a spreadsheet, as you
could flip them around completely and still get valid results. In SQL, rows
and columns are different things.
Table Terminology
SQL tables have their own terminology, but there isn't very much to learn
and what there is, is quite simple to understand. We will use the following
table to explain some of the terminology:
Row I
Row 2
Cardinality of a Table
This is simply the number of rows in a table and can be zero or greater.
The example table has a cardinality of 2.
Degree of a Table
This is the number of columns in a table and must be one or greater. The
example table has a degree of 4.
48
-b
L Termnolog
The rules of this table are that you can convert numerics to numerics of
higher precision and character strings to longer character strings. Also,
character strings which are made up of numeric characters and legal
punctuation can be converted into numerics. Likewise, character strings
which hold an ISO (International Standards Organization) display date or
time string can be converted into temporal (time/date) data types.
Essentially, they have the same structure and could have come from the
same table.
Manipulating Tables
The three basic table statements in the SQL Data Definition Language (DDL)
are CREATE TABLE, DROP TABLE and ALTER TABLE. They pretty much do
what you would think they do from their names.
49
Chapter 2 - Defining a Database
The table name must be unique in the schema and the columns' names
must be unique within a table. The names in SQL can consist of letters,
underscores and numbers, and they can have other characters or space
in them if you enclose them in double quote marks. A table must have
at least one column.
2 SQL can handle a table and column with the same name, but it is a
good practice to give tables plural or collective nouns for names and use
singular attribute names for columns. This helps you think of tables as
sets and columns as parts of a row. For example, don't name a table
'Employee' and then have a column for the employee identifier also
called 'employee'. Instead, use something like 'Personnel' or even
'Employees' for the table name.
3 It is also a good idea to use the same name for the same attribute or
thing in different tables. That is, don't name a column 'sex' in one table
and 'gender' in another when they refer to the same attribute.
50
Cr
But if that rule is enforced in the database, then none of the applications
need to repeat the validation in their code. A change at the database level
will find all the data that doesn't meet the new rule and none of the
application programs will have to change.
Another advantage is that if the rules are in the schema, then the database
system can use them to decide how to allocate storage and optimize
queries. Many of these rules are stated as SQL search conditions and can be
used directly in the query processor.
<table definition> =
CREATE TABLE <table name> (<table element list>);
We'll start with a piece of SQL code that you can type straight in. The code
creates a table called Example which has three columns. The structure is
empty and can't be viewed as such. However you need to know how we
got there via the syntax, so we'll walk you through how to create the code
from the basic syntax. To define a table we need the first clause in the
command definition above.
51
Chapter 2 - Defining a Database
Each table element can be reduced, according to the syntax above, to the
following:
Any time you see a name in an expression such as <table name>, that means
you can name the object yourself. As we're creating an example table, it's
best to call the table 'Example'. We wish to have three columns, so we need
three elements in the table which correspond to the columns.
Each table element is reduced to the column definition (whether the column
contains integers or characters or dates) and rules for all data that goes into
52
- oum
the table. The rules for handling that data might typically be 'only allow a
row to be inserted into the table if the phone number and fax number of
that row are not the same number'. These rules are known as table constraint
definitions. They are methods of checking whether data that's going into a
table is correct or valid. This means that a table acts more like an object
(with its data and methods) than just a simple, passive file.
Column Definitions
Beginners to SQL programming often fail to take full advantage of the
options available to them, and they pay for it with errors or extra work in
their applications. Saving yourself time and trouble begins with column
definitions. The syntax is as follows:
The first important thing to notice here is that each column must have a
data type. There are also no array, pointers or data structure declarations in
SQL; everything is a simple scalar or atomic value. This means that a
column definition must be at least made up from a <column name> and the
<data type> of the that column. Optionally you can add a default and a
constraint to the column, but they aren't essential to the working of the table
structure. In its simplest form you could use this definition, where you need
only a column name and the data type of that column.
We'll look at default clauses and column constraints in detail a little later in
the chapter, but an example using them would look like this:
53
Chapter 2 - Defining a Database
You can mix columns freely whereby some will have constraints and defaults
and others won't have anything at all, apart from the basics. There are
several different types of column constraint, all which require their own
syntax. We will worry about those later. First, we must define the different
possible data types.
M NUMERIC
AD CHARACTER
go TEMPORAL
There are also vendor data types - the most common is MONEY. This is really
a DECIMAL or NUMERIC data type which also accepts and displays currency
symbols in input and output. Another one is BLOBS (those cursed picture
files), which are replaced in the SQL-92 standard with the BIT and BIT
VARYING data types. Products that have BLOBS usually have functions that
can use them, while the standard is silent on what to do with them.
Numeric Types
The SQL standard has a very wide range of numeric types. The idea is that
any host language can find an SQL numeric type which matches one of its
own.
54
Dt
Type
Exact Numerics
An exact numeric value has a precision F and a scale s. The precision is a
positive integer that determines the number of significant digits in a
particular radix or base (you know from school, binary is base 2 and so
on). The standard says the radix can be either binary or decimal, so you
need to know what your implementation provides. The scale is a non-
negative integer which tells you how many decimal places the number has.
An integer has a scale of zero. The data types NUMERIC, DECIMAL, INTEGER
and SMALL INT are exact numeric types. DECIMAL(P, S) can also be written
DEC (P, s) and INTEGER can be abbreviated INT. For example, DECIMAL
(8,2) could be used to hold the number 123456.78, which has eight
significant digits and two decimal places.
Approximate Numerics
An approximate numeric value consists of a mantissa and an exponent. The
mantissa is a signed numeric value, and the exponent is a signed integer that
specifies the magnitude of the mantissa. An approximate numeric value has a
precision. The precision is a positive integer which specifies the number of
significant binary digits in the mantissa. The value of an approximate
55
Chapter 2 - Defining a Database
In recent years, the IEEE (Institute of Electrical and Electronic Engineers) introduced
a floating point standard which can work well with SQL. As more vendors
adopt it, query results will become more uniform across platforms. The IEEE
floating point standard also has certain bit configurations called NaNs (Not a
Number) to represent overflow, underftow, errors and missing values which
provide a way to implement NULS as well as to capture errors.
Character Types
SQL-89 defined a CHARACTER(n) or CHAR(n) data type, which represented a
fixed length string of (n) printable characters, where (n) is always greater
than one - you can't declare a column to have an empty string. Some
implementations allow the string to contain control characters, but this is a
rare case. The characters which are allowed are usually drawn from ASCII
or EBCDIC character sets and most often use the order defined by these
standards for sorting.
56
rDt
Types
L
The standard is silent on how dates and times are stored internally in the
database, so products do it very differently. Some use a character string,
some use a large number which represents fractional seconds from a starting
date and there are variations on those approaches. The important thing is
that the programmer doesn't care, as he or she never handles the internal
date format in SQL.
All SQL implementations have a DATE data type and most have a TIME and
a TIMESTAMP data type.
These values are drawn from the system clock and are therefore local to the
host machine, which betrays the more global approach of SQL-92. They are
based on what is now called the 'Common Era' calendar, also called the
57
Chapter 2 - Defining a Database
The SQL-92 standard also has a full set of operators for temporal data
types. The full syntax and functionality has not been implemented in any
SQL product yet, but you can use some of the vendor extensions to get
around a lot of problems in most existing SQL implementations today
Back to the column definition, the next clause after a data-type in the <table
definition> construct is the default clause.
Whenever the system doesn't have an explicit value to put into a column, it
will look for its DEFAULT clause and insert that value. An example of use of
a default value would be in a table of employees with credit limits. Those,
who hadn't had their credit limits extended and didn't know their credit
limit, would have a minimum rating of $500. The default value would
therefore be 500. The <default option> can be a literal value of the
appropriate data type, such as 1 or 999 or John, or something provided by
the system, such as the current timestamp, the current user identifier, and
so on. If you don't provide a DEFAULT clause, then the definition acts as if
you had declared it DEFAULT NULL and the value NULL would be assigned to
that column. This is because the NULL value is the default value of DEFAULT.
58
Co
Actual Code Syntax Version
This is a good way to make the database do a lot of work that you would
otherwise have to code into all the application programs. The most common
tricks are to use a zero in numeric columns, a string to encode a missing
value ('unknown') or true default ('same address') in character columns, and
the system timestamp to mark transactions.
Column Constraints
There are four different types of column constraints that can be applied. You
can use any single one, or all of them independently in conjunction with the
NOT NULL constraint.
59
Chapter 2 - Defining a Database
In fact, it's not that difficult to master them. Not only is it better practice to
do as much validation at application level, but it makes it easier for you. If
your application rejects the data that's input, it'll tell you from which
column the item was rejected and you should be able to figure out which
row as well and, therefore, which item was the offending one. The
alternative is having to trawl the error code from the database which
doesn't always refer to the point the error occurred and will typically be in
computeresque jargon such as 'Error Code 25987'.
In the short term this is more work for you, but in the long term it will
save you effort. The database shouldn't have to do all your work. If
possible work out the rules on paper beforehand. Look at the columns. Can
you find a way of preventing erroneous data going in?
For example, if you were storing data about credit cards for accountancy
purposes, and you had a column of expiry dates, you could prevent
incorrect data being entered into this column by stopping dates earlier than
the current date and later than five years on from the current date (as most
cards are only valid for five years) from being input.
Imagine at the same time, the flipside. What happens if you let an incorrect
date go in for 1986 instead of 1996? Now, say you have an automatic aging
system which checks by today's date and says this card is invalid as from
today How can it pick up one from 1986? It can't.
60
Column
Constraints
The table requires an entry for each column: one for the customer name,
one for the address and one for a phone number. You can enter the
customer's name into the custname column, the customer's address into
customer address, but if you didn't know the phone number, you could
leave that blank. The database would automatically insert a NULL value into
that column, as it doesn't mean that there isn't a phone number, just that it
isn't yet known:
61
Chapter 2 - Defining a Database
The NOT NULL constraint will help you avoid the complications of NULL
values when you make queries against the data. We will look at some of
the implications of the dreaded NULL in the next chapter.
Check() Constraint
The CHECK() constraint is under-used, even by experienced SQL
programmers. It tests the rows of the table against a logical expression, which
SQL calls a search condition, and rejects rows that don't pass the test. Here
is its syntax:
The CHECK above would simply stop a value above 5000 being entered in
the creditlim column. The usual technique is to do simple range checking
such as CHECK (rating BETWEEN 1 AND 10) or verify that a column's value
is in an enumerated set such as CHECK sex IN ('M', ' F') with this
constraint.
62
Co
We will look at the BETWEEN and IN predicates in Chapter 6.
The real power comes when writing complex expressions, which verify
relationships with other rows, other tables or with system values. For
example, you can use a single check clause to enforce the rule that we don't
hire anyone under 21 years of age for a job that requires a liquor serving
license by checking their birth date, the current system date and the job
requirements table.
File system programmers will understand the concept of a PRIMARY KEY, but
unfortunately for the wrong reasons. They are used to a file which can have
only one key because that key is used to determine the physical order of
the records within the file. However, in SQL there is no sequential order in a
table; the term PRIMARY KEY has to do with distinguishing rows in a table
and making sure they are not repeated elsewhere.
The PRIMARY key might be the customer identifier (custid), which would
uniquely identify all the different customers who had the same names.
63
Chapter 2 - Defining a Database
unless you explicitly add a NOT NULL constraint. Adding the NOT NULL
whenever possible is a good idea.
The truth is that not many implementations of SQL follow the ANSI syntax
for <unique specification> yet. Instead, you first create the table, then
have to use a separate statement which also varies from product to product,
but probably looks like CREATE UNIQUE INDEX <index name> ON
<table> (<column list>). File system programmers don't tend to mind this
separate structure, since it is how they have always done it.
References
The references specification is the simplest version of a referential constraint
definition - it can be quite tricky. For now, let's consider the simplest case:
64
Cu
This relates two tables together, so it's different from the other clauses we
have discussed so far. What this says is that if the value is in the column
of the referencing table, call it table B, then it must either be NULL or that
same value must appear somewhere in the table A's (the referenced table)
column named in the constraint. So table A must match logically to table B.
$500 2 1001
$1000 4 1002
This means that the customer identification number 1001 in TABLE A, must
match the customer identification number in TABLE B, and that the John
Doe who lives in New York must be the same guy who has a credit limit
of $500. Furthermore, the referenced column must have a <unique
specification> on it. Accepting the NULL is another example of the 'benefit
of doubt' principle in SQL.
For our example code we're going to create these two tables so that one
references the other. The first table is a list of customers with their details,
the second is a list of credit limits. For each credit limit there could be a
number of customers who have that credit limit. If we put a value into the
table CreditLimitl under custid then we must ensure that there is already
a customer with that id number. This is why we will use the RzFERNCE•S
constraint, it enforces this. The credit limit is the unique item in this table.
The table that does the referencing (TABLE B in this example) must always
be created second as you can't make a reference to a table (TABLE A) that
doesn't yet exist.
65
Chapter 2 - Defining a Database
Comments
Some versions of SQL also have ways of attaching a comment to each
column of a table. This is often done with a special CozMMNT statement, as
in DB2. What's actually happening is that the comments are placed in a
66
Eaml
Databae
special schema table where users can get to them as a form of on-line
documentation. Commenting isn't standard, but it's a good way to document
your database and you should do it if your product has the facility to do
SO.
You will also see some SQL products which use the C language convention
of '/*' and '*/', the pairs of comment brackets. The actual comment
convention in SQL is taken from the ADA programming language; you put
comment text between a double dash and a new line. These comments are
like the comments you would see in a program - they are part of the
source code and are not available to the user.
The information we need about our salespersons includes their names, the
rank they hold (junior salesman, senior salesman, manager), their salaries
and their commissions. The choice of data types for some of these attributes
is obvious - a person's name is going to be CHAR and salaries are going to
be DECiMAL.
Chapter 2 - Defining a Database
But how big should we make them? The answer is that it depends on the
project itself. The United States Postal Service and most label manufacturers
say that the name line in an address should be 30 characters long. But if
we're only storing their last name, this is probably a bit much.
Commissions are going to be computed from the sales that each salesperson
makes. If you put a column in the salespersons table for their commission,
then you'll have to change that value every time they make a sale and
every time merchandise is returned. Does the column hold the total
commissions earned, or just this month's commissions? How do I compare
one month's commissions against another when I have only one column?
As you can see, we shouldn't have a column for commissions in the table
at all. It's a calculated value, which can have a lot of definitions. We'll have
to construct it as needed, using whatever definition applies.
Customer related:
Employee related:
68
Eam6
Order related:
This isn't even close to a complete description of the business problem or the
database, but it does get us started. We wouldn't advise using those long
phrases for column names in the final database, but these data items should
be documented somewhere, along with any information you have on them
and the rules for maintaining them.
Let's have a first crack at the tables for the sale support database.
So let's not type it in straight away but firstly consider what a CREATE
TABLE definition might look like. Starting with a table for the salespersons,
we might declare that table to be as follows:
69
Chapter 2 - Defining a Database
This would effectively create a table with four columns. The table can't be
viewed, as it doesn't yet contain anything. All that exists are the column
headings along with their legal data types and constraints:
Row 1
Row 2
Now inspect each column declaration. Is there any good reason for
removing the MOT NULL constraints from a column? No, probably not.
Should we add some defaults to any column? The lowest salary we pay
anyone is $100 per week and the lowest rank they can hold is class one
salesman. Let's add those:
Should we add some check constraints to any column? The salary has to be
a positive amount and we only use three ranks. Let's add those:
70
Example
Notice that the CHECK () clause doesn't automatically imply that there can't
be NULL - remember the rule that lets a TRUE or UKwNowN result pass the
test.
Do we have a primary key? Yes, that's why we created the empid column, to
be sure we can find each individual employee. So here is the final
declaration:
Technically, we could remove the NOT NULL constraint from the empid
column since it is redundant. But we prefer to leave it so that it will still
be there if we alter the table and change the primary key. Now it's ready to
type in.
If you typed the table declaration earlier, then this one won't
work. Once a table has been declared, it can't be re-declared.
If you have entered it all correctly, then you should get no
response from the compiler. Hence, each of our examples has
been numbered progressively. We'll learn how to remove excess
or old tables at the end of this chapter.
Some SQLs will let you know that the query has been processed, but some
will give no indication whatsoever. You can't actually view the table, as it is
empty. However, you are able to view the table headings and this should
reassure you that the table exists! The command you will need to do this
has not been covered yet, and it won't be until Chapter 6. It will simply
display the whole contents of the table.
Use this at any time to display any table simply by substituting the name
of the relevant table you wish to look at for salespersons. In this case, the
column headings enpid, ename, rank and salary should be all that are
displayed.
71
Chapter 2 - Defining a Database
If you got an error message when you typed in the CREATE TABLE definition
(and you typed the code in correctly), then this will have been caused by
your specific vendor project. The problem might be that your version of SQL
doesn't support DECIMAL data types. Oracle has its own NUMBER data type, as
it uses its own internal storage mechanism. If you have problems with
DECIMAL then refer to your manual. At a push you could substitute it for
INTEGER, but this isn't very satisfactory
Also, if you are using your interactive SQL tool, then it may not support
DEFAULT values. If this is so, then you will have to remove them. Some
versions of SQL support DEFAULT values, but only for specific data types,
which means you can't assign them to a number. In these versions, the
defaults for any one data type will be assigned automatically to one value.
For example, the default value of the INTEGER data type would always be
zero and the cHAR would be the empty string.
Assuming that salespersons table has now been created successfully, using
the same line of reasoning, let's construct the other tables in the schema:
The partid is an arbitrary number used to identify the parts. The stockqty
is the number of units we have in stock right now - if it is negative, that
means we have backordered parts. The reorderpnt is the reorder point -
when the inventory level drops to this number or less, we place an order
for more of that part. Notice that we have let it go NULL. This will handle
the case where we don't want to reorder a particular part, and so it is no
longer possible to obtain it. Can you see why zero or some dummy value
72
Example
Database
L I
wouldn't work? (Hint - what value would you use if you had run out of a
particular part and did want to reorder it?)
We are allowing the customer to order up to five items and make sure that
the partid is something in inventory This is a copy of a paper or video
terminal order form. The first columns in the table are the header at the
top of the page, and items and quantity fields are the last ten columns.
73
Chapter 2 - Defining a Database
Vendors will often have other commands or clauses which relate to the
physical storage of tables in the database. There is no general advice as to
how to use them, you need to take a vendor training course for that.
However, there are two other actions you can perform on a table in any SQL
product. You can drop a table from the database and alter the structure of
the table. Let's go into the details.
Drop Table
The DROP TABLE statement gets rid of a table. This is primarily used for
removing old or unwanted tables. There is no method of recovery of a table
once it has been dropped. It is important to remember that this is not the
same as making the table an empty table. When a schema object is dropped
it is gone forever, together with any data belonging to it. According to the
SQL-92 standard, the syntax of the statement is as follows:
This would remove the table Customer. Don't type it in, unless you want to
re-create the table from scratch! However, if you are typing in the examples
from the example database and you wish to type each one in as it is
amended, simply type DROP TABLE <table name> to get rid of the old version
74
D
of the table and then you can use the CREATE TABLE command to redefine
the new amendment of that table. As we said earlier, you can't re-declare a
table once it has been created, you can only ALTER TABLE or DROP TABLE.
The SQL-89 standard was silent concerning what happened when you
dropped a table. Either the particular SQL product would post an error
message and deal with the problem, or you would find out about any
references to the offending table by having your database blow up when it
ran into constructs which needed the missing table.
The DROP keyword and <drop behavior> clause are also used in other
statements which remove schema objects, such as DROP VIEW, DROP SCHEMA, DROP
CONSTRAINT and so on.
Alter Table
The ALTER TABLE command adds, removes or changes columns in a
particular table. It gives you a chance to re-do your schema after you
already have a database. This wasn't part of the SQL-89 standard, but most
SQL products had some form of it, with slightly varying power and syntax.
In SQL-92, the syntax is as follows:
Let's explain the sub-clauses by starting with the <add column definition>
clause. This is probably going to be used more than the others because you
always forget something when you design a database. This clause will add
a column to a table using the following syntax:
75
Chapter 2 - Defining a Database
The column has to follow all of the rules for a column that would apply if
it had been created with the schema. This means there can be no duplicate
column names in the same table, and so on. Likewise, the SELECT, UPDATE,
INSERT or REFERENCES privileges that were in effect apply to the new column
name:
Row 1
Row 2
This is the table created by the example database. We will now add an extra
column to it for phone numbers:
The extra column is always added onto the end of the existing table. As
before, you can't view the effect of this statement as the table is empty
Theoretically it would be like this:
76
I le
You can get rid of this column with DROP. The <drop column definition>
clause will destroy a column in the table. The syntax for this clause is as
follows:
The same rules apply on this DROP E COLUMN I as with DROP TABLE. The
syntax below is accepted by some SQL products.
It has all the same pitfalls that the DROP TABLE syntax minus the <drop
behavior> clause had. Using this syntax then, a typical ALTER TABLE
statement would look like this:
This will simply remove the column created by the ALTER TABLE ADD
[COLUmNi which was performed earlier, returning the table to its original
form.
The following functions are supported under different names by some SQL
products, such as MODIFY in Watcom SQL rather than ALTER. The <alter
column definition> clause will change the default of a column. The syntax
is as follows:
The <set column default clause> sub-clause uses this syntax to change
the default value for the column:
The <drop column default clause> will drop the default. This is its
syntax:
DROP DEFAULT
77
Chapter 2 - Defining a Database
This clause requires that the constraint has a name. This is why it is a good
idea to always name constraints, otherwise you can't easily change them.
The problem comes when you try to drop a PRIVMARY KEY or uNIQ•E table
constraint and there exists a referential constraint in another table which
references this table. Again, we will have to look at how this is dealt with
when we look at drop behavior.
Summary
We have covered a lot of ground in this chapter and have made a start on
our example database. Working through the creation of this database will
help you get used to the different requirements SQL demands of you as a
programmer.
We created our first table using the CREATE statement and looked at how
data types differ in SQL from other languages. We looked at all the
different aspects available under the CREATE TABLE statement such as
column constraints and commenting.
We began our work on the example database by writing some SQL code to
create the headings and structure of the tables and columns.
Finally, we briefly covered how to DROP and ALTER table and non-standard
commands involved in their usage.
78
m0
In the next chapter we'll move on to some theory behind some of the
features we have used, such as the NULL clause, making sure you
understand properly how and why they are used. But first we'll look at
how SQL handles the data types we've already used in the database such as
•NTEGERs, cms and WUMERIcs and what rules are applied when using these
data types.
79
Chapter
Chapter
Rounding
SQL leaves rounding to the implementation. There are two major types of
rounding in programming:
82
I Nu
The Commercial Method
The 'commercial method' looks at the digit to be removed. If that digit is 0,
1, 2, 3 or 4, it is dropped and the digit to its left remains unchanged. If
that digit is 6, 7, 8 or 9, it is dropped and the digit to its left is
incremented. However, when the digit is 5, you want to have a rule which
will round up in about half of all cases. One rule is to look at the digit to
the left; if it is odd, then leave it unchanged, if it is even increment it.
There are other versions of the decision rule, but they all try to make the
total rounding error in a summation as small as possible. For example,
3.141592653 rounds to 3.141 because the fourth decimal place is a five, so
we have to look at the fifth decimal place. Nine is odd, so we truncate
instead of increment. This method works with a large set of numbers and
was popular with COBOL programmers and bankers because they need
accurate totals. Bankers especially, needless to say.
Truncation
Truncation is defined as truncation towards zero. This means that 1.5 would
truncate to 1, and -1.5 would truncate to -1. This isn't true for all
programming languages - everyone agrees on truncation towards zero for
the positive numbers, but you will find that negative numbers may truncate
away from zero (for example, -1.5 would truncate to -2) in some products.
83
Chapter 3 - SQL Arithmetic, Logic and the NULL
The SQL-92 CAST() function isn't only more general but it also
allows the <cast operand> to be either a <column name>,
<value expression> or a NULL. You should expect to see it
replace vendor functions in the next few years.
84
I ah
The monadic plus and minus signs are allowed, and you can string as
many of them as you like in front of a numeric value of variables. The bad
news about this is that SQL also uses the double dash ADA-style comments,
so the parser has to figure out if '--' is two minus signs or the start of a
comment. Of course, you can use parentheses to make it clear.
If both operands are exact numeric, then the data type of the result is exact
numeric, as you would expect. Likewise, an approximate numeric in a
calculation will cast the results to approximate numeric. The tricky part is in
how the results are assigned precision and scale.
Let's work through a brief example to sort this out. Let S1 and S2 be the
scale of the first and second operands, respectively. The precision of the
result of addition and subtraction is implementation-defined and the scale is
the maximum of S1 and S2. The precision of the result of multiplication is
implementation defined and the scale is (Si + S2). The precision and scale
of the result of division is implementation-defined and so are some
decisions about rounding or truncating results.
The ANSI X3H2 committee debated about requiring precision and scales in
the standard, and finally gave up. This means we can start losing high
order digits, especially with a division operation, where it is perfectly legal
to make all results single digit integers. No vendor does anything that
stupid in their product. Some products will allow you to adjust the number
of decimal places as a system parameter, some default to a few decimal
places, and some display as many decimal places as they can, so that you
can round off to what you want. You will simply have to learn what your
implementation does by experimenting with it.
Most vendors have extended this set of operators with other common
mathematical functions. The most common additional functions are modulus,
absolute value, power and square root. But it is also possible to find
85
Chapter 3 - SQL Arithmetic, Logic and the NULL
Many implementations also allow for the use of external functions written in
other programming languages.
86
St
spaces or which are also reserved SQL keywords. Look at these various
occurrences of the same word as different types:
The first problem is whether the upper and lower case versions of a letter
will be treated as equal to each other. Many programming languages treat
them in that way within the program text, including all proper SQL
implementations. While the standard says that the two cases are different, it
is very implementation-dependent. Some implementations, such as Sybase,
allow case-sensitivity or lack thereof to be specified when the system is
installed.
The SQL-92 standard has two functions which change the case of a string.
LOWER (<string expression>) shifts all letters in the parameter string to
corresponding lower-case letters. UPPER (<string expression>) shifts all
letters to upper-case. Most implementations have had these functions
(perhaps with different names) as vendor library functions.
Equality between strings of unequal length is done by first padding out the
shortest string with blanks on the right hand side, until the strings are of
the same length. They are then matched, position for position, for identical
values. If one position fails to match, the whole equality fails.
87
Chapter 3 - SQL Arithmetic, Logic and the NULL
String Functions
SQL-92 defines a set of string functions which have been in most products,
but with vendor specific syntax. You will probably find that products will
continue to support their own syntax, but also add the SQL-92 syntax in
new releases.
String concatenation is shown with the ' I' operator taken from PL/1 (the
programming language of the 60's and 70's that was intended to
complement third generation computer systems). There are some special
rules that govern what happens if you concatenate a string with a missing/
unknown or NULL value, but we will cover those when we cover the NULL
value, later this chapter.
It uses three arguments: the source string, the starting position of the
substring and the length of the substring to be extracted. Truncation occurs
when neither the implied starting nor the ending positions are within the
given string.
If any of the three parameters are NULL, this function returns a NULL. If the
<start> position is greater than the length of the <string>, or if the
<string> is empty, then the result is the empty string. If you leave off the
FOR <length> clause or give a value that is too long, it defaults to the rest
of the string.
88
St
UPPER(<string>) to convert all the characters to upper case.
This produces a result string that is the source string with an unwanted
character, removed from the start or the end of the string. The <trim
source> is the original character value expression. The <trim
specification> is either 'LEADING' or 'TRAILING' or 'BOTH' and the <trim
character> is the single character that is to be removed. The SQL-92
function is very general, but you will find most SQL implementations have
a version which works only with leading blanks.
89
Chapter 3 - SQL Arithmetic, Logic and the NULL
Vendor extensions are varied, but there are some which are worth mention.
The names will be different in different products, but the functionality will
be the same.
Reverse
REVERSE (<string expression>) will reverse the order of the characters in
a string to make it easier to search. This function is impossible to write
with the standard string operators since it requires either iteration or
recursion.
Flip
FLIP(<string expression>, <pivot>) will locate the pivot character in the
string, then concatenate all the letters to the left of the pivot onto the end
of the string and finally erase the pivot character. This is used to change
the order of names from 'military format' to 'civilian format'. Therefore, if
you used the command FLIP ('Smith, John', ',' ) it would become 'John
Smith'. This function can be written with the standard string functions,
however.
90
D
Numtowords
NUMTOWORDS (<numeric expression>) will write out the numeric value as
English words to use on checks or other documents which require both
numerals and text versions of the same value. This was replaced by the
CAST function in the SQL-92 standard.
Displaying Dates
Everybody likes a different date display format, so most databases will give
you several options. The usual ones are a mixture of a two or four digit
year, a three letter or two digit month and a two digit day within the
particular month. The three fields can be separated by slashes, dashes or
spaces. SQL-92 can't use abbreviations for month names and still be
international (that is, language independent), so the format in the standard
is a four digit year, a dash, a two digit month (01-12), a dash and a two
digit day within the month (01-31). This 'yyyy-mm-dd' is called the calendar
date format. It is a string that gets converted into an unspecified internal
format in the database.
Timestamps
The TIMESTAMP(n) data type is defined as a timestamp to (n) decimal
places (for example, TIMESTAMP (9) is nanosecond precision), where the
precision is hardware-dependent. You can expect to get at least four to five
decimal places after the second.
Remember that a timestamp will read the system clock once and use that
same time on all the items involved in a transaction. It doesn't matter if the
actual time to complete the transaction is days; a transaction in SQL is done
as a whole unit or not done at all. Most of the time this isn't a problem for
small transactions, but it can be in large batched ones where very complex
updates have to be done.
Times
Most databases live and work in one time zone. If you have a database
which goes over time zones, you might consider storing time in UTC
(Universal Time Coordinate, formerly GMT, Greenwich Mean Time) and
adding a numeric column to hold the local time zone offset. The time zones
start at UTC, which has an offset of zero. This is how the system level time
zone table in SQL-92 is defined. There are also ISO standard three letter
codes for the time zones of the world, such as EST for 'Eastern Standard
Time' in the United States. The offset is usually a positive or negative
number of hours, but there are some odd zones which differ by 15 minutes
from the expected pattern.
You should use a twenty-four hour time format. Twenty-four hour time
display format is less prone to errors than twelve hour (AM/PM) time. It is
less likely to be misread or miswritten. This format manually sorts more
easily and is less prone to computational errors. Americans use a colon as a
field separator between hours, minutes and seconds, while Europeans use a
period. Most databases will give you these display options.
Intervals
One of the major problems with time is that there are three kinds: fixed
events ('he arrives at 13:00 hours'), duration ('the trip takes three hours')
and intervals ('the train leaves at 10:00 hours and arrives at 13:00 hours')
and all three are inter-related.
92
I a
Date Arithmetic
You may assume that your SQL implementation has simple date arithmetic
functions. The syntax will vary from product to product, but the basic
operations every product has are:
Here is a list of the valid combinations of datetime and interval data types
in the SQL-92 standard:
There are other rules that deal with time zones and the relative precision of
the two operands which are intuitively obvious.
There should also be a function which returns the current date from the
system clock. This function has a different name with each vendor: TODAY,
SYSDATE and getdate () are some examples. There may also be a function
to return the day of the week from a date which is sometimes called DOW()
or WEEKDAY 0. The SQL-92 standard provides for CURRENTDATE,
CURRENTTIME [(<time precision>)] and CURRENTTIMESTAMP
[(<timestamp precision>)], functions which explain themselves.
93
Chapter 3 - SQL Arithmetic, Logic and the NULL
<extract expression>::=
EXTRACT (<extract field> FROM <extract source>)
The interesting feature is that this function always returns a numeric value.
For example, EXTRACT (MONTH FROM birthday) will be an INTEGER between
1 and 12. No vendor has implemented this function yet, so you should look
for many separate functions like YEAR(<date>), MONTH(<date>) and
DAY(<date>) which extract particular components from a datetime data
type.
Week Functions
Another common set of functions, which are not represented in standard
SQL, deal with weeks. For example, Watcom SQL has a DOW(<date>) which
returns a number between one and seven to represent the day of the week
(1 = Sunday, 2 = Monday,... 7 = Saturday, following an ISO Standard
convention). You can also find functions which add or subtract weeks from
a date, give the number of the date within the year, and so on.
The best advice we can give you is to read your SQL product
manual and see what you can do.
94
Cm
The comparison operators in SQL are overloaded, which means that the
same symbol will work for numeric, character and datetime data types. The
meaning of, and symbols for, comparison operators are as follows:
You will also see ':=' or '-=' for 'not equal to' in SQL
implementation. These symbols are borrowed from C and PL/
1 programming languages respectively and have never been
part of standard SQL.
95
Chapter 3 - SQL Arithmetic, Logic and the NULL
Most often, one value is converted to the same data type as the other and
then the comparison is done in the usual way The chosen data type is the
'higher' of the two, using the ordering from least to highest is SMALLINT,
INTEGER, DECIMAL, NUMERIC, REAL, FLOAT and DOUBLEPRECISION.
CHARACTER and CHARACTER VARYING data types are comparable if, and only
if, they are taken from the same character repertoire. That means that ASCII
characters can't compare to graphics characters, English can't compare to
Arabic, and so on. In most implementations, this isn't a problem since the
database has only one repertoire. The comparison takes the shorter of the
two strings and pads it with spaces. The strings are compared position by
position from left to right, using the collating sequence for the repertoire
(ASCII or EBCDIC in most cases).
DATETIrME data types are mutually assignable only if the source and target
of the assignment have the same DATETIME fields. That is, you can't
compare a date and a time.
96
Lg and
But Boolean algebra isn't the only possible system of logic and SQL doesn't
use it. SQL has a three valued logic based on the values TRUE, FALSE and
umKNOWN. The SQL standard still refers to Boolean operators, but they have
slightly different rules. This linguistic confusion and the strangeness of
thinking in three values is hard for beginners to SQL programming. The first
problem is realizing that uNKNOwN is a logical value (this isn't the same as
the data value NULL which is also a missing or unknown character).
Notice that SQL-89 doesn't have a BOOLEAN or LOGICAL data type. If there
were such a data type, then you could store TRUE, FALSE, UNKNOWN and NULL
in columns with that declaration. Ouch - that gives us four logical values
and would make all kinds of problems which we don't want to even think
about in an introductory book!
If you need to store a logic value in a column, then use flags in some other
data type. The most common choices are cHAR(l) with allowed values of
'Y' for 'yes' and 'N' for 'no', or 'T' for 'true' and 'F' for 'false'. You will also
see a SMALLINT declaration used with allowed values of one and zero. The
SMLLiNT declaration has the advantage that the column can be added to
tell you how many TRUES were in it and other logical operations can be
done with arithmetic.
97
Chapter 3 - SQL Arithmetic, Logic and the NULL
The OR table:
<EXP> NOT
TRUE FALSE
UNKNOWN UNKNOWN
FALSE TRUE
Notice that if you removed the um-Kows from the table, these
would be the normal Boolean operators you have seen in
every other programming language.
The operands for these operators are simple comparisons (or other
operators) which are shorthands, which expand into chains of comparisons -
(comparison operators are also called theta operators in relational database
theory). Let's start with the simple comparison operators.
98
S
SQL-92 has solved this problem by adding a new search condition of the
form <search condition> IS [NOT] TRUE I FALSE I UNKNOWN, which will
let you map any combination of three-valued logic to two values. It applies
only to search conditions and works in the following way:
The ISN'T form of the operator is the same as putting a NOT outside the
whole expression. For example, the expression ((age < 18) AND (gender =
'Female')) ISN'T FALSE, is the same as NOT ((age < 18) AND (gender =
'Female') IS FALSE). This expression will return TRUE if (age IS NULL) or
(gender IS NULL) and the remaining condition isn't NULL. This is a MAYBE
operator.
99
Chapter 3 - SQL Arithmetic, Logic and the NULL
Likewise, it was a long time before the idea of an empty set found its way
into mathematics. The argument was that if there are no elements, how can
you have a set of it? Is the empty set a subset of itself? Is the empty set a
subset of all other sets? Is there only one empty set or one for each type of
set?
The NULL is how SQL handles missing data. It is a major stumbling block
for experienced as well as new programmers, so we are going to spend
quite a lot of time discussing it to make sure that you have a feel for what
it does and how it works.
The NULL is how SQL handles missing values. The lLrt, is a global
creature, not belonging to any particular data type, but able to replace any
of their values. So what exactly is a NULL?
Programmers coming from Pascal and the like might be tempted to think it
is similar to the NULL string, which is treated like an empty string. Get rid
of that idea immediately. A NULL is a missing value in the sense that it
could be replaced with an actual value. It is as yet unknown. It is incorrect
to say that a student who has taken an exam has got zero marks, just
because they don't yet know their results. So, don't be tempted to think it is
equivalent to nothing.
100
- l
Nu
The basic rule for math with NULLS is that they propagate. What this means
is that an arithmetic operation with a NULL will return a NULL. That does
makes sense - if a NULL is a missing value, then you can't determine the
results of a calculation with it.
Instead of just going for a theoretical approach, let's stait with examples
that a programmer can understand and try to build a consistent model from
them.
Missing Values
It's not the beginner's fault that missing values are such a difficult concept -
there are a lot of reasons why values can be missing. Each kind of missing
value is a little different, with a slightly different meaning, but they are all
folded into the NULL in SQL. SQL doesn't care about semantics, only the
syntax matters. Some of the ways to use a NULL are as follows:
"1 Presently unknown values, which will later be replaced with real values
when we do know something. For example, you just got to the hospital
and they don't yet have a diagnosis for your condition. The tests are at
the lab and we are awaiting the results.
2 The value is permanently missing from the attribute and won't come
back. The hair color of a bald man is an example that comes to mind
(see author's photograph). This kind of situation is often shown with the
code 'N/A' for 'not applicable' on paper forms and in computer
spreadsheet programs.
Chapter 3 - SQL Arithmetic, Logic and the NULL
4 The attribute itself doesn't apply, so it can't have a value. For example,
the color of the feathers on a pig's wings can only make sense when
pigs have wings. This situation also might be shown with the code 'N/
A' as in situation 2 (but it is actually different).
6 The value can be partly known, but not confirmed. You create a table
with a column called 'Tricolor' which is limited to the values 'red',
'white' and 'blue' and put a constraint on it that says each color can
appear only once.
If my table has a 'red' and two NULL values in that column, then I have
some information about the two NULLS. I know they are either 'white'
and 'blue' or 'blue' and 'white'. This is what is termed by one author,
Chris Date, a 'distinguished NULL', which means we have some
information in it. If my table has a 'red', 'white' and NULL value in that
column, can I change the last NULL to 'blue' because it can only be 'blue'
under the rule? Or do I have to wait until I see an actual value for that
row?
102
I
This idea can be carried further with 'marked' NULL values. For example,
u
we are given a table of hotel rooms which has columns for check-in
date and check-out date. We know the check-in date for each visitor, but
we don't know their check-out dates. Instead we know relationships
among the NULLS. We can put them into groups; for example Mr and
Mrs X will check out on the same day, Tour Group Y will check out on
the same day, and so on. We can also add conditions - nobody checks
out before his check-in date, Tour Group Y will leave after 1993 Jan 07,
and so on.
7 The value can be known, but not within the range of the computer
which holds it. The obvious examples would be numeric overflow and
underftow in calculations, which will produce a run-time error message
in SQL. However, trying to put a string into a column which is too
short might also qualify, or trying to put a numeric value into a string
without converting it into characters first. The inability of some
computer spreadsheets to handle leap year day in the year 2000 is
perhaps the most frightening and unforgivable example of such a
situation.
9 We just don't care about the value because the result is the same no
matter what the value is. Programmers who have used decision tables
are familiar with this concept. A decision table is a grid of conditions
and rules made up of 'Yes', 'No' and "Don't care" (shown as 'Y', 'N' and
respectively) to the conditions. If a rule is true, then its action is
taken. The meaning of "Don't care" is that the action is taken for either
a TRUE or FALSE answer to that condition.
103
Chapter 3 - SQL Arithmetic, Logic and the NULL
NULLS in SQL are used for purposes 1 through 5, since the others can't be
represented in SQL. Among all these possible missing values, which is the
correct way to read a NULL? The answer is that there is no one correct way to
read a NULL. It depends simply on the context in which a NULL is used.
Again, SQL is a language which works on syntax, not semantics. It is up to
the programmer to assign meaning to the NULLs - it isn't up to SQL. The best
approach is to avoid them unless you really need to use them.
A NULL isn't a blank string. A strings of blanks has a length greater than
zero. This is one reason that SQL requires all ciiR columns in a table to be
declared at least one character wide.
A NULL isn't a string of length zero. If you concatenate a zero length string
to another string, that string stays the same; if you concatenate a NULL to a
string, that string becomes a NULL. This is another example of the rule that
NULLS propagate.
Comparing NULLS
If you compare a non-NULL value to a NULL with a comparison operator
(equal, not equal, less than, greater than, and so on), you get an uimom
result. In fact, a NULL compared to another NULL will return an urmomoW
result. Think again about the example of awaiting a diagnosis at a hospital.
The results for your condition are not always the same as the results for
my condition, but they both are presently unknown.
This rule breaks down in some cases, however. There are grouping
operations in SQL which will place all the NULLS together, as if they were
equal. According to the SQL-89 standard, the implementor gives to decide
how NULLS sort with actual data values. The rule is that you first sort all of
the data values, either into ascending or descending order, then always
attach the NULLS at the top or the bottom of the result.
104
I Converting
Nulls
L
Converting Values to and from NULL
Since NULLS 'mess up' operations on data types and host languages don't
support NULLS, yOU the programmer will often have to convert NULLS into
some value which is usable in the context of the problem. SQL has
functions for just such conversions.
NULLIF() Function
SQL-92 specified two functions, NULLIF() and the related COALESCE(),
which can be used to convert expressions to and from NULL. These functions
are not yet in most SQL implementations, but you will often find something
like them.
That is, when the first parameter is equal to the second, the function
returns a NULL, otherwise it returns the first parameter's value. The
properties of this function allow you to use it for many purposes. The
important properties are as follows:
1 NULLIF(x, x) will convert all values of x into NULL. This includes NULL
since (NULL = NULL) is UNKNOWN, not TRUE.
2 NULLIF(O, (x-x)) will convert all non-NULL values of x into NULL. But
it will convert a NULL into a zero since (NULL - NULL) is NULL, and the
equality test will be UNNow and fail.
105
Chapter 3 - SQL Arithmetic, Logic and the NULL
COALESCE() Function
The COALESCE(<value expression>,..., <value expression>) function
scans the list of expression>S from left to right and returns the
<value
first non-NULL value in the list. If all the <value expression>s are NULL,
then the result is NULL.
Concatenating NULLs
Watch out for the difference between a NULL and a string of length zero
when you concatenate strings! The rule is that NULLS propagate. This holds
for strings, so a string concatenated with the empty string is unchanged, but
a string concatenated with a NULL is a NULL.
Advice Time
You can't completely avoid NULLS in SQL, since some functions and
operators will produce them. However, it is a good idea to try as hard as
you can to avoid them whenever possible.
Encoded values can have special codes for missing data. For example, the
ISO sex codes are as follows:
106
1 0 = unknown
I se
M 1 = male
A 2 = female
AN 9 = not applicable
The 0 code is for an unknown sex, while the 9 is for legal individuals,
such as corporations.
Likewise with names, you are probably better off using a special dummy
string for unknown values rather than the general NULL. In particular, you
can build a list of 'John Doe #1', 'John Doe #2', and so on, to differentiate
between them, and you can't do that with a NULL.
Dates and times have to use a NULL in some cases. Unfortunately, you often
know relative times and have no way of expressing this in a database. For
example, a pay raise occurs sometime after you have been hired, not before.
A convict serving life might expect a release date of Eternity. The usual
trick is to insert the maximum possible date for Eternity, but the problem is
that it will show up in averages and other summary statistics.
((a < x)
OR ((a = x) AND (b < y))
OR ((a = x) AND (b = y) AND (c < z)))
107
Chapter 3 - SQL Arithmetic, Logic and the NULL
The SQL-92 standard decided that the theta predicates would work as laid
out in a set of rules. The rules are as follows:
2 x <> Yis TRuE if and only if x[i] <> Y[i] for some i.
3 x < Y is TRUE if and only if x[i] = Y[i] for all i < n and x[n]
<Y[n] for some n.
4 x is TRUE if and only if x[iJ = Y[iJ for all i < n and x[n] >
> Y
Y[n] for some n.
108
-Uo
mm
The expression x <comparison operator> Y is a shorthand for a row X
compared to a row Y and likewise X[i] means the ith column in the row X.
The results are still either TRUE, FALSE or UNKNOWN, if there is no error in
type matching. The rules favor solid tests for TRUE or FALSE, using UNKNOWN
as a last resort.
The idea of the greater than and less than operators is that the values in
one row are always greater than (or less than) those in the other row as
you go from left to right after a certain column. This is how it would work
if you were alphabetizing a word. The negations are defined so that the NOT
operator will still have its usual properties.
Summary
In this chapter we have covered a lot of heavy-duty math, and you should
try to nail a lot of it down before you learn more about SQL. Time spent
familiarizing yourself with math as it hits on SQL will be time well spent.
We looked at the numeric functions and how SQL dealt with various
aspects of rounding and truncating. We looked at this in conjunction with
how you can convert one numeric type to another.
109
Chapter 3 - SQL Arithmetic, Logic and the NULL
Next, there were temporal data types which is where we discussed how
SQL stores the date and time and how you can use these basic data types
to do simple arithmetic.
Then we were ready for the NULL. We talked about what a MILL is, how it
is used in SQL. Also, we took great pains to talk about what a NULL isn't.
We covered many aspects of its usage that may arise in SQL.
Finally we looked at some rules for Row comparison operators, for which
you need to understand Three Valued Logic.
I um
111
Chapter
I Types of keys
4 An introduction to normalization and normal forms
I Keys and the example database
--- Referential integrity and other constraints
Chapter 4 - Keys and Referential Integrity
Definition of a Key
A key is a column or a set of columns in a table which
uniquely identify each row within that table.
100 NAME
1001 John Doe
1002 identifies only this
1003 John Doe occurence of John Doe
1004 John Doe
Every time there are an identical set of entries within a table, and each item
is actually completely separate and individual, you need a key to uniquely
identify it. As this could possibly occur in every table, it's a very good idea
to have a key within every table. Keys can have more than one column. If
it's impossible to uniquely identify each entry with just one column, then
you may have to use more.
1000 1 oojit
1000 2 oojit
1000 3 oojit
1000 4 oojit
A Key is usually numeric, (as this makes it easy to sort) but it can quite
possibly be a character, a word or even an alpha-numeric. Think about what
you'd like to be able to do with that key. Does the key actually specify some
sort of numerical order within the table, such as positions within a league
table? (Excluding the possibility that two teams could have the same
position!) If so, you might want a numeric key so you could flip the table
over and have all the teams doing the worst displayed at the top of the
list. So, before you make a key you can't sort easily or that can't describe
every item in the table, think about what the key should actually need to
I s
do (if you used a single letter of the alphabet for every item as a key and
you had 27 items, that would be pretty stupid.)
Keys are important for normalization, indexing, sorting and searching for
data. Keys come in many flavors, which we will discuss in a moment. The
idea of a key is that if you know the key, then you can find the rest of the
data in the table. If I know the part number in an inventory, then I can look
up the part and find its weight, color and price. If I know an employee
identification number, then I can look up the employee's records.
In SQL, a table doesn't have to have a key, but it's still a good idea to have
one. One possible exception to this is a table which holds the data from a
supermarket cash register tape. Each purchase consists of one line per item,
and many of those items are repeated. One can of Brand X tuna fish is like
any other can of Brand X tuna fish, so we don't give them individual part
numbers. (You might well give different brands of tuna fish different
identifiers, however). If it were automobiles, then we would worry about
telling them apart. The problem is that without a key, we will have to handle
the subset of 'can of Brand X tuna fish' as a unit; that is, in SQL when we
delete and search on 'can of Brand X tuna fish', we will delete or find all of
them without a key to uniquely identify them.
Types of Keys
A candidate key is one of the possible keys in a table. (Yes, this does mean
a table can have more than one key!) For example, a candidate key arises
when there could be two likely possibilities for a primary key Consider a
car: it has a number plate, which is unique in the country. It also has a
license tag which is unique in Pennsylvania. So the candidate keys are
number plate and state + tag number. Either one uniquely identifies the car,
while the fact that the car is a green 1995 Mazda doesn't.
A surrogate key is a key which has no meaning in itself. These keys are
usually constructed by the database system using a sequential or random
number generator. Very often, these surrogate keys are never shown to the
user.
115
Chapter 4 - Keys and Referential Integrity
a building uniquely identifies the building and it tells you its location. The
name of the building or a property code number would uniquely identify the
building, but wouldn't give you any extra information.
A super key is basically a key with too many columns in it. That is, you
could remove some of the columns and still have a key. While this sounds
like a bad thing, in practice it can be handy or even necessary. For example,
if we converted our inventory system from one encoding system to another,
we would probably keep the old part numbers and the new part numbers in
the inventory table so that we could take orders from both the old and the
new catalog.
The foreign key in this example is in the Sales Order Table (Part No.). A
foreign key may or may not be a key in its own table. In the example here,
Part No. isn't a key, Order No. is.
N r lz i
A foreign key can also reference its own table. For example, we could have a
table of employees which has a column for the employee identification
number of each employee's boss. Since the boss is also an employee, he or
she will also have a row in the table. We have to make the owner of the
company report to himself for this to work.
Employee Id Boss Id
LackeyO01 BossmanOOl
LackeyO02 BossmanO01
BossmanO01 BossmanO01
Normal forms are an attempt to make sure that you don't destroy true data
or create false data in your database. One of the ways of avoiding errors is
to represent a fact only once in the database, since if a fact appears more
than once, one of the instances of it is likely to be an error. A man with two
watches can never be sure what the correct time is.
117
Chapter 4 - Keys and Referential Integrity
You should make a list of the separate physical items that will go to
constitute your database. You need to know what information you will be
storing, and you need to put on your list the definitions of what you want to
store. For example, in the specimen database, you would need a table for
Salespersons. You'd want to decide what information to store on each
salesperson. A name column would be one of the first requirements, so you
would have a definition reading something like employee name - a column
containing the first and last name of each person who works in sales for this
organization. Next, you would decide what other details you want on each
salesperson. Their salary, perhaps; so now you need to have a heading
related to employee name that would be defined as the weekly salary for
each salesperson within the organization. This would become a column
within the table.
Group each set of related items into a separate list (some will overlap, but
this is a first draft so it doesn't matter too much where you put them, you
can always rearrange later). These will form the basis of your tables. Each
item in the list will become a separate column. Make sure that each 'table'
has a key, that is, a key with which to identify each individual. This key will
be the main way for picking out each item of data that will go into the
table. You could use the employee name as a key, but what happens if two
names are identical? For a key, you might want a separate identifying
number for each salesperson, which would be their id number.
There! You've just created a database that is already in first normal form. Not
too hard, was it? Here is a summary of what needs to be done for first
normal form.
First normal form requires a separate table being made for each
set of related columns. Each separate table should have a
primary key.
L o
Now let's start from scratch and create a database about class schedules. We
are required to keep the course, courseid, department, time, professor,
student, major and grade. Suppose that we initially set up a Pascal file with
records which look like this:
Classes = RECORD
course: ARRAY [1:7] OF CHAR;
courseid: INTEGER;
room: ARRAY [1:5] OF CHAR;
roomsize: INTEGER;
professor: ARRAY [1:251 OF CHAR;
Students: ARRAY [l:classsize]
OF RECORD
student ARRAY [1:25] OF CHAR;
studentid INTEGER;
major ARRAY (1:10] OF CHAR;
grade CHAR;
enrolmentdate DATE;
END;
END;
This table currently isn't in the most basic (first) normal form of relational
databases. First normal form (lnf) also means that the table has no repeating
groups. That is, every column only contains scalar or atomic values. The table
can't contain an array or list or anything with its own structure. This Pascal
file has several arrays.
In SQL, it's impossible to use anything other than scalar or atomic values,
unless the vendor has added arrays or other extensions to the language. The
Pascal record could be 'flattened out' in SQL to look like this:
This table is acceptable to SQL. But notice that the course, and professor
values, would have to be repeated for each student.
119
Chapter 4 - Keys and Referential Integrity
There are some ways around these problems without changing the tables. We
could permit NULLS in the table. We could write routines to check the table
for false data. These are tricks which we will be less able to implement as
the data and the relationships become more complex.
The solution is to break the table up into other tables, each of which
represents one relationship or one simple fact:
120
-Seo
- l Fr
eNrm
assume that this database has every car number plate in the world. They're
unique in each country, but can you be sure that a plate in the US doesn't
match up with one in the UK? Probably not, so you'll have to use both the
country and the car serial number to be sure, in other words, a multi-valued
key.
For this example, we're excluding the fact that people might
choose to use unleaded fuel, we're assuming that they are only
doing so because the law says they have to!
You're the DBA and yes, you've guessed it, you have to change the entries
for each car in the fuel column for the UK to unleaded. That's roughly
twenty million entries. Can you be sure that you update every single entry?
Clearly not. If you miss one out, that means the database is holding incorrect,
outdated information. Wouldn't it be so much easier if there was a separate
table for each country? That's second normal form.
121
Chapter 4 - Keys and Referential Integrity
Full Derendencv
This means that the table has to be in lnf and has to have a key. So if the
table is nothing but a key, then the table is in 2nf. For example, a list of
classrooms on its own would be a 2nf table.
In the class schedule table, knowing the studentid and course is sufficient
to determine his or her courseid, since students can't sign up for more than
one version of the same course. We also know that a student can have only
one enrolment date in a particular course. This is the same as saying that
(studentid, course) determines (courseid, enrollment date). Looking more
closely we can also see that knowing the student's name determines his
major, as students can only have one major. This leads us to the following
tables:
122
-Ii
CREATE TABLE Classes
(course CHAR(7) NOT NULL,
courseid INTEGER NOT NULL,
room CHAR() NOT NULL,
roomsize INTEGER NOT NULL,
professor CHAR (25) NOT NULL,
PRIMARY KEY (course, courseid));
Take a look the classes table as it now stands. The (course, courseid)
column determines the room where the class will be held. But if you know
the room number, then you can find how many students it will hold, and
this is a transitive dependency!
123
Chapter 4 - Keys and Referential Integrity
The reason they are trouble is that you might update only one of the
relationships involved. For example, if I decide to move a course to another
classroom, I have to also update the roomsize column. What we need to do
is split the classes table into the following:
The class size doesn't describe any part of the course or courseid, so
therefore it should be removed. This is the same method as eliminating
transitive dependencies, but taken from a different perspective.
124
Example
Database
and one to represent the body or detail lines of the order form:
125
Chapter 4 - Keys and Referential Integrity
Notice that there are a lot of NOT MULL constraints, which is a good sign
that your schema is normalized. The detail will probably be a line number
taken from the screen or paper order form, but its purpose is to identify
the separate items within an order.
This is the first multi-column primary key we have seen. It's the
combination of (orderid, detail) which is unique - individually they are
not unique.
3 Since the constraints are logical expressions, putting them in the database
lets the optimizer use these rules to speed up performance.
126
Ta
The constraint name is a qualified identifier in the schema, just like a
column would be, and it has to be unique within its table. In SQL-92,
constraints can be given names by putting a clause in front of them that
looks like the following:
CONSTRAINT commands are placed at the end of the list of table elements,
rather than after the column definition like column constraints are, and there
can be as many as you need.
Check Constraint
The CHECK (<search condition>) constraint is like the CHECK clauses that
are attached to rows in a table declaration. The difference is that they appear
as separate declarations in a CREATE TABLE statement. By disassociating the
constraint from a particular column, you can use the ALTER statement on it
separately from the columns with which it is involved.
127
Chapter 4 - Keys and Referential Integrity
The search condition can be anything that you could also write in a query,
but it must apply only to the column in its table. The way around that is
to use sub-queries which reference other tables in the schema. In theory, you
can write almost all the other constraints as CHECK clauses, but you would
have some really elaborate code to maintain.
In the old SQL-89 standard, UNIQUE also automatically implied that all the
columns being tested were all NOT NULL. Now, in the SQL-92 standard, the
columns get the benefit of the doubt and matching is done for equality of
all the non-NULL values in the column(s). Trust me, it works better with
the CHECK() constraints and other features of SQL-92. You can't have a
column name appear more than once in the <column list>, however two or
more UNIQUE constraint's <column list>s can overlap.
When you use a UNIQUE constraint on multiple columns, it's the combination
of values in those columns that must be unique. This makes sense when both
values are needed to ensure uniqueness. Consider a scheme where a
128
Ta
Company is divided into departments numbered 1 - n. Departments are
divided into divisions. Divisions are numbered 1 - n within each
department. This means that there are as many Division l's as there are
departments, and you need to know the department number in order to
know which division you're talking about. In this case UNIQUE
(Department, Division) will give you exactly what you need.
However, it is easy to get confused and think that you can use unique
constraints to enforce all kinds of business rules. For example, consider this
table for a school's class schedule:
Each teacher teaches one and only one class (we have a specialization of
faculty!) By itself, this constraint would prevent the two rows ('Jones',
'zngi 101', 2) and ('Jones', 'Engi 101', 3) from appearing in the
table. However, ('Jones', 'Engl 101', 2) and ('Jones', 'Engl 102',
3) would be fine, as would ('Jones', 'Engi 101', 2) and ('Jones',
'Engi 102 ', 2). This last pair implies that Jones is in two places at
once, but our present constraint doesn't worry about that.
A class is taught in only one time period. At this point, you can figure
out what is allowed or disallowed by the constraint, so let's move on.
129
Chapter 4 - Keys and Referential Integrity
You might think that this enforces the other three constraints in a
single rule, but it doesn't! Consider a table with rows:
This would fail the first uniqueness constraint and pass the three others.
Be careful and test your logic with some data. We could use any or all
of these constraints on the Schedule table. The allowable rows would be
different in each case; as an exercise, you might want to set up a
dummy table and try all the combinations.
This constraint says that the column(s) involved are all NOT NULL and that
they are also UNIQUE. Furthermore, a table can have only one PRIMARY KEY
because the primary key is the default column set for foreign key references,
which we will discuss shortly.
You can't have a column name appear more than once in the
PRIMARY KEY constraint column list. However, it can overlap
with other UNIQUE constraint column lists in the table
declaration.
130
IU
The idea of a foreign key is that we have one or more columns in one table
which reference matching columns in a second table. Strictly speaking,
relational database theory says that they have to reference the primary key
of the other table, but SQL lets you pick the columns to use by name as
long as they are part of a uNIQUE constraint. In fact, SQL will even let you
use the same table as the referenced table.
Again, the two column lists have to be union comparable to each other. That
means that they have the same number of columns and that the columns
match up in position by data type. A row in this table matches a row in the
other table, but if a row in this table has a NULL, you give the match the
benefit of the doubt.
We'll go back to the example we used in Chapter 2 using the two tables A
and B. We'll assume now that custid isn't enough as a primary key for Table
A, so now we'll add another column custid2. Together custid and custid2
can uniquely identify every customer within the table. For example, before
the first customer identifier might only have been enough to identify a family
and second identifier might identify each individual family member. Apart
from that the function of the two tables is identical to that in Chapter 2, in
which table B references table A.
131
Chapter 4 - Keys and Referential Integrity
When you drop a table, you have to make sure that consistent action is taken
for other tables in the database, which in turn name the table you have just
got rid of. You can't have references from one of your current tables to a
table that no longer exists. Hence, the addition of the <drop behavior>
clause to the DROP TABLE, DROP COLUMN and DROP CONSTRAINT commands.
There are two methods to go about doing this, RESTRICT and CASCADE.
132
me
Firstly, for tables:
If RESTRICT is specified, then the table can't be deleted unless every table
referenced in the definition of the table or the search condition of any
constraint is also deleted. If the table is referencing other tables, then you
will just receive an error message preventing you from doing that.
If CASCADE is specified, then all such objects (tables) that reference the table
to be dropped will also be dropped/deleted along with the table.
133
Chapter 4 - Keys and Referential Integrity
Referential Actions
In SQL-89, violations were reported by the system and the transaction was
disallowed. In SQL-92, the programmer gets a lot more power! There is even
a MATCH sub-clause which defines exactly how multiple columns are matched
between both tables.
134
-I.e
The term 'table event' means something changes the table and
that means one of four things: rows are inserted, deleted,
updated or the table is altered. SQL-92 allows referential
actions only on updates and deletes, but some products will
also allow actions on insertions.
If someone attempts to insert a row which violates the foreign key constraint,
there is only one action; the attempt is rejected. Since this is automatic, we
only have sub-clauses for updates and deletions. The syntax is as follows:
<referential action>::=
ON ( UPDATE I DELETE ) <action>
The ON UPDATE CASCADE will move the changes made to the matching foreign
key columns over to the referenced table. This allows us to make a rule that
when the part numbers are changed in the Inventory table, they are
automatically changed in the Orders table, too. The ON UPDATE SET ( NULL
DEFAULT } will move a NULL or a DEFAULT value in the matching foreign key
columns in the referenced table. This allows us to make a rule that when the
part number is changed in the Inventory table, we effectively 'blank out' the
referenced values with either an explicit DEFAULT value, such as 'Discontinued
item', or a NULL.
The NO ACTION option is the default and it means no referential actions are
taken, but do make sure the constraint isn't violated.
The ON DELETE CASCADE will delete the row with matching foreign key
columns over to the referenced table - this means the whole row and not just
the columns! This allows us to make a rule that when an item is dropped
from the Inventory table, it is automatically dropped from the Orders
table, too. The ON DELETE SET ( NULL I DEFAULT } will move a NULL or a
DEFAULT value in the matching foreign key columns in the referenced table.
135
Chapter 4 - Keys and Referential Integrity
This allows us to make a rule that when the part number is out of stock,
we would put either an explicit DEFAULT value, such as 'Out of Stock', or a
NULL in the Orders table.
Again, the NO ACTION option is the default and it means take no referential
actions but do make sure the constraint isn't violated.
We will hold back from giving you any example code until we look at these
options within the database here, as it is very difficult to give a small
example without having to create several tables.
Triggers
As an aside, there is a feature in many versions of SQL called a TRIGGER
which, like the referential actions, will execute a block of procedural code
against the database when a table event occurs.
Our advice would be to avoid them when you can use declarative
referential integrity instead. If you do use them, check the code very
carefully and keep it simple so that you won't hurt performance.
136
m0
There is a proposal for standardizing TRIGGERs in the current
ANSI/ISO SQL3, using a procedural language based on ADA.
The proposal is fairly complicated and no product has
implemented it completely. You should look at what your
particular vendor has given you if you want to work with
TRIGGERS.
No, we don't need to change this table. But, we might want to make the
check clauses into table check constraints. With the table check constraints it
would look like this:
137
Chapter 4 - Keys and Referential Integrity
We need some changes here. Firstly, let's get the REFERENCES clauses into
FOREIGN KEY constraints, like so:
But now we can add referential actions to this table. What would we do if a
salesperson's employee identification number is changed? Well, we would
cascade the change, so that the right person gets their commissions!
138
Exapl
What if a customer is hit by that runaway beer truck? Then there is nobody
to pay for that order! We'd better cancel it. That gives us the final table:
Finally, let's look at the OrderItems table. If an order is deleted, then the
details that went with it should go also, so we will cascade a deletion.
Likewise, if the order identification number changes, we want to keep the
OrderItems in sync. That would give us this declaration:
What about the parts that appear on the order form? Again, if the part
number changes, we want to cascade the change so that the buyer gets the
right merchandise. But what do we do if the item on the order is dropped
from the Inventory? There is no single right answer, but here are some
choices:
139
Chapter 4 - Keys and Referential Integrity
2 When an item is dropped from inventory, replace the ordered item with
a default item that happens to have a price of $0.00, a shipping weight
of 0 kilograms, and so on. The problem is that all the dropped items
would be replaced by this single default part number. You wouldn't be
able to tell a missing screwdriver from a missing submarine (not to
mention a missing beer truck driver).
3 When an item is dropped from inventory, drop that line from the order.
We don't sell what we don't have, as a matter of company policy. That is
pretty easy, so let's go with that decision for the final version of the table
definition:
Summary
This chapter has only begun to deal with the complex concepts of data
normalization. You could get a whole book on the subject, so we've had to
condense the topic considerably
We looked firstly at the definition of a key and all the different types of
key.
140
I Sumr
Next, we looked at what normalization is and the reasons you need to have
a normalized database. We looked at the first three normal forms, explaining
how to get from one form to another and then relating the concepts to one
specific example, which we started from scratch.
We finally linked all of these things into our example database and created
the final set of definitions, five in total, which you'll need for the example
database. Now we have an empty database structure, that has referential
integrity and is ready to have data entered into it. This is what we'll look at
in the next chapter.
141
Chapter
Chapt
- * one-many relationship
primarykey
foreignkey
There is one feature on the diagram that we haven't yet discussed: the
concept of the one to many relationship. This is more database terminology,
but it simply means that for every single entry in table X, there can be
many corresponding entries in table Y. Looking at our example database,
this means that one employee could be responsible for many orders.
144
Im
However, each order can only have one employee dealing with it. The same
goes for customers; there can be one customer who makes many orders, but
each order can only have been made by one customer.
These relationships define the order in which tables can have data inserted.
Some tables will require data from other tables. These dependencies were
created in Chapter 4, when we included referential integrity in our database.
When we specifically added foreign key constraints, it became impossible for
us to put unrelated data into the database.
For example, the orders table requires an employee identifier. Yet how
could you insert data into the Orders table relating to the specific
employee, if there was no data within the Salespersons table? It could
well refer to an employee identifier that didn't exist. The SQL engine will
therefore prevent you from doing this with an error message along the lines
of 'no primary key value for the foreign key'. We will have to keep this in
mind as we physically insert the data into the database.
145
Chapter 5 - Manipulating Database Information
If no insert column list is given, then SQL will create a column list that has
ALL the columns in the table, listed in the order they appear in the table.
This means that the value list also has to have the number of columns as
the target table.
Query Insertion
The query insertion executes the query and puts the result into the table.
Again, the columns have to match up in position, number and datatype. All
the rows from the query result are inserted into the target table at the same
time. This is important as again this is the principle of sets! If just one row
violates any constraint on the table, then the whole insertion fails.
146
Inserting
Data
L
The INSERT IwO statement needs to know which table to put the values
into. Following the keyword VALUEs, there should be a list of requisite
values. It is also good practice to put the column names in after the table
name, although this isn't strictly necessary If you choose to ignore the
column names, however, you could be creating more work for yourself at a
later stage. Therefore, type in the following:
We can add records for which there is no empid with the same value, so
another valid entry to this table would be the following:
Similarly, if we try to add a record with too many or too few values
specified for the INSERT Iwo, this will also be rejected. For example, the
statement:
Rather than get you to type in every entry into the database, once you have
typed in this entry you will find that the rest of the data is already
provided on the example disk, and is already present on the example
database.
147
Chapter 5 - Manipulating Database Information
The next table we would populate is the customers table. All of the
required customers data is stored in a straightforward list of INSERT INTO
queries for the Customers table. This is also the case for the Inventory
table data. We could actually have inserted the data into these three tables
in any order, as none of them require information from another table.
However, when it's time to insert data into the Orders and OrderItems
tables, we have to watch carefully the order of insertion. The Orders table
needs to reference values from the salespersons table and Customers
table. Likewise, the OrderItems table references the Orders table and the
Inventory table.
We could insert all the orders in their table first, then add all the items
ordered. However, the correct way to do this is to first enter an order row
and immediately follow it with the items:
You will notice that all of the items are serially numbered within each
order. This isn't obligatory, but the combination of (orderid, detail) has
to be unique within the table. However, most front-end forms tools will use
an auto increment function or the database itself may provide an auto-
incrementing column. Both Sybase and SQL Server have an auto-
incrementing keyword IDENTITY which can be used in the CREATE TABLE
statement. Microsoft Access has a COUNTER datatype.
Selective INSERTing
You can probably guess that in many databases there is a lot of repetitive
data that needs inserting, and rather than insert the data line by line, you
can use a more powerful method. You can specify criteria for where data
should be inserted, but we need to know how to SELECT data first. We will
look at this in Chapter 6.
148
I U a
USDleingRw
Import and Export of Bulk Data
The SQL standard doesn't have any construct for doing bulk data loads into
tables, or for importing and exporting data from and to other file formats.
Every vendor has a utility program or extension to his SQL for importing
and exporting bulk data. The tools vary from product to product, and you
will have to read your vendor manuals to find out the means by which
they effect bulk data transfers.
There are two ways to find the rows that you want to work with: by a
logical search, or by a cursor. A cursor is a way of scanning a table to
change or remove one row at a time. As they are dealt with by the host
programming language and not by SQL, we won't deal with cursors in this
book. SQL-89 and SQL-92 treat them in different ways which means that
there are many different implementations available. We will therefore focus
on the searched deletion and searched updates.
Notice that the <table name> can't have a correlation name according to the
standard. However, this is a common extension to many vendor tools.
149
Chapter 5 - Manipulating Database Information
that while the rows are being marked, the entire table is still available for
the w•i•iE clause to use in the search condition.
The DELETE PROM statement removes zero or more rows of one table. We'll
deal with the rather odd concept of deleting zero rows in a moment. First
we'll look at deleting a single row. Since we added Larry Larder's record,
he has decided that this job is underpaid and he's moved on to better
things. This query removes the one row that you have added. We specify
his name, but if we searched by name we would also remove any other
employees who coincidentally had the same name:
A deletion that removes zero rows can seem a little strange, but remember
that an empty set is still a subset. So a statement like this that removes
anyone earning a salary of above $5000 is a valid query, even though there
is no-one on our database who earns that much:
At this point it seems wise to remind you to not forget the WHERE clause,
unless you do want to delete the whole table. You would have to type all of
the data in for that table, which is perhaps not what you want to do!
150
Ud i a
Rows
L- eltn
UPDA~TE Salespersons
SET salary a2100
WHERE enam -'Dale Dahlman,;
There's nothing to stop you updating several columns at once, if you specify
them within the restriction. If the top dogs of the company (those with rank
3), Faulkner Forest, Gloria Garcia, and Harvey Harrison had awarded
themselves another raise from $2500 to $3000, this would need to be
recorded. However, just to make it slightly more awkward, Kevin Kody
already earns $3100, so his record won't need amending. The code that you
would need is as follows:
151
Chapter 5 - Manipulating Database Information
The way that most SQL implementations do this is to make several passes
through the table. The first pass finds all the rows which tested TRUE for
the WHERE clause search condition; it skips those that tested FALSE or
UNKNOWN. This pass makes a duplicate of each qualified row, which we will
call the old and the new copy of the row.
The next several passes use the old copy and the SET clause statements to
make changes in the new rows. The important point is that the entire
original table is still available for the WHERE and SET clauses to use.
Finally, the new rows replace the old rows. If an error occurs during all of
this, then the table is unchanged and the errors are reported.
This works by making a copy of all the rows in a table called MW and
labelling the original columns table OLD in the first pass. There is no WHERE
clause to test the columns against so all of the rows are copied across from
the original table:
Pas,sOne
The second pass changes all of the values in the table Nw, column a to
those of table OLD, column b. It will do this on a row by row basis, where
the value for a is 1 in the first row of NEW, then it becomes the value for b
in the first row of OLD:
152
-H
Pass Two SET NEW a = OLD b
The third pass changes all of the values in the table Nzw, column b to those
of table OLD, column a. Again, it will do this on a row by row basis, where
the value for b is 10 in the first row of mw, then it becomes the value for
a in the first row of OLD:
The fourth pass just renames the table NEw, table OLD and the original table
can be discarded:
153
Chapter 5 - Manipulating Database Information
In the first UPDATE, columns a and b will swap values in each row. In the
second pair of UPDATES, column a will get all of the values of column b in
each row, and then that same value will be written back into column b.
There are some limits as to what the value expression can be. The same
column can not appear more than once in a <set clause list>, which
makes sense given the parallel nature of the statement. Since both go into
effect at the same time, you wouldn't know which SET clause to use.
However, the <set clause list> doesn't have to contain only simple
values on the right of the assignments. You can also use arithmetic
expressions to update the table. We will look at how to do this in
Chapter 9.
If there is no WmERE clause, then all rows in the table are changed. The final
state of the table can't violate any constraints on the table.
Summary
In this chapter, we took an overview of the all tables that we've created so
far and the database structure as a whole. We looked at the process of
inserting data into the database structure and why it's so important to make
sure the tables are populated in a certain order. Once that order has been
determined, the actual process of insertion becomes fairly simple. We also
considered the methods by which specific records could be deleted from
tables and how fields could be updated. We will return to all three methods
of data manipulation in Chapter 9, when we consider how to use them in
conjunction with SELECT queries. Next, we'll consider how you can extract
the data you want from the database, now it's been stored correctly.
Exercises
1 Make up an order number, say 9999, and try to insert the items into the
OrderItems table without first inserting a row in the Orders table. What
happens?
154
I-
What happens?
(Hint: Remember that when the columns are not specified, the INSERT
statement will try to use the DEFAULT values given in the table
declaration.)
3 Black Cat Stores, customer number 33, has placed one order, with order
number 6279. The items on it are:
But as everyone knows, if you have a perfectly good 'thingie' then you
don't need a 'thingabob'. Delete it from the order.
4 There has been a recall of gizmos! Yes, that unfortunate accident with all
those dead fuzzy bunnies was a result of a defective gizmo. Delete all
the orders.
155
Chapter
Chapte
In its simplest form, the SELECT command will simply return a whole table,
or specific columns and/or specific rows. The syntax begins with a simple
SELECT object FROM table. You specify what exactly it is that you want from
a particular table and this information is returned to you. This is known as
a query. Querying the database doesn't affect the structure of the database.
Nor does it affect the ordering of the data; it simply presents it in a certain
way to the user.
The WHERE clause acts as a filter. In the first chapter we said that for a
query you would say "SELECT jobs paying over $5 an hour FROM the table
of railway jobs." However, this isn't quite the whole truth. If you only wish
to see certain data, then you can formulate a rule which only accepts the
data you want to see. You then "stick" this rule on the end of the SELECT
statement under the WHERE clause. Therefore, the new clause would read
"SELECTjobs FROM table of railway jobs WHERE wage > $5."
158
-I The SELEC
To build more complex queries, you just keep adding clauses. Imagine you
want them in order with the highest wage at the top. In this case, you
would add another clause at the bottom of the query, which would carry
out the command ORDER BY. This command carries out a sort of the wage
which can be specified in either ascending or descending order. The way it
works is to simply take the job with the highest wage, display that row
first, then look for the second highest job, display that, and so on.
Now imagine that you don't want information from only one table, you
want it from several. The SELECT statement combines two relational
operations, join and projection, in one statement. The following boxes and
diagrams look at these operators in a little more detail. It will be worth
your while getting familiar with them as they are pretty fundamental, and
very useful.
A join takes two tables and builds a new table from them by
concatenating the rows from one table to the rows of the other.
Table A
UNION
1.
2.
2.
3.
3. Union:
4. a set operator
4. that combines
5.
Table B
V 5.
A.
B.
two tables. Done
with a UNION
operator in SQL.
A.
B. C.
C. D.
D. E.
E.
159
Chapter 6 - Querying the Database - the SELECT command
Table A
Projection:
a relational operator that
removes columns from a table.
Done with the SELECT clause
of a SELECT statement.
The rules for using these operations are given as logical expressions. The
expressions can be simple, or they can be very complex, but they can
always be reduced to Boolean expressions with comparisons or function
calls connected by AND, OR and NOT.
The real trick in SQL is learning to write a clear specification of what you
want from the database in these logical expressions. The experienced
programmer knows that analysts don't always give a clear specification from
which to program. The burden on precision will inevitably be on the SQL
programmer. We can handle it!
160
I S Tabl
This statement is one whole command that will simply get all the rows in
the single table, and display only those columns which are named in the
SELECT clause expression or column list in the given order.
uELECT * FRMSalesperons;
i
Type this in and you will get a list of all the salespersons and all the
corresponding information contained about them in the Salespersons table.
If you don't want all the information, (for example, you just want a list of
the employee names), then you simply supply the column name, in this case
ename.
You can also select a list of specified column names, as long as you divide
each name by a comma. The SELECT * option is the shorthand for a list of
all the column names in the given table in the order in which they appear
in the table declaration.
161
Chapter 6 - Querying the Database - the SELECT command
For this table, the SELECT * statement is actually equivalent to the following
code, and when you type SELECT * this is what is executed:
This is where problems can occur. The SELECT * statement might display
the column headings empid, ename, rank and salary. You then add the
phone column into the table using the ALTER TABLE statement.
Because of the shorthand expansion rule, the addition of the new column is
supposed to have no effect on any existing query expression. However,
when you recompile query expressions, you will see the new column and it
could change your results. Even though the same statement SELECT *
effectively means the following, it might be that the Salespersons table now
contains an extra column:
This is the original table you created; the first version of the table, minus
the new column you have since added. Or you might see the equivalent of
this:
Check to see how your SQL product actually handles all this.
162
SEE
n
This also affects the SQL-92 JOIN operators, which are
discussed in Chapter 7.
In SQL-92, you could also use TABLE <table name> in place of SELECT *
FROM <table name>, but not many products yet allow this shorthand.
' I " is the concatenator we saw in Chapter 3. This statement takes ename
and empid and adds them together as one string, which is displayed
beneath the column heading combine. If both columns contained INTEGER or
DECIMAL datatypes, then you could use arithmetical operators (+,-,%,)
instead of the concatenator.
a eame, salary
SELECT FROM
Salespersons;
Empty
Rows
163
Chapter 6 - Querying the Database - the SELECT command
We then go to the FROM clause and get the table listed there.
ename salary
Andrew Allen 1000
Burbank Burkett 1000 Details from
Salespersons
The ALL clause says that you keep duplicate rows, while the DISTINCT
clause indicates that you remove redundant duplicates in the final results.
The working table in the FROM clause is used to fill in the columns in the
SELECT clause. In practice, the keyword ALL is never used, since it is the
default of the clause.
Let's try a more complex one table query, using the following:
Foobar
b a
a bc
1 23 10
1 23 10
2 17 11
3 32 12
4 41 NULL
You've already got all the necessary building blocks to create this table, so
you can do it yourself. SELECT * FROM Foobar will enable you to check if
you have created the table correctly
The text which creates and fills this table can be found in the
Appendix B.
164
-SIELEC
'Blatz' a b fred
E1atz 1 23 230
B1at- 2 17 187
Bl1tz 3 32 384
BIatz 4 41 (NULL)
the first column is assigned a constant string value. Since we didn't use an
AS clause to give it a name, it has none. However, for display purposes,
most interactive query tools will use the same string as the column name.
The second and third columns keep their original names and their values.
The fourth column has a name because we gave it one with an AS clause.
We also gave an expression for computing its values. The expression can
involve operators, functions, and even subquery expressions, but let's keep it
simple for now. Finally, when we have the whole answer table, the
DISTINCT option removes one duplicate row from the results (expressly, the
row 1, 23, 10).
This is how SQL is able to build its tables, one row at a time.
165
Chapter 6 - Querying the Database - the SELECT command
The WHERE clause is used to filter down the amount of information you get
from a query Without it, you'd just get every row in the file reeled off. In
terms of relational database theory, the WHERE clause is called a restriction. It
is another basic relational operation, like the two we met earlier.
Table A
I I
Restriction:
a relational operator that
removes rows from a table.
Done with the WHERE clause
of a SELECT statement.
I I
I I
166
Cl
evaluate to FALSE). A statement containing WHERE will get all the columns in
the single table, and display only those rows which test TRUE when the
search condition is applied to them. That also means that if the search
condition is FALSE or uNKNowN, that row will be removed from the results.
Let's use this concept in conjunction with the Foobar table to ask the
following:
The procedure is just as before, but the final step is to filter the working
table in the WHERE clause. Imagine this as the working table (this is
assuming the duplicate row has already been removed):
'B1atz: a b lfre
'Rlatz' 1 23 230
'Blatz' 2 1.7 1.87
'Blatz' 3 32 384
'Blatz' 4 41 NULL
167
Chapter 6 - Querying the Database - the SELECT command
The WHERE clause take the first row. It evaluates (lO*l) >=23. The condition
evaluates to FALSE. The row is removed. The second row (10*2)>-17. The
condition evaluates to TRUE, so this row remains. The third row goes to
(10*3)>=32, which is FALSE again and so that row is removed. The final
row is also FALSE. Hence, there is only one row in the answer.
If a column name appears in two tables, you have either to rename one of
the columns with an AS clause, or fully qualify a column name. That is, you
write it in the form "<table>. <column>" so that the SQL engine can tell
them apart. If we use the last example, the Foobar. a would refer to the a
column. This is known as a qualified column name.
NULLs in Comparisons
The problem with NULLS in comparisons and Boolean expressions is that the
law of tricotomy doesn't apply.
The law of tricotomy states for two numbers x and y, one and
only one of the following is true: either (x - y) or (x < y)
or (x > y). e.g. as two is smaller than three, two can't be equal
to or greater than three.
168
I B l
ng
- Ieaci
Therefore, you have to be careful. Consider the table Foobar and the query:
a b c
1 23 10
1 23 10
2 17 11
3 32 12
You would think that this query would return all the rows in Foobar.
Intuitively you would deduct that each value of c must either be greater,
smaller or equal to 10. However, you don't get the row (4, 41, NULL)
because of the NULL. That NULL isn't equal, less than, or greater than, any
value.
4M' The LIKE predicate. This is mainly of use when you only have a
rough idea of what you want.
M The IN predicate specifies a list of objects in its search.
169
Chapter 6 - Querying the Database - the SELECT command
SThe BETWEEN predicate looks for everything that can be found within
two specified objects or string patterns.
<like predicate>
<match value> [NOT] LIKE <pattern>
[ESCAPE <escape character>]
The expression "MNOT LIKE P" is equivalent to "NOT (M LIKE P)," which
follows the usual syntax in SQL.
If you are an MS-DOS programmer, then you are used to the idea of
wildcards for pattern matching. A wildcard is a special character that stands
for some character or set of characters. The two wildcards in the LIKE
predicate can only appear in the <pattern> string. They are the V' and _
characters. The ' ' character represents a single arbitrary character and in
this sense it is just like the '?' in an MS-DOS command file name. This
translates to the following:
The underscore was a bad choice, but we are stuck with it. With today's
clear, crisp laser printers, it is hard to count underscores by eye.
170
I hL
name or file extension to which it is matched. In SQL, the percent sign, in
effect, generates trial strings of 0, 1, 2, .. (n) underscores and tries those
patterns against the <match value>. Thus:
The <escape character> syntax isn't yet widely implemented. The <escape
character> is used in the <pattern> to specify that the character which
follows it is to be interpreted as a literal character, rather than as a
wildcard. The escape character is always followed by another escape
character, an underscore, or a percent sign.
171
Chapter 6 - Querying the Database - the SELECT command
match the bit your boss can remember to any entry in the customers table
that begins with the word "Black."
The only entry in the table commencing with Black is Black Cat Stores, so
that must be the one.
If the <match value> and the <pattern> are both character strings of
length zero, then the result is TRUE. If only one of them is a zero character
string, then you will get a FALSE result.
Things can be equal but not alike! A very important point that
is often missed is that two strings can be equal but not alike
in SQL. String equality in SQL first pads the shorter of the
two strings with rightmost blanks, then matches the characters
in each, one for one. Thus "Smith" and "Smith " (with three
trailing blanks) are equal. However, the LIKE predicate does
no padding in the <pattern>.
The IN Predicate
The IN predicate asks if a value is in a set of other values e.g., Is the letter
A in the set of vowels, or alternatively is the letter B in the set of vowels?
You can use this as a criteria for selection, such as select all the products
beginning with a vowel. The predicate would then have to check each letter
in turn. The IN predicate has two forms, one with a list of values and one
with a subquery. If you are a Pascal programmer, you already know the
value list version of this predicate. The syntax for the expression list version
is as follows:
172
Th
I
This really is shorthand for the following:
(<expression> = <valuel>
OR <expression> = <value2>
OR <expression> = <valueN>)
This predicate is handy in a CHECK () constraint to make sure that only the
correct values get into the table. While not required by the standards, most
implementations will scan the list for matches from left to right.
Unfortunately, most people will order the list by sorting it numerically or
alphabetically. If you put the mostly likely values in that order in the list,
the query may run faster.
For example, did anyone order any of the items that we had on sale?
Assuming that the items were as shown below, the query would be:
If you wanted to know what else was sold, apart from these items, you
could also use the IN predicate. The addition of the NOT operator to the
statement is a method of searching for everything, excluding those pre-
selected objects.
173
Chapter 6 - Querying the Database - the SELECT command
174
-I.
New programmers also tend to forget that the range includes the end
points, and that it has to be in the right order. Conversely, if you add a
NOT operator you effectively reverse the meaning of the statement, so it
looks for everything that doesn't fall between A and C, but for everything
from D to Z, or that which begins with a numeric:
If any of the three value expressions is a NULL, then the answer follows
directly from the definition.
4 If both <low value expr> and <high value expr> are NULL, then
the result is UNKNowN for any value of <value expression>.
MW If <low value expr> or <high value expr> is NULL, but not both of
them, then the result is determined by the value of <value
expression> and its comparison with the remaining non-NULL term.
175
Chapter 6 - Querying the Database - the SELECT command
IM-bIf <value expression> is NULL, then the results are uNKNowN for any
values of <low value expr> and <high value expr>.
There should only be the one entry in the result table. Conversely, you can
insert a NOT between the is and NULL statement and look for all the entries
in the reorderpnt that are not NULL.
The other three is tests are all identical to each other in structure. The user
provides a search condition and the result table is formed from all of the
rows in the table that satisfy the is condition. The following example looks
for all the items in the inventory that cost more than $10:
176
ThR
Watcom InteracliveS01
Rilc Edit Command Window Hel
description
Joohickey
gimmick
gizmo
:hingabob
Nvidget
:hachka
hingee
The use of the other tests should be intuitive, you simply apply them in the
same way. If you wish to reverse the test, you apply a NOT operator
between the is and second part.
177
Chapter 6 - Querying the Database - the SELECT command
This clause sorts the results into either ascending or descending order based
on a list of column names. The syntax is as follows:
The ORDER BY clause goes at the end of the SELECT statement. The result
table is sorted on the columns whose names are given in the <sort
specification list>, ordering from left to right. The keywords ASc and
DESC stand for ascending and descending order, respectively. The default is
to assume ascending order on a column. Here's a quick query which simply
flips the Foobar table you created earlier around the a column.
a. X
3 30
2 20
1 10
178
Su
Notice that a column position number can be used in place of a column
name. This is an old SQL-89 convention which is being phased out after
SQL-92, so you shouldn't use it. The reason that it exists is that the SQL-89
standard did not have a way to name expressions in the SELECT clause list
(the <expression> AS <name> syntax of SQL-92).
According to the standards, NULLs are grouped either before or after known
values; the position is implementation defined. After the NULLS are in their
position, the known values are sorted into ascending or descending order.
However, you will find many implementations that treat NULLS as if they
were values and will assign them a position in the collating sequence.
Summary
In this chapter you have introduced you to the SELECT statement. This
statement queries the database, but doesn't alter the database in any way
That's the fundamental difference between a Data Manipulation Language
and a Data Definition Language. The Data Definition Language gives you
the building blocks to create your database-the Data Manipulation Language
allows you to examine the structure and view it in different ways.
In the next chapter we introduce the concept of joining two SQL tables
together within a query
179
Chapter 6 - Querying the Database - the SELECT command
Exercises
1 Select all of the details of our employees who are ranked over 1.
2 Select only the descriptions of the parts that cost more than $12.00.
3 Select all of the employees who earn between $1800 and $2500.
4 Select all our customers who begin with the letter 'D'.
5 Select all of the orders made before the 16th November 1995 and list
them in order of date, with the most recent first.
180
I xe
Chapter
Joining Tables
Up until this stage of the book, you have only been able to make
rudimentary queries of one table. You'll soon find this too confining when
you want to perform queries of any real complexity. If you want to
compare data from two or more different tables, then you will have to start
joining tables together. This is traditionally a difficult topic, so in this
chapter the different types of join have been carefully separated to aid
understanding. We'll also introduce the joins in order of complexity
Table
A C 1 3
Table 2
B D 1 3
A C 2 4
B D 2 4
184
Cr
Cross Joins
Cross joins are the basic level at which relationships between data tables are
cemented. They are the fundamental background activity to all join
processes. It is possible to determine a Cartesian product in SQL in one of
two ways: with a comma or with the command CROSS JOIN. Here is the
syntax:
As before, the statement goes to the SELECT clause to set up the columns
for the final working table and then to the FROm clause to construct a
working table. If your SQL product has SQL-92 features, then you can use
either the keywords CROSS JoIN, or the comma. If your product uses SQL-
89 syntax, it will only accept the comma. The FROM clause builds the
Cartesian product of the two tables. This takes all the rows from one table
and concatenates them to all the rows in a second table. The new rows in
the resulting table have all the columns of both input tables, as well as all
possible combinations of their concatenations. Let's use two small tables to
illustrate this (if you're not using the Watcom run-time engine provided,
then you can find code for the tables in Appendix B):
185
Chapter 7 - Joining Tables
Watco
SO Intractie
I-le ai-t jommandJ winnow I2elp
L"
x b x
10 10 A
2 10 20 B
10 40 C
20 10 A
2 20 20 B
20 40 C
3 30 10 A
3 30 20 B
3 30 40 C
If you wish to reference either of the two columns named 'x', then they
have to be qualified in a query by the names of their source tables. The
two columns named 'x' would have to be Foo.x and Bar.x, to distinguish
them.
Once this CROSS JOIN is built, it is the working table in the FROM clause
that the WHERE and SELECT clauses operate upon. The WHERE and SELECT
clauses have to be able to tell all the columns apart, so if the two tables
have columns with the same name, you have to use the fully qualified
<table>. <colunm> version of the column name. Foo.x distinguishes itself
from Bar.x by the addition of the table name.
186
If you have named an expression in the SELECT
I
list with an AS subclause,
q
then you can use that expression name in the WHERE clause since it was
constructed in the CROSS JOIN.
Always keep in mind that when two tables are joined, they
are not physically connected. You are just viewing them as
though they were. A join has no physical effect on the
structure of your tables or database - it doesn't change them
in any way.
Equi Joins
We have detailed some of the theory behind joins, but we need to expand
on the simple CROSS JOIN and introduce the idea of matching information
and records across tables.
Normalization tends to split data and create many small tables. Sometimes,
normalization splits logically grouped information into two separate tables.
Information, such as dates and details about orders, end up being separated
from clients' names and addresses. This is because you would commonly
get tables devoted to personal details, and tables specifically for orders. To
be able to join the information and view the results in one table, there must
be a common column, identical in both tables. You can join the tables by
matching the record through this common column, where an entry in one
table equals the entry in the same column of the other table. This is known
as an equi join.
187
Chapter 7 - Joining Tables
BANK EXAMPLE
188
E
Customers Orders
CNAME CREDIT ORDERID EMPID CUSTID SALESDATE
Equijoin to A-
take place on
this CUSTID
The necessary SQL for this join would be as found in the command
window. The desired output is combined into one table. The first portion of
the result table would look like this:
. . .......
....
Watcorn Interactive SOL
File Edit Command Window Help
S. A,
189
Chapter 7 - Joining Tables
Now, let's look at an SQL-92 example of an equi join. If you were to join
two tables on their x columns, you would get the following result:
a x x b
1 10 1 A
2 20 20 B
In this situation, the term equi join means that the search condition was
based on equality. If the contents of one row 1 in table Foo, column x,
match the contents of the equivalent row 1 in table Bar, column x, then that
row is displayed in the result table of the equi join. Columns that are not
equivalent are eliminated from the result table, and don't appear (although
they are not eliminated from their actual tables). Compare this with the
CROSS JOIN, where each row of Foo was matched to each row of Bar.
Non-equi Joins
Using equality in the wHE-R clause is only one of many possible ways to
join two tables together, but it happens so often that we give it the name
equi join. There are other relational operators apart from equality that you
can use. There is nothing wrong with writing a non-equi join in this way:
190
I Nn
File Edit Command Window _Help
0 x x b
2 20 10 A
3 30 10 A
3 30 20 B
You can also introduce NOT, Am and OR operators with these joins to
further restrict the query We will look at using the Boolean operators in
conjunction with joins later in this chapter.
Theta Joins
Both equi joins and non-equi joins, and indeed any type of join that uses a
relational operator, are known collectively as theta joins. Theta joins compare
the data of different tables or restrict the result table to a specific range of
data. However, you should be careful when you construct theta join queries,
since these queries can produce unexpected results and be accompanied by
slow performance.
illustrate this rather cryptic idea, we need to refresh our memory as to how
we rename columns.
Correlation Names
In the previous chapter, we mentioned the AS clause as a method of
renaming columns. The AS clause can also apply to table names as well as
column expressions, and it creates what is known as a correlation name for
the life of the query. A correlation name can be thought of as an alias for a
column or table name. In the following example, the correlation name or
alias for ename is employee:
192
Se
Salespersons
EMPID
Salespersons
ENAME SALARY
RANK
Copy oftSalespersonsTable
193
Salespersons Tasks
TI and T2 are just aliases for the table salespersons. Once you have
created a copy of the table you wish to join, then you are ready to perform
an equi join on it.
If you try this, you'll find an immediate problem in the first record of the
result table:
The result table has joined instances of each record to the identical record in
the table copy. To eliminate this, you must stop the search from matching a
record to itself, so you will have to introduce a new condition into the
WHERE clause. The query must be able to identify that two records are
identical, and the only way to do this is if the two records in question have
the same primary or unique key. So the second condition must check if the
primary or unique key from one table equals the equivalent primary or
unique key from the table copy In Salespersons the primary key is empid.
So, when empid in Ti doesn't equal empid in T2, you know that the
records can't be identical:
Each pair is matched twice. The query searches down table 1 and gets the
first record, that of Andrew Alien. It searches down table 2 and finds that
Burbank Burkett has the same rank. It's fine up to here. However, the
query doesn't stop there. Once the record of Andrew Allen is finished with,
the next record is that of Burbank Burkett. It compares the rank of
Burbank with each record in table 2 and comes across the record of Andrew
Allen, even though this pair has already been discovered. What can you do
to prevent this?
The solution again involves the primary key empid. If we were to introduce
the empid into the SELECT clause, then you might notice a way of selecting
only one of the pairs:
If the empid isn't identical in each record, then for one occurrence it must
be smaller in one record than the other, and therefore larger for the other
occurrence when the records are compared in reverse. So, if we use a non-
equi join, where the records are only joined if [Link] is smaller than
t2 .empid, then one of the two occurrences will be removed. The final result
will look like this:
196
mU
We will now move to a more complex example, where the join takes place
on two columns. Imagine that you want to investigate purchasing patterns
to discover if people buy doodads on the same day they buy chachkas. We
would want to write a query to find pairs of orders from the same
customer on the same day. We would use the two columns orderid and
salesdate on which to perform the self join:
The FROM clause makes a copy of the Orders table and names it oi, then it
makes a second copy and names it 02, and finally builds a working table
with the CROSS JOIN. The first two predicates in the wHERE clause restrict
the results to orders from the same customer on the same day. The last
predicate is again to prevent pairs of the form (a, a), (a, b) and (b, a) from
showing up in the results.
197
Chapter 7 - Joining Tables
This example computes the total value of each item sold by each salesman.
The three tables joined are the Inventory, Orders and OrderItems table.
The OrderItems orderid is joined to the orderid in the Orders table.
Imagine an intermediate table made from the OrderItems and orders
tables which contains a column for partied:
Orders (01)
6099 1 1995-12-15
6109 2 1995-12-15
Intermediate Table
np id part id city
)1 1002 3
A 1003 3
A 1004 3
Order Items (011)
I
6099 1
6109 2
6099 3
The partid in this intermediate table is then joined to the partid in the
Inventory table. This creates a result table, which consists of three tables
being joined:
198
- I -
Intermediate Table
Result Table
1002 doohickey 69 25
1003 2 23 30
1004 gadget 71 20
The query selects only what it needs to help create the final result table.
The formal model of SQL says that the engine computes the cRoss JOIN on
all the tables first, and then does the required restrictions. In the real world,
this would simply take too long. What actually happens is that the SQL
engine works with pairs of tables and tries to decide on the best sequence
of steps, in order to reduce the execution time of the query, whilst still
getting the right answer.
199
Chapter 7 - Joining Tables
This means that the order of execution is out of your hands; the query
optimizer does this. In this case, the optimizer could decide to assemble the
join between Inventory and OrderItems first, then join that result to
Orders, or it could join Orders and OrderItems first, then join that result
to the Inventory table. How the optimizer decides how to do its job is an
advanced topic, and we are not going to talk about it in this book.
Basically, it looks at the available indexes and certain statistics to make its
decision, but if you wish to know more, we suggest you check an advanced
SQL title.
This syntax isn't yet fully implemented in most SQL products, but you can
expect to see more products using it in the future.
200
Jo
could be written in SQL-92 as:
This SQL-92 statement works by doing the same pruning of the CROSS JOIN
as the FROM and WHERE clauses did in SQL-89. The differences are as
follows:
1 You use parentheses to nest these statements inside each other and thus
control the order of their execution. You can also use them with the
other kinds of JOINS which we will discuss later.
2 You can use an As clause to give their results a name. You can also use
an AS clause on each <table reference> to give them correlation
names too.
An outer join takes two tables and displays the records of one table where
there is the matching record in the other table. The method by which an
outer join is created depends on which table is referenced first within the
query A left outer join would display the records in the table referenced
first (the 'left-hand' table), together with any matching entries from the table
referenced second (the 'right-hand' table).
i•iill
The records in the left-hand table that don't match with any entries in the
right-hand table are displayed. Records from the right-hand table that don't
have a match on the left are not displayed. This, therefore, brings the need
201
Chapter 7 - Joining Tables
for a right outer join. This displays the records in the right-hand table, with
or without a match in the left-hand table, and records that match in both
tables.
Imagine that we just added the Acme Company to our list of customers.
Now we would like to do a join between the table of customers and the
purchases they made to show us how much total business we did with
each customer. One of the customers has just as many returns as purchases,
so the total amount of business with them is zero dollars. However,
compare this to the fact that we have not done any business with Acme -
their total should be a NULL. Stop and think about this; there is a big
difference between never having done business with Acme and the customer
who cancels orders.
202
O Joi
a x x b
1 10 10 A
2 20 20 B
3 30 (NULL) (NULL)
Sttstc an
-stlmated J rows in query (I/U estimate 2)
PLAN> Foo (seq), Bar (seq)
The table Foo was preserved. Foo.x matches Bar.x for the first two
entries, as they both have the same values. When Foo.x had a value of 30,
that couldn't be matched to rows remaining in the Bar.x column. That row
was padded out with NULLS.
203
Chapter 7 - Joining Tables
Window Help
File Edit Command
ax x b
1 10 10 A
2 20 20 B
(NULL) (NULL) 40 C
SELECT
FROM Faa
PULL OUTER JO0IN Bar ON Foo. x - Ear at;
204
O Jon
1 10 10 A
2 20 20 B
3 30 NULL NULL
NULL NULL 40 C
Before the SQL-92 standard, many vendors implemented different syntax for
outer joins, which involves using some sort of 'extended equality'
comparison operator. However, they don't always work in some
implementations and you should avoid them if you can. For example, this
query:
SELET *
FROM Orders AS 01, Orderltems AS 11
WHERE [Link] -m ii-orderid
AND qty >- 2;
where "-=" means "preserve the left-hand table" can be equivalent to either
this:
SELECT*
FROM Orders AS 01 LEFT OUTER JOIN T OrderItems AS I..
ON ([Link] = [Link] AND qty >- 2);
or this:
SELECT *
FROM Orders AS 01 LEFT OUTER JOIN T OrderItems AS 11
ON [Link] = [Link]
WHNERE qty >- 2;
The order of execution of the predicates matters! If we first remove all the
OrderItems rows with a quantity not less than two, we will create rows
that don't join to the Orders table. Consider this abbreviated version of the
two tables:
orders OrderItems
orderid ordered qty
6099 6099 3
6109 6109 1
6128 6128 2
6129
205
Chapter 7 - Joining Tables
Then we apply the "qty >= 2" predicate to get the final answer:
Now look what happens if we first apply the "qty >= 2" predicate, which
affects only the Orderztems table, thus:
See the difference? Sybase and SQL Server do this one way, Oracle does it
another and Gupta turned the method into a system parameter that could
be set by the D.B.A. Queries with multiple outer joins, or queries where the
same table appears both as preserved and unpreserved have serious
problems with this notation.
Now you have an idea of the two major different types of joins - inner and
outer joins. Every other type of join we have learned can occur within the
context of an inner or an outer join. It is one of the most central concepts
206
ISmmr
to the understanding of SQL and relational databases. Without a join, you
can't have a relational database. Before we move on to the next chapter,
which looks at the nesting of one SELECT statement within another, you
must feel comfortable with the ideas discussed here, as they will continue to
reoccur throughout the rest of the book.
Summary
Here is a list of the different types of join you can specify within a SELECT
command and a quick summary of what they do:
_ The cross join, which simply concatenates two tables and gives you
an idea of how relationships between tables work at the most basic
level.
A The equi join, which concatenates two tables according to a search
condition specified by the user, such as wKERE column 1 = column 2.
A The non-equi join, which is identical to the equi join except that it
uses the lesser than and greater than operators, rather than the
equality operator.
4 The self join, where you can specify a table by two different names
and then join it to itself, using each name.
AS The three table join, which is self-explanatory.
f Inner joins, which involve eliminating those rows which don't match.
4 Outer joins, which preserve the rows that don't match and pad the
remaining columns out with MULLS.
4 Different types of outer join, which join the tables in different orders,
such as a full outer join, a left outer join and a right outer join.
Now if you aren't worn out, you should have a go at the exercises over the
page to see how much of this chapter has really sunk in!
207
Chapter 7 - Joining Tables
Exercises
The boss just walked in and wants some queries to answer these questions:
4 Who has ever ordered anything that begins with the letter 'g'?
5 Which items (not just their partidas, but their whole descriptions) did
we sell for Christmas 1995?
6 Which items (not just their parties, but their whole descriptions) did
we sell on all the days, except Christmas 1995?
208
Exrie
209
Chapter
Chapte
There are rules that govern how the partitions are divided. A partition of a
set divides the set into subsets in the following ways:
Salespersons Table
Continued...
212
Th
RU
empid enarne rank salary
As an example, we'll look at almost the most basic query you can make. It
just groups the three different ranks in the table. You might think that we
could achieve the same ends with the SELECT DISTINCT clause and you'd
be right. However, it is imperative that you understand the concept of the
working table, which is introduced next, via a very simple example. The
most useful aspects of the GROUP BY clause are found when used in
conjunction with other features, which will be introduced later. So we'll take
this query first:
SELECT rank
FROM Salespersons
GROUP BY ran.k;
The query will go first to the FROM clause (and the WHERE clauses if there is
one) to construct the working table specified therein. In this case, this is the
full Salespersons table. It could be a very complex query, but the important
point is that we get an unnamed working table first, then pass it along to
the GROUP BY clause.
The GROUP BY clause will break the working table into groups which have
identical values for the grouping columns. Since we are grouping by ranks,
each group is a subset of rows which have the same rank.
There will now be a working table which is called a grouped table, and all
operations are now defined on its groups rather than on the original rows.
In effect, the original working table has disappeared. The order of the
grouping columns in the GROUP BY clause doesn't matter, but since they
have to appear in the SELECT list, you should probably use that same order
to make it more readable. We'll get to that point in a little while. The
grouped working table looks like this:
213
Chapter 8 - Grouped Tables and Aggregate Functions
The results give us three groups. As we only asked for the ranks to be
grouped, the final result will be reduced to the single column that we asked
for:
The subsets would be based on those two columns and we would get a
separate group for Mr. Allen and Mr. Burkett, since they are both employees
of rank =1, and salary -$1,000. However, each Mr Cox, Mr Dahlman and
Mr Everling will get a separate group because, although they share a rank
-2, they all earn different salaries. The table would look like this:
214
W
Never include a primary key in a GROUP BY clause. Since a
key is always unique, you will always get one row per group.
Let's do another query and ask for the ranks which have people earning
more than $2,000.00:
SELECT rank
FROM Salespersonis
WHE salary >- 2000.00
GROUP BY rank,
As usual, the query will first go to the FROM clause, then to the WHERE
clause and remove disallowed rows. At this point we have the following
working table:
Now we group the table by rank as before and we have only two groups:
215
Chapter 8 - Grouped Tables and Aggregate Functions
The important point to remember is that the original working table, with all
the rows, has been replaced with a grouped table.
Why do the grouping columns have to be in both the SELECT and GROUP BY
clauses? The reason is that the SELECT list is now a description of the
groups and can only contain group attributes. Group attributes are grouping
columns and aggregate functions - we'll discuss this later in the chapter.
If you still can't grasp why this query is wrong, think about what value for
the employee name column should be returned in each rank group. Should
it be the longest name, the shortest name or a NULL? There are many
employee names, yet only one rank in the result table. This query would
probably be greeted with an error message. Therefore, the query makes no
sense.
216
G B
Problem
The expression with the division is going to return the salary expressed in
thousands of dollars. So why doesn't this work? The expression we named
"thousands" is calculated in the SELECT clause. When we do the grouping,
the SELECT clause has not yet been executed, so we can't see the
calculation. You will probably be greeted with some form of error message,
informing you that you need to have salary in the GROUP BY clause.
Having said all of that, you will sometimes find SQL implementations
which allow this as a vendor extension to the language. Avoid using this
extension, as it destroys portability
The practical problem that these early implementations found were that you
quickly got too many groups and that the NULL was often used for a single
class of missing values. For example, a hospital admission form would use
a NULL in the diagnosis column to show that a diagnosis had not yet been
made. While it's unlikely that each patient would have the same disease
(and therefore be placed in the same group), it's reasonable to treat
undiagnosed patients as a group. This is another area where SQL gives
uumomW the benefit of the doubt.
Right now, you may be thinking that a GROUP BY clause is a clumsy way of
replacing a SELECT DISTINCT statement. Be patient - as mentioned earlier,
the real use of the GROUP BY clause is with aggregate functions, which we'll
discuss in the next section.
217
Chapter 8 - Grouped Tables and Aggregate Functions
While statistics can get very complicated, SQL has only basic,
well-understood functions - remember that SQL is a database
language and not a computational language. You should also
consult your particular product's manuals to find out the
precision of the results for exact and approximate numeric data
types.
The standard aggregate functions are the count (or tally), the average (or
arithmetic mean), the sum (or total) and the extremes (minimum and
maximum values in the set). The count and the extremes will work on any
data type, but the average and the sum apply only to numeric values.
Obviously, we can count any distinct object. The extremes are based on the
highest and lowest numeric value for numeric data types, the highest and
lowest sorting order for character data types; and the earliest and latest
chronological values for temporal data types. Average and sum would
clearly make no sense for character and temporal data types.
There are two options, ALL and DISTINCT, which are shown as keywords
inside the parameter list. Putting keywords inside parameter lists is one of
218
Agea
- ucton
the strange things about the ISO/ANSI Standard SQL syntax, which makes
SQL look different from other languages. You will see this in other places
too.
Aggregate functions can't be nested inside each other; for example, sum
(AVG (x)) is illegal. Nor can the value expression be any kind of subquery
expression; for example, AVG (SELECT columnl FROM SomeTable WHERE... )
is illegal. However, you must watch out for NULL values in the parameter if
you use an expression, since the rules say that you do the work on the
expression first. In this expression, a NULL commission for a non-sales
employee will make the whole expression be MULL and the sum () function
would remove it before doing the calculation.
For each of the functions we'll consider how they act with the ALL
keyword, or default option. We'll then take a look at the DISTINCT
keyword.
This returns the number of members in the <value expression> set. The
NULLS have been thrown away before the counting took place, and an empty
set returns zero. The best way to read this is as "Count the number of
219
Chapter 8 - Grouped Tables and Aggregate Functions
known (non-null) values in this expression," stressing the word known. For
example, using the Salespersons table again, the following query:
The syntax for the DISTINCT sub clause is virtually identical to the previous
syntax:
This returns the number of unique members in the <value expression> set.
The NULLS have been thrown away before the counting took place and then
all the redundant duplicates are removed. Again, an empty set returns a
zero. However, the following:
would result a result of 3, because there are three ranks (1, 2, 3) to which
an employee can aspire.
This returns the numeric total of all known values. This function only works
with numeric values, which again makes sense. An empty set returns a
NULL result. This may seem a little strange to you if you are expecting to
220
Th
U Function
get a zero result, but if you give it further thought, there is a difference
between an empty set and a set that sums to zero, such as (5, -5,-4, 4). An
example query which would find the sum total of all of the salaries would
be as follows:
SUM(salary)
20400 00
This returns the numeric total of all known, unique values. The NULLs and
all redundant duplicates are removed before the summation took place. As
before, an empty set returns a NULL, not a zero. If you were to total only
each distinct salary, then the query would run as follows:
221
Chapter 8 - Grouped Tables and Aggregate Functions
------
-- -- ----- - W acom 1ntr
,%v ac e S,
[01- M
IFile tdt _Lommond Window Help
E:
SUM(distinct salary)
13400.00
The above expression returns the average of the members in the value
expression set. An empty set returns a NULL. Remember that in general, AVG
(x) isn't the same as (sum (x) / COUNT (*)), because while the sum (x)
function has thrown away the NULLs in column x, the COUNT (*) has not.
SQL programmers who are new to the language often forget that COUNT (*)
counts the NULLS. This means that if you have NULLS in the value
expression, then (sum (mycoluzan)/COuNT (*)) isn't the same as (sum
(mycolumn) /COUNT (mycolunm)) or AVG (mycolumn). If we wished to find
the average salary of the salespeople, we would use the following query:
222
Th
V
File Edit Command Window Help
AVG(salary)
226,b 1b3u1616B6117
As before, this returns the average of the distinct members in the <value
expression> set. The SQL engine is probably using the same code for
totaling in the AVG () as it used in the sum () function. This leads to the
same problems with rounding and truncation, so you should experiment
with your particular product to find out what happens. This aspect of AVG
is only of much use when used with the GROUP BY command. Here is a
query which will find the average salary per rank:
223
Chapter 8 - Grouped Tables and Aggregate Functions
AVG(distinct salary)
1000.0000000000000
2100.0000000000000
3050.0000000000000
In the AVG (salary) case, we are dividing by 9, since this is the number of
rows involved. In the AVG (DISTINCT salary) case, we are dividing by 6,
since that is the number of rows involved after we throw away duplicates.
224
IThExrm
The MAX Function
MAX ([ALL] <value expression>)
The above expression returns the greatest known value in the <value
expression> set. Here is an example query which will find a maximum
salary:
MAX(sal"ry)
3100.00
This function will also work on character data types to return the highest
string in the collating sequence which will be the last one if you sorted the
list. For temporal data types, it returns the one furthest in the future not
the most recent date, which would involve looking at the current date and
time; this, then changes over time.
An empty set or all NULL sets returns a NULL. Technically, you can write
MAX (DISTINCT <value expression>), but this is the same as MAX (<value
expression>). This form is cited for completeness, but it's never used.
225
Chapter 8 - Grouped Tables and Aggregate Functions
The above expression returns the smallest known value in the <value
expression> set. The mim function has exactly the same format as the other
functions:
WllN(salaxy)
1000.00
Because there is only one minimum value, this is all that is returned, even
though there is more than one person earning that specific salary. Again,
this function will also work on character data types to return the first string
in the collating sequence, which will be the first one if you sorted the list.
For temporal data types, it returns the earliest DATZTIME. Just as before,
HIN(DISTINCT <value expression>) exists, but it's cited only for
completeness as it is hardly ever used.
226
IEO
GROUP BY and Aggregate
Functions
The main reason for using a GROUP BY clause is to get aggregate functions
for each of the groups. Since a descriptive statistic is a group property, it
can go in the SELECT clause list of a grouped query The results will be the
grouping columns and the aggregate function results for each group. For
example, using the Salespersons table again, we can ask the following,
which will again first give us a grouped table:
Here is the grouped table from which the SQL engine will compute the
aggregate functions within each group:
227
Chapter 8 - Grouped Tables and Aggregate Functions
1 2 $2,000.00 $1,000.00
2 3 $6,300.00 $2,100.00
3 4 $124100.00 $3,025.00
You can use any of the aggregate functions in expressions, but you should
be careful. There is a big difference between AVG (salary + commission)
and AVG (salary) + AVG (commission) when you have used NULLS for the
commissions of employees who don't get commissions! Another problem is
that although algebra might say that sum (salary/2) is the same as sum
(salary)/2, your SQL compiler can get slightly different results, due to
cumulative rounding errors.
Using our example, let's attempt a slightly more tricky query to discover
whether or not we can find the total quantity of items that the customers
have bought, and then compare them in descending order to find out who
buys the most goods.
The initial problem is that the quantity of an item ordered and the customer
details are in two different tables, with no common columns. That means
they have to be joined via a common table. We only need the customer
name and the sum of the quantities that they purchased. We then group
these according to customer name. This provides a disorganized list, so we
use the ORDER BY clause to sort the final result table. The complete query
looks like this:
228
I GU
WacoIneatv SO
File Edit _Command Window Help I
SUM(qty) name
78 Ladera Enterpri
65 DRTMarine Lab
47 Cardiff Industr
45 Kalakaua Corpor
43 Monterey Univer
38 Quinton College
37 Rensselaer& Co
36 Mission Hills I
35 Blumenfeld Educ
34 Wellesley Inc.
29 Berryville Supp
28 Mentone Enterpr
23 Alexandria Liqu
19 Ridgewood-Berkm
229
Chapter 8 - Grouped Tables and Aggregate Functions
The grouped working table for this query can compute a COUNT (*) for
each group, and the HAVING clause checks that particular coUT (*),
retaining only the groups which passed its tests.
It's also worth noting that some older versions of SQL would
require that the SELECT clause also has COUNT (*) in it. This
is because they materialized the grouped table with all of the
aggregate function expressions in the SELECT list first, and
then passed that working table to the HAVING clause. This isn't
standard SQL - check out your product to be sure.
230
I
that using a SELECT DISTINCT with a GROUP By is at best redundant; at
um
r
worst, it will cause extra sorting and make your query take longer to run.
If there is no GROUP BY clause, then the HAVING clause will treat the entire
table as a single group (according to the SQL-89 and SQL-92 standards).
The easiest way to think of this is to imagine a nameless, mythical and
invisible column which has only one value in it, and a mythical GROUP BY
clause on it in your query. In practice, however, you will find that many
older implementations of SQL require that the HAVING clause belongs to a
GROUP BY clause.
Summary
In this chapter we've considered how to tailor your output to a desired
format. SQL doesn't provide good presentation facilities, and turning a
query into a presentable report can require a little work. The GROUP BY
clause, which we introduced first, is your main ally here. On its own, it
provides little more use than a SELECT DISTINCT statement, but when
combined with the aggregate functions COUNT, suM, AVG, mIN and zAx, it
provides a valuable way of organizing data for analysis. Finally, we
introduced a new restriction, the HAViNG clause, which can be used with the
aggregate functions.
231
Chapter 8 - Grouped Tables and Aggregate Functions
Exercises
1 What is the average salary for the Salespersons?
5 How much money did we get from each customer? What was the
average order for each customer?
232
Exrie
233
Chapter
Using Subqueries
Once you are familiar with the SELECT command and its basic components,
you can then move on to more complex queries. There's a special feature
within SQL that lets you use the output from one query as part of another
statement. This is known as a subquery. With subqueries, you can use the
result of one query to answer the WHERE clause of another. They can be
used to create very specific demands of the database. They are not confined
to queries alone. You can also use subqueries with other features of the data
manipulation language.
I An introduction to subqueries
-- The nested scalar subquery and the correlated subquery
I The IN predicate
The ALL and ANY predicates
Subqueries
A subquery is a SELECT statement which is contained inside another
statement. A subquery can be located in the following parts of the following
statements:
The subqueries are used in special predicates which convert the results of
the subquery into a chain of comparison predicates, connected by Boolean
operators, at run time.
236
I - -
you understand how subqueries function here, you will understand how to
use them in other statements. There are several different versions of these
types of subqueries. We'll look at the most basic first.
cname
Black Cat Store
The nested query returns the highest order identification number in the
Orders table. The second query then displays the customer name from the
customers table, which has to be linked with the orders table first, which
is in turn is linked with the highest order identification number calculated
by the nested query.
237
Chapter 9 - Using Subqueries
Since the mAx () function will return only one value, the subquery can be
used with a comparison operator. If you accidentally write a subquery
which returns more than one value, you will get an error message, or else
the system simply won't return an answer to you.
This shouldn't be confused with there being several values in the Orders
table which match the one value returned by the nested query In this case,
all matches in this table are displayed, together with the corresponding
values of custid. This scenario couldn't have happened for orderid, as
orderid is a primary key within Orders. However, if you had been
searching for the customers who had made an order with the employee
with the highest empid, then your query would resemble the following:
Cname
Blumenfeld Educ
Kalekaua Corpor
Quinton College
RensselEer & Co
:idgewood-Berkm
Durell Equipmen
238
-e
Predicates and Relational Operators
We've already met the IN predicate in Chapter 6. It can also be used in
conjunction with subqueries. Remember that the IN predicate tests for set
membership. In this example, the subquery tests for all customer
identification numbers that made an order on December 12th, 1995.
SELECT name
FROM Customers
WHERE custid IN (SELECT custid
FROM Orders
WHERE salesdate= '1995-12-12 );
The AmY predicate can be used in conjunction with any of the relational
operators used by SQL. The relational operator is used to compare the
variable with every value in a specified column. Generally, there are four
separate possible outcomes.
239
Chapter 9 - Using Subqueries
Wa-
SL co Iteaciv /
File Edit Command Window Helo
II Daa
name
Ladera Enterpri
Mission Hills I
Monterey Univer
Berryville Supp
Mentone Enterpr
Durell Equipmen
240
N
Some NULL Values and Some Non-NULL Values
If the subquery has non-NULL values, then the predicate becomes equivalent
to the following:
We'll now take two search conditions that look similar, but are not
necessarily the same, and demonstrate how a subquery works without and
with the ANY predicate. They will both return the same results unless there's
a NULL value in the answer.
For this example let's assume that we are using a table which has three
values in a column y, (1, 2, NULL). We'll call this table Sometable.
Sometable will have both search conditions applied to it:
Sometable
241
Chapter 9 - Using Subqueries
Let's assume that the value of x is zero, so that the ANY predicate expands
into ((0 >= 1) OR (0 >= 2) OR ((0 >= NULL)).This in turn becomes (FALSE
OR FALSE OR UNKNOWN). If you utilize the rules of NULLs and boolean
operators which were discussed in Chapter 3, then you will discover that
the result is expanded out into UNKNOWN, not FALSE! (FALSE OR UNKNOWN
always goes to FALSE)
Tips
Note that you can also use the keyword soME in place of Aw. This is a
simple straight swap of keywords that doesn't change the results in any
way. The main reason for using one keyword instead of the other is due
mainly to personal preference.
Unlike other predicates, the quantified predicates don't have a [NOT] option.
If you want to make a negative version, you simply change the comparison
operator.
Imagine that customers who order more than 30 items, in one order, gain
special discounts. We'll use an example of the ALL predicate to discover
which orders have been placed for more than 30 items. We break the query
down into two stages. We need to know the total of items for each order.
This we do in the subquery To prevent the subquery from merely totaling
the whole quantities ordered, we need to form a correlated subquery that
relates it to the orders table, so that each order has a separate SUM(QTY)
total. The second stage is to compare each of the totals generated to see if
they are larger than 30. The resulting query would look and function like
this:
242
Wa~c.
SO[ Iniractiv
I hL
File Edit Command Window Help
Wm Dat F
ordered custid
6196 18
6214 21
6216 22
Special Cases
Like the ANY predicate, there are also four possible scenarios for the ALL
predicate. We have just covered the first of them, where the predicate
returns all the records matching the specified criteria. If the subquery is all
NULLS, then the predicate returns uNmo1w. If the subquery has non-NULL
values, then the predicate becomes equivalent to the following:
If the subquery is empty, then the ALL predicate returns TRUE. This seems a
little weird if you have not studied formal logic, where this rule is called
'existential import.' The idea is that if a set has no members, then anything
you say about it is TRUE because you can't find a counter-example.
243
Chapter 9 - Using Subqueries
The subquery will be executed once for each row in the Orders table. In
the previous example of a subquery, the subquery was evaluated once and
the results were made available to the outer query. With a correlated
subquery, the outer query is processed and some information for each row
is 'passed' to the correlated subquery. The important thing to notice here is
that the WHERE clause of the subquery refers to [Link]. There is no Ol
table in the subquery's FROM clause. The value of [Link] comes from
the current value in the orders table.
If the subquery were to come back empty (nobody bought that many
thingabobs!), then the subquery would become a NULL, and the WHERE clause
result would be uNmowN.
Let's go one step further, and keep nesting. Did anyone order more than
two units of the first item in alphabetical order of our catalog?
244
-UI
245
Chapter 9 - Using Subqueries
Since this predicate is based on rows, and not column values, you can use
the SELECT * clause in the subquery You must, however, note the extra
line, which you might not intuitively add. The two tables have to be
correlated by a join clause, so that Salespersons. empid from the outer
query must equal Order. empid. The empid value is effectively passed as a
parameter from the inner query to the outer query Consider what might
happen if you ignored this. The SELECT subquery would find the first
occurrence of that particular salesdate and would return a TRUE to the
EXISTS predicate. This would mean that every employee name would be
selected from the table Salespersons as the two tables would be
independent, and the two queries would function independently
Conversely, you could simply reverse the query with the addition of NOT to
find any Salespersons who didn't make a sale on October 15th. This, as you
might expect, gives the list of all remaining salespeople not identified by the
first query:
246
Predica[ e
name
6,ndrew Allen
Rurbank Burkftt
-aulkner Forest
31oria Garcia
-larvey Harrison
<evin Kody
It's possible to make complex queries of the database using the EXISTS
predicate. Imagine you were checking sales patterns, and you wanted to
know on which dates orders were made for doodads. You would have to
query three separate tables, so to make the query work you would need to
create a join spanning two tables. This assumes that you did not know the
partid for a doodad.
First, you must create the initial query, which will simply display the
information you require. This is the sales date, and the orderid to make
the output maybe a little more meaningful than simply a list of dates.
The subquery is a little more problematic. Consider that a doodad can only
be located in the description column of the Inventory. However, there are
no common columns between Orders table (which contains the sales date)
and the Inventory table, but, the OrderItems table has fields common to
both Inventory and Orders. If you created a join from the Orders table to
247
Chapter 9 - Using Subqueries
Orderitems table on the common orderid column, and created a join from
OrderItems to the Inventory via the p)artid column, then the tables
would be effectively linked. The rather unwieldy code is simply checking
the description column for 'doodad'. It returns the information to the
EXISTS predicate for each row of orders, rather than just giving one
answer from the subquery.
porlid
1009
1010
IiiSatstc
FurFIu~(If
PLAN>•Inventrs
PLAN> (Irnaqunry k Inuvnntn
In-nt-•(1nvmntnrc) -0-l
Inv-tn- -)n
248
- adao
Note that the GROUP BY partid line won't be strictly necessary on all
systems, as partid is already a primary key, and can only have one record
per key entry However, some systems require the HAVING clause to always
be used with GROUP BY.
One interesting use of this feature is in the SELECT clause of the containing
query, which is helpful when writing tricky joins. For example, this is a
query which would find the highest order number for each customer:
:nme MAX(orderid)
Stanwood Consul 6099
/alle:ito Indus 6109
tv'ellesley Inc 9129
_0hite
Cour (NULL)
\cton Computers (NULL)
Distib
_gorrst
9148
'\exandriaLiqu
3lumenteld Educ 9197
6170
;ardrflInduntr 9175
DelToro Enterp (NULL)
)RT Mlrrne Lab 6194
249
Chapter 9 - Using Subqueries
If we look at the first eleven entries, we can see that NULL has been
returned for the customers who haven't yet placed an order, as there can be
no maximum order identification number.
The subquery can also refer to the same table as the DELETE statement, so
in such situations a correlation name becomes important. For example, to
delete all the orders that total less than $500, you could write the following:
The 'orderid' in the first line of the WHERE clause refers to the outermost
copy of the table in the DELETE FROM clause. This assures that the total we
just computed is matched to the proper row which is being considered for
deletion.
250
-H
table of contest winners which was built by a random drawing of order
numbers. We are going to pay off the winners adding a $500 gift certificate
to their orders (assume the $500 gift certificate is in the inventory as part
number 9999).
UPDATE Salespersons
SET aary -alary +S5
WHER EXISTS (SELECT saalesate
FROM orders
WHERE [Link].u gpid
AND saloodat.-,'1995-1O-15 ) j
251
Chapter 9 - Using Subqueries
Summary
There are several advantages of using subqueries, perhaps the key one being
that they breakdown larger queries into logical steps. This means that you
should be able to work out what each stage does separately, rather than
having to write one large and incredibly complex query. They are intended
to closely parallel the structure of the English language and there will be
times when you have no choice but to use them.
We have dealt with several forms of subquery. The simple scalar subquery
is the most common way of breaking down a query, and it returns a single
value as a result. There are several predicates, ALL, AY and EXISTS, which
can all be used to tailor the results to your needs. The correlated subquery
allows you to take short cuts with subqueries that would otherwise have to
be repeated many times. Subqueries can also be used with the INSERT INTO,
UPDATE and DELETE FROm statements which allows you to selectively alter
the data contained within the database. The next thing we'll look at is how
to selectively view only the parts of the database that you need to see.
Exercises
SQL doesn't have its own input and output, so you will probably use a
front end tool that will paint an order form on a terminal screen. This
form will take the fields on the screen, put the values into SQL
statements, and execute those statements behind the scenes.
What single query would you need to get all the information in a
typical order form on the screen?
(Hint: you need the customer data, the order data and the items in
that order.)
252
Sum
2 Find all the orders which have both a 'doodad', and 'doohickey' on
them. Don't look up their part numbers, and use the constants in the
query.
3 Find all the orders which have both a 'doodad', and 'doohickey', and
nothing else.
253
Chapter
Views
A view is a query which can be used in the same way as a table. However,
a view doesn't actually exist in the database. It's used for security reasons,
to prevent the user from seeing data for which he or she has no
authorization. It can also be used to take data from many different tables
and present it as one table, or to give the viewer his or her own personal
look at the database. It does this by showing only specific records or fields
that have been selected by the user. In this way it's the same as a SELECT
query. Views differ from SELECT by the fact that they can also act like
tables: parts can be updated and deleted. Unlike tables though, you can't
see the whole of the information that is contained. The view is restricted by
whatever conditions that are applied by the DBA.
Introducing VIEWs
When you use a CREATE TABLE statement, you physically create a structure
within the database by which to see selected parts of the data. These
structures are known as base tables. You can, however, use another
method to look at the data contained within these tables. This method is
known as viEw, whereby you give yourself the ability to see different
sections of different tables at the same time. A vizw is sometimes called a
virtual table, in order to distinguish it from a base table. A vIEW is a query
which is given a name so that it can be used like a table. When the SQL
engine sees the viEw name, it goes to the description of the viEw, executes
the query, and returns with the result table. To draw an analogy between
SQL and procedural languages, a viEw is the closest thing that SQL has to
an in-line macro, subroutine or procedure.
To give you a better idea of how a view operates within the database, we'll
compare it to other database schema objects and their properties:
Those of you who are cynical might also add a line to this chart which
defines a "back-up copy" as having neither existence nor accessibility.
Handling VIEWs
Upon finding a VIEW within an SQL statement, a database system can do
one of two things:
256
Ha
However, the standard says that a vI!W always has to act as if it were
materialized. By materializing we mean that the SQL query which creates the
vzIW must be executed, and the results stored in a temporary table. When
you create a VIEW, it's very like assigning a name to a particular SELECT
query, in the way we assigned different names to tables we created in
Chapter 2. Once the VIEW has been created, it can be treated in the same
way as a table, in that you can make SELECT queries of it, you can INSERT
INTO it, you can UPDATE it, and you can even perform joins upon it.
The name of the VIEW must be unique within the database schema, like a
table name. The WHERE clause in the <query expression> can't reference the
VIEW itself, but otherwise isn't restricted by complexity. The view definition
can't reference itself since it doesn't yet exist. The VIEW definition can
reference other viEWs, but these viEws must eventually reference underlying
base tables. All of these restrictions make sense; if there were no permanent
base tables involved, what would you actually be viewing?
Let's compare how the two different methods of viEws work, taking first an
example of a materialized VIEW and then compare how an in-line VIEW
expansion would deal with the same query.
Materialized VIEWs
Let's define a VIEW of the items costing over $10.00 in Inventory. We'll
give it a meaningful name of OverTen so as not to confuse:
257
Chapter 10 - Views
This creates a VIEW which is simply all of the columns in the Inventory
table and all of the records that have a price value greater than 10. To find
the expensive items whose quantity in stock is greater than 20, we could
write the query as follows:
The materialization method would construct the overTen table, then execute
the query.
258
Inln
In-line VIEWs
Compare the in-line method which would, in effect, convert the query to
the following:
In this case, this expression would be able to use all the indexes and other
features on the Inventory table and should give a fast answer. This makes
in-line expansion look pretty good, but there are always trade-offs. Consider
a viEw which gives us the average quantity of each item ordered:
We want to figure out who the above average customers for each product
are. This monster query has to get data from three tables:
259
Chapter 10 - Views
If the final subquery expression is expanded into in-line code, the subquery
will be re-calculated for each row in the containing query, and it will run
like molasses in winter. If the vzw is materialized, however, then the
HAvING clause becomes a simple correlated query on a small table that will
probably buffer into main storage. The materialized version should run
several hundred to several thousand times faster than the in-line expansion
in most current SQL implementations.
260
I paal
Updatable and Read-Only VIEWs
You can use VIEWS to INSERT, UPDATE, and DELETE into the base table from
which it is built. This only applies under certain conditions in SQL,
however.
Let's look at an example of how we could pass the changes on from a view
to the base table. If we wanted to add a new product to our view of
Inventory items worth more than $10 which also had more than 20 items
in stock, then we could write the following query:
Although the values are inserted into the view, all of the values are passed
onto the base table, Inventory. If you were now to query the Inventory
table in this way:
261
Chapter 10 - Views
you would see that the entry for dibdob exists in the Inventory table, as
well as the view Overten. However, as a result, updatable vzzws are
defined only for queries on one table, with no GROUP BY clause, no HAVING
clause, no aggregate functions, no calculated columns, and no SELECT
DISTINCT clause. Also, any columns excluded from the viEW must be
NULLable or have a DEFAULT in the underlying base table, so that a whole
row can be constructed for insertion. By implication, the view must also
contain a key of the table.
This is very restrictive and there are many other conditions under which a
VIEW could be updatable. The SQL standard simply decided to play safe.
262
- s
The number of column names in the <view column list> must be the same
as the query expression. The same column name can't be specified more
than once in the <view column list>. This is because there would be no
way of differentiating between the two columns. This is the same as for the
CREATE TABLE statement.
CREATE VIEW VI
AS SELECT *
FROM F00
WHERE a
UPDATE V1
SET a = 9;
The UPDATE will take place without any trouble, but the rows which were
previously seen in the VIEW now disappear when we reuse vi:
263
Chapter 10 - Views
They no longer meet the WHERE clause condition! Likewise, an INSERT INTO
statement with VALUES (a = 9) would insert just fine, but its rows would
never been seen when you reuse this VIEW.
viEws created this way will always have all the rows that meet the search
condition, and that can be very handy. For example, you can set up a VIEW
of records with a status code of "to be done," work on them, change a
status code to "finished," and they will disappear from your view. The
important point is that the WHERE clause condition was checked only at the
time the vImE was invoked.
The WITH CHECK OPTION makes the system check the WHERE clause condition
upon each INSERT or UPDATE, verifying the VIEW over again. Thus, the
previous UPDATE statement would get an error message, and you couldn't
change certain columns in certain ways.
For example, consider a VIEW of salaries under $250, and another VIEW of
employees getting between $100 and $250 per week:
If neither VIEW has a WITH CHECK OPTION, then the effect of updating
Mediumpay by increasing every salary by $1000 will be passed without any
check to Lowpay. Lowpay will pass the changes to the underlying
salespersons table. The next time Mediumpay is used, Lowpay will be
rebuilt in its own right and Mediumpay rebuilt from Lowpay.
However, if the CREATE VIEW statement for Nediupay had a WITH CHECK
OPTION on it, the UPDATE would fail. Medium pay has no problem with
such a large salary, but it would cause a row in Lowpay to disappear, so
Mediumpay will reject it.
264
I rp
Dropping Views
vizws, like tables, can be dropped from the schema. The SQL-92 syntax for
the statement is as follows:
The <drop behavior> clause did not exist in SQL-89. (It doesn't exist in
Watcom SQL either.) The usual way of storing viEws is in a schema level
table is with the view name, the text of the view, and any other information
that the SQL engine might need for reference purposes. When you dropped
a VIEW, the engine usually removed the appropriate row from the schema
tables. You found out about dependencies when you tried to use viEWs built
on viEws which no longer existed. Likewise, dropping a base table could
cause the same problem when the vIEw is accessed.
The new SQL-92 CASCADE option will find all other viEws which use the
dropped VIEW and also remove them. If RESTRICT is specified, then the
VIEW can't be dropped if there's anything which is dependent upon it. It's a
good idea to first use RESTRICT to see what is happening, then remove
schema objects in an orderly way.
If we decided that we didn't really want the to use the view of Inventory
to see products which cost more than $10, then we could use the following
code to get rid of it in Watcom SQL. Like tables, however, this version of
the statement must be used with caution as it affects any other related
vIEws. In this example, we know there are no other related viEws that we
have created:
DRPVEv~ew rte
265
Chapter 10 - Views
While the SQL-92 standard has a CREATE SCHEMA statement, each vendor
will have a little different version of it. The reason for this is that schema
creation depends upon how the SQL engine uses physical storage. Some
products have to allocate special storage space for the schema all at once,
while others use the operating system and allocate storage as needed. Read
your manual and look for a utility program specific to your database.
Summary
In this chapter, we have looked at how to create viEss via one of two
methods, the first being with materialization, the second being the on-line
method. We discussed what physical effect viEWs had on the database. We
also looked at how you can update vIEWs and physically update the base
tables of the database at the same time. Then we worked on how you can
drop v•Iws and noted the similarity with dropping tables. Finally, we
looked at a few do's and don'ts with views, and how they are used to
provide different users with different vIEWs of the database.
266
Exrie
Exercises
In the chapter on subqueries, we talked about building a query which
you would need to get all the information in a typical order form on
the screen. Put that query into a vIEw, called OrderForm.
2 Use the OrderForM view to find all the orders which have both a
doodad, and doohickey on them.
3 Use the OrderForm view to find all the orders which have both a
doodad, and doohickey - and nothing else.
5 Take the view you made in question 4 and use it to make a view with
the customer name, salesman's name, the order number and the total
value of that order.
267
Chapter
Embedded SQL
We begin this chapter with a look back at all of the standard features of
SQL that we've learned so far. However, one drawback should be
apparent, and that is that SQL doesn't work by itself. You can't write
stand-alone applications with pure SQL. What you must do instead is to
embed SQL into a host language program and get it to pass data
between the host language program and the database. You must send
this mixed language program to a pre-compiler which looks at the
program and can extract the SQL and convert it into the host language.
The way the results of the SQL are passed back and forth is in
structures known as CURSORS which act like sequential files. You can use
these structures to query and update the underlying base table.
So far, you've been relying on whatever interactive query tool was supplied
by your DBMS vendor to issue your queries. However, if you don't want
the user to type SQL in directly, you'll need to write some code in a high-
level programming language.
Embedded SQL
SQL by itself is pretty useless, in fact the joke is that the initials stands for
'Scarcely Qualifies as a Language' because it doesn't work by itself. In fact,
there's no way for SQL to input or output data to the operating system and
the user. Instead, SQL sits quite happily between the database and a host
language program passing data back and forth. The host program takes
responsibility for communicating with the outside world.
270
Ih m
The host language can be an ANSI or ISO procedural language like COBOL,
FORTRAN, PL/1, Pascal, C, C++ and BASIC. In fact, the ANSI and ISO
standards committees for these languages helped to define the actual
interfaces for SQL.
Today, there are also front end tools on PCs which are designed to use
SQL. Powerbuilder from Powersoft, SQL Windows from Gupta and Delphi
from Borland International are fine examples. There are also 4GL products,
such as Informix and Progress, which have switched from their original
proprietary database languages to SQL.
t The first problem is that SQL has a very rich collection of datatypes
which might not match those available in the host language program.
If the NULL, for example, doesn't exist in the host language, it's a
real problem.
ý1. The second problem is that SQL uses a set model of data and not a
file model. This is referred to as 'impedance mismatch' in the
literature. A file is read, updated, deleted from and inserted into one
record at a time, while in contrast, the same operations in SQL are
performed on a set and happen all at once.
271
Chapter 11 - Embedded SQL
The host language database calls are low-level routines which operate
directly on the SQL engine. These routines are part of what is called an API
(Application Program Interface) library and are very specific to each SQL
product and host language.
There will be a work area in the host program which passes data and
messages between the host program and the SQL database. Some SQL
statements, such as a DELETE FROM, will only send messages into the work
area, whilst others will send data as well. The data which is sent back and
forth is automatically converted into the appropriate host and SQL
datatypes. In practice, there's really not that much data conversion done
since the SQL implementation and the host language exist on the same
platform, they very often use the same hardware features for numerics and
character strings with few differences.
These work areas are the first thing we need to look at.
SQLDA
The SQLDA is another pre-defined structure called the 'SQL Descriptor Area',
which holds parameters for the connection between the database and the
host program. The SQL descriptor area is allocated and maintained by the
system with the following statements:
272
* IA a
* L A
This isn't something that the programmer usually worries about, so we'll
skip over it and tell you to read your manual.
SQLCA
The SQLCA is a pre-defined data structure called the 'SQL Communications
Area', which passes messages from the database to the host. The most
important parts of this structure for the programmer are the SQLSTATE and
SQLcODE. The SQLCODE is an integer which returns a code for the results of
the last action against the database. The SQLSTATE is new in SQL-92 and
will replace the old SQLCODE.
SQLSTATE is a five character field which also returns a code for the results
of the last action against the database. The first two characters are the
general class of the message and the last three give the details. Vendors are
allowed to add to the basic encoding scheme to provide even more details.
Declarations
The area where data passes one row at a time between the host and the
SQL database is defined with a collection of host language variable
declarations blocked off with the pre-compiler phases EXEC SQL BEGIN
DECLARE SECTION and EXEC SQL END DECLARE SECTION.
273
Chapter 11 - Embedded SQL
SQLERROR is True whenever any error code is raised by the last SQL
statement executed. The NOT FOUND condition is raised when no rows were
returned by the last SQL statement executed.
If you need more control over the embedded SQL, then you can use the
SQLSTATE just as you would any other host language variable and take any
action you wish. If anything went wrong during the transfer, errors are
reported back in a character field called the SQLSTATE. This will replace the
older numeric field, SQLCODE, specified in the SQL-86 standard. Right now,
you'll often see both of these in products so as to maintain compatibility
with older programs.
Vendors can add to the standard to provide messages that are particular to
their product. The more important classes are the following:
I
Code Description
274
IS g
The old SQLCODE for successful completion was 0, for a non-fatal warning it
was 100, and all errors were negative numbers defined by the vendors. This
is why the SQLSTATE uses strings that can be easily converted into those
numbers for success and warning codes. Beginners to SQL programming
often try to get a '00000' SQLSTATE and they're happy. More experienced
SQL programmers learn to use the information that comes back in the
I0lxxx' and '22xxx' SQLSTATE codes in their programs.
Using the principle of working in sets, SQL can return more than one
SQLSTATE by putting them into a host language diagnostics area. For
example, on a really bad day, a programmer might try to divide by zero in
a table for which he doesn't have authorization, and then compound the
error by making a syntax error during the process. How many messages
this area can hold is up to the vendor (as is the point at which the engine
stops looking for errors).
CURSORs
Structures, called CURSORS, will convert the result sets from SQL queries into
records that can be passed one at a time through the work area. CURSORs
behave much like simple sequential files in the host language. You open and
close cursors like a file. You can read rows as you would records with the
FETCH statement. And if it's an updatable cursor, you can write via the
cursor to the underlying base table. SQL-92 also has provisions for
navigating rows the way that you would with a random access file. They
can act like pointers insofar as they're moved through a set of rows, where
the program gets to access each row individually
275
Chapter 11 - Embedded SQL
<sort key>
<column name> I <unsigned integer>
<ordering specification> =
ASC I DESC
This is the full SQL-92 specification which many vendors haven't yet
implemented, although Microsoft SQL Server 6.0 is one major exception. The
cursor works by executing the query inside it to get the rows of the result
set. The ORDER BY clause will sort those rows on the given columns in
ascending (ASC) or descending (DESC) order. If no ORDER BY clause is
given, then you can't depend on any order in the result set.
If the query was on only one table and included a set of key columns for
that table, then the cursor is assumed to have an implicit FOR UPDATE
clause. These are the same rules SQL uses for updatable views. We will talk
about special versions of the UPDATE and DELETE statements for updatable
cursors a little later, but for now we'll just say that it means that you can
use the cursor to update the underlying base table because every row in the
cursor is associated with exactly one corresponding row in the underlying
base table.
If the cursor isn't updatable, then there's an implicit READ ONLY clause on
the cursor. When a cursor is READ ONLY, then you can also have
INSENSITIVE, SCROLL, or ORDER BY clauses.
The INSENSITIVE option specifies that the cursor is either READ ONLY or that
we're treating it that way. The operations of UPDATE and DELETE aren't
allowed for READ ONLY cursors.
276
IU g
The SCROLL option allows the cursor to move from row to row under
program control. Normally, a cursor can only be read one row at a time in
a forward direction, like a deck of punch cards (lucky readers under the
age of 40 who have never seen a deck of punch cards are asked to consult
their computer history books).
While we've been telling you that the cursor is like a file, this isn't strictly
accurate as not all file systems behave in the same way.
Some SQL products materialize the results of the query when they execute
the DECLARE CURSOR statement and some only materialize the results when
they execute the first fetch statement - not when they open the cursor. This
means that some errors can't be detected when the OPEN statement is
executed.
When the cursor is opened, it's positioned before the first row of the result
set. At this point, the cursor isn't positioned on a row, so you'll get an
invalid cursor state message.
277
Chapter 11 - Embedded SQL
card reader. If the cursor was declared to be scrollable, then you can
position the cursor on the NEXT, PRIOR, FIRST or LAST row. The ABSOLUTE
option positions the cursor on a particular row, identified by its position
number. The RELATIVE option lets you move forward or backward a
number of positions from your current row. A positive integer means read
forward, while a negative integer means read backwards.
and this assumes that the cursor was in the open state when you closed it.
This statement also destroys the <cursor specification>, so you can reuse
the cursor name if you wish.
278
I gE
If the cardinality of the query result set is greater than one, then you'll get
a cardinality violation error. It's implementation dependent whether or not
data values are assigned to the targets identified by the <select target
list>.
If the query is empty, then no data values are assigned to any targets
identified by the <select target list>, and a completion condition of no
data is raised. Otherwise values in the row of the query are assigned to
their corresponding targets.
The row in the base table from which the current row of the cursor is
derived is marked for deletion. If another operation also marks the same
row for deletion, then you'll receive a warning that you have a possible
cursor operation conflict. All rows that are marked for deletion are
effectively deleted at the end of the statement and prior to any integrity
constraint checking.
If the positioned DELETE statement deleted the last row of the cursor, then
the position of the cursor is situated after the last row; otherwise, the
position of the cursor is prior to the next row in the result set.
279
Chapter 11 - Embedded SQL
280
Em
An Example of C-Embedded SQL
We've now considered how SQL can return error codes and how it can use
CURSORS to handle the transfer of results sets, in a manner similar to the
method that the host language would use sequential files. However, we
haven't considered any examples of how SQL is called up within a host
language program.
Rather than give a separate example after each part of the syntax, we've
included them all in one C program. It doesn't matter if you don't
understand C or if you don't understand the program given here, as it's not
meant to be compiled. This is simply to demonstrate how SQL is called
throughout an example program in a host langauge. Throughout the
program we've highlighted the EXEC SQL calls in gray. These are the bits
that the pre-compiler would need to translate into equivalent host language
calls.
It's possible that you won't be able to run the following program since pre-
compilers tend to deviate from the standard and offer distinctly alternative
versions of the statements we've outlined. If you did wish to compile it you
would need to run the pre-compiler on the program first. You would also
need to gain a pre-compilation version of the program which would then be
able to be compiled.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FAILURE 1
#define SUCCESS 0
281
Chapter 11 - Embedded SQL
/* Function prototypes */
long getLong(char *prompt);
int getStr(char *prompt, char *dest, int lim);
void printError(void);
int main))
/* Order data */
long order id;
long order-empl;
long order cust;
char order date[ll];
/* OrderItems data */
long item orderid;
long item detail;
long itempartid;
long item qty;
char action[2];
char response;
printError ();
return FAILURE;
282
Imbedn
/*
"* On SQL Error or Not Found, set error result
"* and break out of loop
*/
EXEC SQL~ WHENEVER SOLERROR.
result = FAILURE;
printError();
break;
1;
printError);
break;
1;
printf(
"Order Customer Sold by Date\n");
printf("%51d %-30s %-15s %s\n",
orderid, cust name, empl name, order date);
283
Chapter :1. - Embedded SQL
response = toupper(action[O]);
while (response == 'Y')
goto finish;
I;
/* prime the print loop */
EXEC SOL FETCH dealpro
INTO :iteM~orderi, sitom4*r*$i,
finish:
if (response == 'Q')
break;
return SUCCESS;
/*
284
-IEbdin
L
Example
* getLong(char *prompt)
long r = -1L;
char s[171;
if (getStr(prompt, s, 16))
r = atol(s);
return r;
if (*prompt)
printf("Please enter %s: ", prompt);
if (gets(workarea))
dest[l] = '\O';
return strlen(dest);
* printError()
* function to
* print a description of any SQL errors that bite us.
void printErroro)
f
285
Chapter 11 - Embedded SQL
Summary
We've looked at the method SQL uses for passing data between the host
language and the database. We looked briefly at the actual concept of
embedded SQL, before moving on to how SQL uses the SQLCA structure to
hold information about the completion status of embedded SQL statements.
From the codes returned the user can deduce whether a statement was
successful or not.
We also looked at CURSORS which are used to pass information back and
forth between the database and the host language. We then looked at the
different statements for controlling such CURSORS, including the FETCH
statement which enables information to be passed, one row at a time.
Finally we integrated all of the concepts into a C program with embedded
SQL. Even if you aren't familiar with C, this should have given you an idea
of how embedded SQL works in practise.
286
Summary
287
Chapter
Chapte
Database Schemas
A set of table declarations (and other objects) make up a database schema
(or just a schema for short). By analogy, this is a house without any
furniture. A database is a schema with data in it; the house with furniture
added.
A database engine can work with several different databases. The user has
to connect the engine to the correct database before he or she can actually
do anything. This usually involves a password or some other log-on
procedure.
290
-I
Physical Database
Files
The engine is the traffic cop who will handle problems with multiple users
trying to get to the same data at the same time. It checks and enforces
privileges and rule violations, using the schema as its law book.
291
Chapter 12 - Optimization and Performance Tips
The Optimizer
The optimizer is the bit that figures out what sequence of SELECTS,
PROJECTS, JOINS and set operations will answer the request most efficiently.
The actual techniques used by optimizers vary from DBMS to DBMS.
The optimizer includes an SQL compiler. Like the compilers you use for any
procedural language, this compiler produces an executable program. But it
differs in that parsing an SQL statement produces an execution plan instead
of machine code. This execution plan is a description of how the query will
be run against the database. The execution plan is built from the SQL
statement using the optimizer which looks at table statistics, available
indexes, and other things which may help it to find the best approach to
the problem in hand. If indexes are added or dropped, if tables change size,
or indeed, if any number of other things happen, then the same SQL
statement can produce a completely different execution plan. This is a big
difference from a procedural language which uses files. The procedural
language program doesn't change upon being re-compiled just because its
files have changed.
Queries
A request for data from a SQL database is usually called a query rather
than a search. The reason why the word search is avoided is that it sounds
too procedural, as though you were moving about in the woods looking for
things. Querying is more appropriate as it sounds non-procedural: ask a
question, and your loyal servant brings back the answer.
The SQL engine uses special tables which describe the schema in SQL.
These tables are queried by the engine to check privileges, to get table and
column names, to get information for the optimizer, the size of columns and
any other required data. They're usually given distinctive names which
begin with a prefix, such as 'sys-' or 'SQL-' and they're always kept away
from users via the DCL (Data Control Language). You don't want anything
but the SQL engine to change these tables!
292
-A .
implemented it, nor are they very likely to do so. Each vendor will have
different algorithms for doing things, which will require different data. You
can usually find a description of the information-schema tables for your
product in the database administrator documentation. On the bright side,
you will probably also find a set of useful queries already written to extract
helpful data from these tables.
You want to work with the smallest number of rows required to answer a
query, or to perform an insertion, update or deletion because physical access
time is the slowest part of any computer system. The more work you can
do in the central processor, the faster the system will run.
The two most common access methods used in SQL implementations are
indexing and hashing. A SQL program never references an index or a hash
table except to create or to drop it. The optimizer in the SQL engine
decides how to use them and creates what is called an execution plan for
the statement. Should an access structure be changed or dropped, then the
execution plan has to be re-compiled, but the original statement doesn't.
When a row is inserted, deleted or updated, the SQL engine will also
handle the changes in the data structures that hold these access methods.
Older file systems didn't have this feature and the programmers had to
write code to keep the indexes and their files synchronized.
Indexing a Table
Think of an old fashioned magnetic tape drive, moving a tape from reel to
reel. This is an example of sequential file access; to get to a particular
record in the file, the system has to physically read all of the records in
front of it and check them until it finds the record that you requested.
293
Chapter 12 - Optimization and Performance Tips
When we finally got random access storage (that means disk drives), we
could make a physical jump directly by moving a read-write head to the
desired position and waiting for the disk to rotate underneath it. Similarly,
the SQL engine can find the desired record without reading through all of
the preceding records.
An index is a way to take search values and locate the records that go with
those values. If you have an unabridged dictionary, then there is a good
chance it has a thumb index cut into the sides of the pages. Rather than
looking up a word by turning a page at a time, you can stick your thumb
in a notch and flip to the start of the right section, bypassing all the
unwanted sections.
Indexes are called 'primary' indexes if they are used to guarantee that a
value is unique within a column as well as to speed up searching, and
'secondary' if they're only used to speed up searching on non-unique
column values.
Index Defining
Defining indexes is usually a job reserved for the DBA. An index that
benefits one job can hurt others, so the decision has to be made on a global
level.
An Example of Indexing
How the indexes are built varies from one SQL implementation to another,
but to give you an idea of how they work, let's assume that we're going to
put an index on the empid column in the Salespersons table.
294
Id i
While vendors use different indexing methods, the X/Open consortium has
specified syntax for CREATE INDEX and DROP INDEX statements, and their
specification is currently the most common format in use. It looks roughly
like this:
and
Almost every product has a uNIQuE option, since that's how SQL products
were built before the standard insured primary keys. In SQL-92, a
declaration of a primary key constraint on a table will often be implemented
with an index which has a system generated name.
Another common extension is a sort order (ASC, DESC) option which will
impose an ordering system on the index. This option lets the database
engine know that when it uses the index, it will fetch rows in the specified
order.
Clustered Indexes
Clustered indexes keep the table itself in sorted order. This is expensive to
update and maintain, but knowing the ordering of the rows lets the
database engine improve its strategy for planning sequential searches and
joins.
295
Chapter 12 - Optimization and Performance Tips
In a real index the system would probably use multi-way trees instead, but
the principles still hold. The binary tree would look like this:
If we wanted to find the row for employee number 107, we would start at
the top of the tree, then go to the right node, then the left node, and then
finally to left again to arrive at a leaf node which would also have the
physical address of the row (107, 'Gloria Garcia', 3, $3,000.00) in
the Salespersons table.
If the SQL engine looks at all the possible paths, then the average number
of nodes visited to find a particular record is 4.22 reads - remember that
the SQL engine needs to read the table once after doing any index reads. If
it were to read the rows one at a time from the Salespersons table, it
would take an average of 4.5 reads to find a single row.
296
Ha
If this were a real database indexing scheme, the tables would be much
bigger and the savings in read time would be exaggerated, but the you can
see the point, even in this toy example.
Indexing Techniques
What if we wanted to compute the total salary of all the employees? A
sequential file read (or table scan to use SQL terminology) of all the rows
will use a total of 9 reads. If we used the index to read all the rows, then
it would take 38 reads - 29 in the index plus 9 reads in the salespersons
table. One of the things the query optimizer needs to do is to decide when
to use an index and when not to.
On the other hand, if the SQL engine wants to find the MAX (empid) or
MIN (empid) functions, all the SQL engine has to do is look to the right or
left side of the tree. Without the index, the SQL engine would have to read
all 9 rows to be sure that the right value was found.
Indexes also give a boost to the EXISTS predicate. For example, if the SQL
engine wants to find if a row with an (enpid > 105) exists in the table,
then all the SQL engine has to do is look to the right side of the tree, see a
node with a value of 108, and never read the table at all. Without the
index, the SQL engine would have to read rows in the table until it found
one that met the criteria.
In the case of the NOT EXISTS predicate and no index, the engine would
have to read all the rows to be sure. With an index, the tree is searched
until it leads to a nil node (a node that has no branches) or a probe that
goes outside the bounds of the tree.
Hashing
Hashing takes the search value and passes it to a formula called the
hashing function. The result of the hashing function is used as the subscript
into a matrix of the physical locations of the rows.
297
Chapter 12 - Optimization and Performance Tips
To find the row with (empid = 107) apply the hashing function to the
search value (107 MOD 3 = 2), then read the corresponding bucket to locate
the desired row. A sequential search of a bucket in this case would average
2.5 reads - remember you always have one read into the Salespersons
table.
This toy example is a little misleading in that you'll notice that hashing was
faster on average than indexing and you might think that this is the general
case. There are trade-offs however. Hashing is usually very fast for unique
values, because you'll have a lot of very small buckets if you have a good
hashing function. But if there are a lot of duplicate values, then the buckets
can become very large, be slower to search than a sequential file and
contain many unwanted rows. A clustered index would do a better job of
finding all the duplicate values, since all the duplicates would be located
together physically on the disk.
The hashing function will hardly ever use all the values in its address
matrix, so you're trading increased storage for access speed. Furthermore,
when rows are added or dropped from the table, the table should be re-
hashed to minimize hash clashing. This takes more time than re-organizing
an index.
The tips that we have offered so far are based generally on how you can
improve the performance of the SQL engine, however another way to
improve the time taken in queries is to improve the way that you actually
write queries.
298
mS
Hints on How to Write Queries
A lot of the trouble with a new programming language is not so much
what you don't know as what you know that's not right. People tend to try
to make the new programming language work like their old programming
language. This isn't surprising since it also happens with natural languages
too. When you first try to speak a foreign language, you use the new
words, but with the word order and grammar of your first spoken
language.
SQL has some general principles that will help you to write queries that
will at least run, and as you get more experienced with the language, you
can improve what you've done.
Let's use a moderately difficult problem and work it out in detail. The
problem is basically to report the sum of the (n) largest orders placed by
our customers.
1What if there are fewer than (n) orders - does he want the sum of the
existing orders that we have? Probably, so let's make that a part of the
problem specification.
2 When we're talking about the customers, do we mean the '(n) largest
orders made by each of our customers individually' or do we mean the
'(n) largest orders made by all of our customers as a single group' in
this problem? Either way is quite reasonable, but let's assume that we
mean each of our customers individually - one customer, one total.
299
Chapter 12 - Optimization and Performance Tips
3 However, if there were more than (n) orders, then one of our bosses
said he wanted the sum the (n) largest amounts. That is, a customer
who bought items for ($40, $40, $30, $20, $20, $10, $10, $10) would be
reduced to the set of orders for ($40, $30, $20, $10) or a total of $100?
4 Another boss said that he wanted the sum the (n) largest orders. That
means a customer who bought items for ($40, $30, $20, $20, $10, $10,
$10) would be reduced to the set of orders for ($40, $40, $30, $20) or
a total of $130?
Once we've determined exactly what is wanted we can begin to write the
query. However we won't start in SQL, we'll start with an English-like
pseudo code, that hopefully is meaningful to you despite not being a
legitimate programming language. We'll specify in very simple terms and
expand each clause in turn:
Imagine that it is the best of all possible worlds. Pretend that the SQL
we're using has a library of functions, f (), which answer any of the tricky
stuff we want to compute. The pseudo code query is just going to call
300
!7
those functions in the SELECT list with expressions of the form <<function
call>> AS <<name>>. They're just a place holder for now. I can figure out
that I have to do something with the customer, whom I identify by their
custid, the price of the product and their quantity to compute the total of
each order by customer.
The only one we don't need in this problem is the Employees table, but in
a larger database the number of tables excluded would typically be much
higher than those included. The pseudo code query looks like this:
We could also put correlation names which end in zero on the tables at this
point, more to save writing longer names than anything else. If some of the
known result columns come from only one table, then attach the correlation
names to those columns.
301
Chapter 12 - Optimization and Performance Tips
The OrderItems table has the qty column and the Inventory table has the
price. We know that (qty * price) is something that we have to calculate
to get the totals we're after, so let's figure out how to join those two tables.
In the real world, most joins are usually an equi join performed on columns
with a common name representing the same attribute. The common column
here is partid. Put the join and formula in the pseudo code query like this:
SELECT custid,
f(custid, orderid, ([Link] * [Link])) AS BigOnes
FROM Customers AS CO, Orders AS 00,
OrderItems AS 010, Inventory AS 10
WHERE [Link] = [Link]
AND/OR <<some conditions>>
GROUP BY <<some columns>>
HAVING <<some group conditions>>;
SELECT custid,
f(custid, orderid, ([Link] * [Link])) AS BigOnes
FROM Customers AS CO, Orders AS 00,
OrderItems AS 010, Inventory AS 10
WHERE [Link] = [Link]
AND [Link] - [Link]
AND/OR <<some conditions>>
GROUP BY <<some columns>>
HAVING <<some group conditions>>;
But look, we don't need the customers table since we already have the
custid in the Orders table; remove it and add correlation names to the
columns we have thus far:
302
I on
SELECT [Link],
f([Link], [Link], ([Link] * [Link])) AS
BigOnes
FROM Orders AS 00, OrderItems AS 010, Inventory AS 10
WHERE [Link] = [Link]
AND [Link] = [Link]
AND/OR <<some conditions>>
GROUP BY <<some columns>>
HAVING <<some group conditions>>;
SELECT [Link],
f([Link], [Link], SUM([Link] * [Link])) AS
BigOnes
FROM Orders AS 00, OrderItems AS 010, Inventory AS 10
WHERE [Link] = [Link]
AND [Link] = [Link]
GROUP BY [Link]
HAVING <<some group conditions>>;
303
Chapter 12 - Optimization and Performance Tips
We could put this in a vizw and then use the vuw in another query. That's
often a handy trick, especially in situations where the same vzzW might be
used by a lot of different queries. In fact, if you're using a SQL-89
implementation, you'll find that you almost have to use vzzWs for this sort
of problem. However in SQL-92, we can treat this query as a subquery
expression and use it directly.
304
I c o
Po ble,
- ghe
But when we look at the FROMclause, we can see that we don't actually
need the OrderItems or Inventorytables in it. And if we don't include the
OrderItems or Inventory tables in the FROM clause, we cannot use them in
the WHERE clause. The query now becomes:
Which query should we use? There are almost always several ways to write
a query that will achieve the same results. The answer is that this depends
on your SQL product. Some the queries won't work on some
implementations. One version might perform well on one implementation
and not so well on another.
But this query doesn't yet answer the original question about the top (n)
orders per customer. We could stop here and then use a procedural
program to sort and pull off the top (n) orders from each customer. But we
don't want to do that - we're going to put the query into a VIEW, so that
we can use it for other things too:
305
Chapter 12 - Optimization and Performance Tips
We also have another option - we could create a temporary table, (for those
using the Watcom SQL engine provided, this temporary table has already
been created):
The trade off for this is that the user will have to insert all the rows from
the query into the temporary table himself. The engine will check its
PRIMARY xiY constraint and any other constraint that might be added to the
table. However, once the table is loaded, the SQL engine can use any
indexes on that table for optimization.
Repeating our steps and pseudo code query, we can make an attempt at a
query again. To find the total of all the orders for each customer, we could
write the following query:
306
UIS,Rpeai.
self-join. Now our pseudo code query looks like this:
Some of the characteristics of the top (n) items in a list without duplicates
is that there are (n) of them. This implies that there is a highest value in
the subset. We can build the subsets using ordertotal and one copy of the
OrderSu=nary table to provide the highest element in each subset
constructed from the other copy of the ordersumary table. That's easier to
see than to say:
Since we're looking for subsets with (n) or fewer elements in them, this
sounds like a job for a HAVING clause. To check this we need a test value
for n, so we will use 3 from now on:
One way around this would be to get rid of the repeated values by making
the SUM() into a SUM(DISTINCT) function, like this:
307
Chapter 12 - Optimization and Performance Tips
But this query will handle the case where a customer has separate orders
for identical amounts by reducing them to one purchase. That is probably
not what the boss wanted. How do we tell one order from another in the
database? By it's orderid of course! We're missing an important part of the
answer. In SQL-92, we could write
connect the total and id for an order together, but in SQL-89, we have to
break it apart:
Notice that we have to put the [Link] in the GROUP BY list to avoid
the repeated total problem.
Now we're getting a set of 1 to (n) rows which represent the largest single
order, the largest two orders, and so forth. But the code is getting to be a
bit of a mess. We can just put it in another viuw which we can use for
other reports. This is also a chance to generalize the query by moving the
COUNT([Link]) into the SELECT list, like thus:
308
SQ
rik
Now the query can finally be written as:
Now if the boss wants to change (n) to another value, or just look at
customers who placed exactly (n) orders or some other query based on
running totals, we can appropriately change this little query
Bag of Tricks
Obviously, this query was solved in a way that shows off as many SQL
programming tricks as possible in one example. Let's make a list of those
tricks and give a rule of thumb for each one:
1 Look for ways to join the tables that have the attributes you need for
the query. Always consider more than one way and more than one
table.
4 Be careful with joins as they can be expensive. Make sure that the
WHERE clause limits the selection sets as much as possible.
5 You can use temporary tables like views, but you are trading the time
it takes to insert rows into them with extra storage space for indexing
and constraints. The rule of thumb would be to do this when you
need to create an index on columns that don't exist in the original
tables (function calls and expressions), or you need to add constraints
that don't exist in the original tables.
309
Chapter 12 - Optimization and Performance Tips
7 Test your query as you go along. Look for problems with empty
tables, duplicate rows and NULLs.
8 You can build vizws with other viws to hide complexity and to
"fake" nesting aggregate functions. Try to write vixws which are
understandable and useful in their own right so they can be reused in
other queries.
Summary
We've looked at the different aspects of SQL that affect the performance and
access times. The SQL engine takes a query and uses the optimizer to
decide which set of SQL commands will answer the query most efficiently
It then produces an execution plan of how the query will run against the
database. Next we considered two indexing methods which can be used by
the optimizer to improve database access. Both binary trees and hashing are
common methods used for storing data within many computer languages
and we looked at how SQL implements these features. Finally we outlined a
system for constructing complex queries from simple pseudo-code and we
followed an example, building it up step-by-step.
310
I Sumr
311
Where Do We Go From Here?
You may or may not have had opinions on or ideas about the SQL language before
you read this book, but we're sure that you've found this tour an enlightening,
informative and eye-opening experience. Not only have we tried to remove the
excess stuffy baggage that traditionally drags programming material down, but
we've also attempted to pack everything you need to know into a compact, cost-
effective reference guide.
Now that you've had a taste of our refreshing style, would you like to know more
about Wrox Press and our other publications? If you do, then why don't you ask
for our latest catalog or check out our Web page. And remember, when you're
down at your local bookstore, look out for our distinctive red binding - your
guarantee of Wrox value.
Are you interested in writing or reviewing any of our future books? We warmly
welcome any willing contributors that can help Wrox to publish even better books. If
you're interested then contact us right away - see the details at the back of this book.
If you have any general comments about our books you can contact us via the
reply card also at the back of this book, or get in touch with us by any of the
following means:
While there are often several declarations of a table in each chapter, each
stage showing a progression, we'll only give you the final version of that
table from that chapter. If the table has been defined in a previous chapter
and there haven't been any amendments, then that table is also excluded
from these notes.
Finally, the data for the example database isn't contained within this
appendix - it can be found within Appendix B. This is because the one set
of code can be run under all three vendor implementations, without any
alterations.
Appendix A - Conversion Notes for Different Vendor SQLs
Oracle
The code contained in the book is very similar to Oracle SQLPLUS, but
there are a few differences to be chronicled.
316
I O
2 Instead of the CURRENT DATE, Oracle utilizes the data type SYS DATE.
3 Oracle requires that each clause is completed with a comma and each
query or structure terminated with a semi-colon.
317
Appendix A - Conversion Notes for Different Vendor SQLs
318
Or
CONSTRAINlT right-order
FOREIGN KEY (orderid) REFERENCES Orders (orderid)
ON DELETE CASCADE,
CONSTRAINT rightpart
FOREIGNI KEY (partii) REFERENCES Inventory (partid)
ON DELETE CASCADE);
EXIT
SELECT *
FROM Foo,Bar
WHERE Foo.x-Sar.x(+);
319
Appendix A - Conversion Notes for Different Vendor SQLs
SELECT *
FROM Foo,Bar
WHERE Foo.x(+)=Bar.x;
FROM Prizewiinners AS 11
WHERE orderid = P1. ord~erid;
This would have to be performed by defining a view for the subquery and
joining it with the prizewinner's table.
320
IS.
Sybase
Sybase is very similar to Microsoft SQL Server. These are the notes and
code you should use if you wish to use the examples on the Microsoft SQL
Server.
Initialization
Before you can enter any CREATE TABLE statements in Sybase, you have to
create the database and make that database current (with the USE statement).
This rather unpleasant looking piece of code will do the necessary
groundwork for you:
use master
go
321
Appendix A - Conversion Notes for Different Vendor SQLs
end
go
322
I -
323
Appendix A - Conversion Notes for Different Vendor SQLs
3 SQL Server has Money and SmallMoney datatypes that would normally
be used for the salary column. In this example use Sinallmoney (max of
214,[Link]). CHECK, DEFAULT, PRIMARY KEY and DECIMAL aren't
available in Microsoft SQL Server V4.2 and below or in Sybase SQL
Server V4.9.x and below.
4 In Sybase, the order definition will fail if the Inventory table hasn't
already been created.
324
IS .
8 Sybase has functions that return parts of the date (the day or month for
instance) , together with the names of those parts (Monday or January
for example).
The redefintion of the Salespersons table with table constraints rather than
column constraints is:
325
Appendix A - Conversion Notes for Different Vendor SQLs
326
I -
4 Sybase requires a constraint name when you use the word CONSTRAINT.
2 Unlike the result given in this book, for this example query:
a b fred
327
Appendix A - Conversion Notes for Different Vendor SQLs
2 The following query in Sybase will give a different set of results to the
ones shown in the book:
Sybase doesn't include the ones with NULL in its result set.
328
IS.
end
go
create trigger Orders d_tr
on orders for delete as
begin
1* Save for audit fields
*1
declare OTS datetime, OUI smallint
select @TS = getdate()
select @UI - user id()
329
Appendix A - Conversion Notes for Different Vendor SQLs
end
go
Access
Before the advent of Access 2.0 there was no way to create and/or modify
table definitions. With Access 2.0 Microsoft has given us two methods of
creating and modifying tables; one method is the Data Access Object (DAO)
whilst the other is Data Definition Language (DDL). It's important to note
that the Access DDL isn't fully developed and you'll need to go elsewhere
within Access to complete the job.
DDL queries can be created in one of two ways, using the SQL view of the
QBE window or by defining and executing a QueryDef. Access SQL
supports four basic DDL statements:
CREATE TABLE
• ALTER TABLE
I• CREATE INDEX
DROP
and the CONSTRAINT clause for just the creation of indexes. Access SQL
doesn't support the NOT NULL or CHECK contstraints, but may at a later date.
Before you can enter any CREATE TABLE statements in Access, you have to
make the database available to you. The following code snippet will get you
started:
ConnectString = "ODBC;UID=Fred;PWD=RHS;"
The following Access-SQL statements can be executed using Access Basic via
DAO (Data Access Objects) or interactively using the Access QBE in SQL
mode and typing SQL statements in the QBE then executing them as
queries.
330
IAc
331
Appendix A - Conversion Notes for Different Vendor SQLs
3 Access uses two functions to change the case of a string. LCASE$ shifts
all letters to lower-case and UCASE$ turns all letters into upper-case
letters.
332
IAc
The CONSTRAINT clause in Access SQL doesn't support a way to create non-
unique indexes within CREATE TABLE or ALTER TABLE statements. However,
you can use the CREATE: INDEX statement to create unique indexes.
or "+" as follows:
333
Appendix A - Conversion Notes for Different Vendor SQLs
SELECT *
FROM Foo IMuiR joiN Bar oN roox - srx
334
Ace
SELECT *
FROM Foo
LEFT JOIN Bar On Foo.x = Bar.x;
SELECT *
From Foo
RIGHT JOIN Bar ON Foo.x = Bar.x;
335
Appendix A - Conversion Notes for Different Vendor SQLs
2 Access-SQL assumes the ALL predicate if you don't use the DISTINCT,
DISTINCTROW, or TOP predicates when defining a SELECT statement.
SELECT *
FROM Inventory
WHEREZ price > 1.0.00;
336
IAc
337
Appendix
Example Databases
Note that the code represented here should work on most SQL
implementations without any problems.
Appendix B - Example Databases
340
Example
Data
The Orders table needs to use values from the salespersons table and the
customers table. Likewise, the OrderItems table references the Orders and
the Inventory tables. The order for insertion into both tables is as follows:
341
Appendix B - Example Databases
342
INSERT INTO Orderltem (orderid, detail, partid, qty)
I xm
l
VALUES (6148, 6, 1010, 2);
INSERT INTO OrderItems (orderid, detail, partid, qty)
VALUES (6148, 7, 1001, 2);
343
Appendix B - Example Databases
344
Example
Data
L
INSERT INTO Orderltemn (orderid, detail, partid, qty)
VALUES (6175, 7, 1007, 2);
INSERT INO Orderztm (orderid, detail, partid, qty)
VALUES (6175, 8, 1008, 1);
INSERT INTO OrderItems orderedd, detail, vartid, aty)
VALUES (6175, 9, 1009, 1);
345
Appendix B - Example Databases
346
Example
Data
347
Appendix B - Example Databases
348
Ex
INSERT INTO OrderItems (orderid. detail, partid, qty)
VALUES (6237, 6, 1004, 3);
INSERT INTO OrderItems (orderid, detail, parted, qty)
VALUES (6237, 7, 1005, 3);
INSERT INTO OrderItems (orderid, detail, partid, qty)
VALUES (6237, 8, 1006, 1);
INSERT INTO OrderItems orderedd, detail, partid, qty)
VALUES (6237, 9, 1007, 2);
349
Appendix B - Example Databases
350
Ex
INSERT INTO OrderItems (orderid, detail, partid, qty)
VALUES (6252, 3, 1005, 1);
INSERT INTO OrderItems (orderid, detail, partid, qty)
VALUES (6252, 4, 1006, 2);
INSERT INTO OrderItems orderedd, detail, partid, qty)
VALUES (6252, 5, 1007, 10);
INSERT INTO OrderItems (orderid, detail, partid, qty)
VALUES (6252, 6, 1008, 1);
INSERT INTO OrderItems (orderid, detail, partid, qty)
VALUES (6252, 7, 1009, 1);
INSERT INTO OrderItems (orderid, detail, partid, qty)
VALUES (6252, 8, 1001, 2);
INSERT INTO OrderItems (orderid, detail, partid, qty)
VALUES (6252, 9, 1002, 10);
351
Appendix B - Example Databases
352
I Example
Data
L
INSERT INTO OrderItems ordered , detail, partid, qty)
VALUES (6292, 3, 1010, 1);
INShRT INTO Orderlts orderedd, detail, partid, cty)
VALUES (6292, 4, 1001, 1);
INSERT INTO OrderItems (orderid, detail, parted, qty)
VALUES (6292, 5, 1002, 1);
INSERT INTO Order:tems (orderid, detail, partied, qty)
VALUIES (6292, 6, 1003, 1);
INSERT INTO OrderItems orderedd, detail, partid, qty)
VALUES (6292, 7, 1004, 1);
INSERT INTO OrderItems (orderid, detail, partid, qty)
VALUES (6292, 8, 1005, 1);
INSERT INTO OrderItems (orderid, detail, partid, qty)
VALUES (6292, 9, 1006, 1);
353
Appendix B - Example Databases
354
-h
INSERT INTO OrderItems orderedd, detail, partid, qty)
VALUES (6314, 2, 1003, 2);
INSERT INTO OrderItem orderedd, detail, partid, qty)
VALUES (6314, 3, 1009, 2);
INSERT INTO Orderltems (orderid, detail, partid, qty)
VALUES (6314, 4, 1010, 2);
Chapter 6 Examples
In Chapter 6 we discussed the command for querying your SQL system, the
SELECT statement. Several tables with corresponding data were used to
illustrate this important command, and this where we show you how to
create them.
The first statement creates the columns, then the statements insert the
necessary data into the table. You need to avoid having a NOT NULL clause
for column c because for this example you actually need to place a NULL
value in it.
355
Appendix B - Example Databases
(a ItNTEGER MfLL,
NOT
x ZINTEGER);
356
- T
357
Appendix
Users
schema objects
IM privileges
We will look at each of these in turn. There are also two operations that
SQL uses to maintain security:
M GRANT
40REVOKE
Appendix C - Security and Control in Your Database
Users
The mapping of names and passwords, or authorization identifiers as they
are called in SQL, to operating system users is implementation dependent.
This can be done as part of the GRANT CONNECT TO Watcom SQL statement
and as part of the CONNECT statement in other SQLs, or it can be done
separately and may or may not be linked to the operating system.
360
Th
RN
Granting Privileges
Once the new user id has been created, the next stage is to give the user
certain privileges. This is done with the GRANT statement:
In terms of the three concepts discussed earlier, the GRANT clause deals with
the privileges, while the ON clause deals with the schema objects, and the TO
clause deals with the users.
The grantor has to hold the privilege that they grant and must have a WITH
GRANT OPTION to pass it to another user. If the grantor also passes those
privileges via the WITH GRANT OPTION then the grantee can become a
grantor of those same privileges.
In short this is a chain of authority, but the chain has to start somewhere.
SQL has a fictional grantor of all things named -SYSTEM (note the single
underscore) in the database at its creation, who has the rights to everything.
The users can access the schema definition tables and the DBA can set up
the initial configuration only because they have these privileges from
-SYSTEM. When a user creates a new schema object, the fiction is that all of
their privileges as the owner of the object come from -SYSTEM. In practice
the DBA will gain control of the SYSTEM password immediately and play
this role.
361
Appendix C - Security and Control in Your Database
If a grantor to tries to give the same privileges to a grantee twice then the
duplicate privilege descriptors are redundant and won't appear in the
Information Schema tables.
It is possible for two or more grantors to try and give the same privileges
to the same grantee. If two privilege descriptors are identical except that
one indicates that the privilege is grantable and the other indicates that the
privilege isn't grantable then both privilege descriptors bre set to indicate
that the privilege is grantable. This is a point where SQL's security is
particularly weak; other people can sabotage your attempts to restrict access
to data and you have to get the other grantors to explicitly REvoKE
privileges.
Schema Objects
The schema objects that the DCL controls can be base tables, views,
columns, domains, character sets, collations, and translations. The last four
objects are new to SQL-92 and we won't be worrying about them.
Obviously they've existed implicitly in SQL before now - it would be
impossible to store data without character sets or to do a sort without a
collation sequence, but SQL-92 has made them explicit for
internationalization and ISO conformance.
Privileges
If a user tries to do something for which he or she doesn't have privileges
then the system issues a warning - 'privilege not granted' or something
362
I ch
similar and the transaction fails. If there aren't any security problems then
the statement is passed along to the query processor where it will succeed
or fail on its own merits.
Actions
The privileges action list tells the grantee what the grantor will let them do
with the table or the updatable view. The possible actions are:
4 INSERT
UPDATE
~ DELETE
SELECT
REFERENCES
SREFERENCES
(<column name list>)
4 USAGE
The actions INSERT, UPDATE, DELETE, SELECT, or REFERENCES are called table
privilege descriptors. The actions SELECT (<column name list>), INSERT
(<column name list>), UPDATE (<column name list>), and REFERENCES
(<column name list>) are called column privilege descriptors. Most
implementations today only have table privilege descriptors and they
achieve the column privilege descriptors effect by using views.
The INSERT, UPDATE, DELETE and SELECT actions allow the grantee to use
those statements on that schema object. When these privilege descriptors are
specified without a column name, privilege descriptors are also
automatically created for each column in the table or view.
The USAGE privilege allows the grantee to use a character set or other 'meta
data' schema objects when constructing new schema objects. This action was
defined in SQL-92 and isn't yet common place.
363
Appendix C - Security and Control in Your Database
GRA SELECT?
ON Salespersons
TO Joe;
The user Joe, once he'd logged would be able to query the Salespersons
table. However, for the user Joe, it isn't quite as straightforward as simply
typing in the query, as you might expect:
The user Joe has to specify who owns the table also, which in this case is
DBA. This creates a secondary problem in Watcom SQL as DBA is a
reserved keyword, and therefore has to be encased in quotation marks. To
actually query the Salespersons table, Joe has to use the following query:
If you wished to pass on all privileges to Joe on the Inventory table and
allow him to pass on these privileges to other users, then the following
statement would apply:
364
L Stm
Joe would then in turn be able to pass on those privileges to other users,
providing that the other users had been created by the GRANT CONNECT TO
statement.
The REVOKE statement has to be done by the grantor who granted the
privileges to the grantee. Once the grantee has lost those privileges, the
effect can cascade down to the users to whom the grantee granted
privileges. If two different grantors have given the same privileges to a
grantee, then only one set of privileges is revoked. If a schema object is
completely cut off from any grantor, so that nobody can get privileges on it,
then the object is supposed to be dropped from the schema. Likewise, if an
object is dropped, then all privileges granted on it are automatically
revoked.
GRANT OPTION FOR removes the ability to grant that privilege, but not the
privilege itself.
The RESTRICT clause says that if this privilege would also remove the
ability to access a schema object from another user, then give a warning
message and not to perform the revocation. The CASCADE clause says revoke
the privileges for the named users and keep following the chain until
you've cleared out all derived privileges.
The SQL-92 standard requires that one of these options be used, but most
implementations don't have them yet. Right now, each SQL implementation
has a default behavior which might be a CASCADE, a RESTRICT or something
else. You will also see other special fictional users in Sybase, groups of
privileges and objects put together as ROLES in Oracle 7.0 and so forth.
365
Appendix C - Security and Control in Your Database
Summary
We have looked very briefly at the main concepts of the DCL. It revolves
around 3 main concepts: users, schema objects and privileges. As SQL is
weak on security, a lot of the emphasis in keeping tight security must
revolve around good security practices. This means giving GRANT OPTIONS
only to those who can be trusted not to freely dish them out. We've looked
at the main commands used in assigning and revoking privileges, but
basically it's up to the administrators of the system to ensure that security
isn't violated or compromised.
366
Summary
L--
367
Appendix
Appen
X/Open Foundation
c/o Blanc & Otis Public Relations
100 Spear Street #425
San Francisco, CA 94105
Att: Steven Curry
ph: (415) 546-8080; ph: (415) 546-8090
In the UK:
370
I SQL
Standards
L
371
Appendix
Over the page is a table of the valid combinations of source and target data
types in SQL-92. This is often referred to as the 'table of legal conversions'.
<valu e ex pr>
00 E -o a'
0 -z n-~ 0' 2z >
1<U ) O-~ 0, arc'0 -
0D 2D (D "'D cD2 3
< K CL Q-w
-' L<.a
CD a) 3
3 a 3
z m
K Kt z z z z -< C3
C >
z Z z z z -< -< -< -<
0 -R
C-) r- -
ca.
Z z z z z -< -< z z A
U)
z z z z z - -<z z
aa
CD
-I
z z -< 0- -< z -< z z V
z Z -< -< z z -• Z
h) 3~
z z -< "< -< z -< z z 3 o
-0
z -< z z z z -< z K 0
a'
"-< z z z z -< z • -a'
0
374
Legal
Conversions
375
INSTANT
Index
Symbols A
(MOD) function 86 ABS0 functions 86
= 33 AGEO function 94
* 84 aggregate functions
+ 84 and grouped tables 218
- 84 AVG functions 222
/ 84 COUNT functions 219
< 95 extreme functions 224
<= 95 GROUP BY 211 - 217, 227
NULLs 218
<> 95
SUM functions 220
= 95
aliases 191
> 95
ALL 218
>= 95
aggregate functions with groups of tables
I 32
218
1 88 and subqueries 242 - 243
[1 32 ALTER TABLE 49, 75 - 78, 127
lnf 118 and SELECT 161
2nf 120 ANSI SQL-92
3nf 123 nesting expressions 46
3GLs. See procedural versus row comparisons 107
declarative languages syntax for joining tables 200
4GLs inner joins 201
embedded SQL 271 outer joins 202
Index
378
Id
379
Index
380
In
F NUMTOWORDS0 91
POSITION) 89
FALSE 97
POWER() 86
FETCH 277
REVERSE) 90
syntax 277 ROUND 86
file-oriented language 17 SQRTO 86
FLIP( function 90 SUBSTRING0 88
fold functions 88 SUMO 220
FOREIGN KEY 116 TRIM() 89
constraints 130 TRUNCATED 86
FORTRAN UPPER 87
and algebraic model 44 WEEKDAY( 93
FROM YEAR 94
inner joins 185 - 200
subqueries 245
functions
(MOD) 86 G
ABSO 86 General Regular Expression Parser
AGEO 94 (grepo) 90
AVG0 222
getdateo function 93
CASTO 38
GRANT
CHECKO 62, 127
security in a multi-user environment
COALESCE() 105, 106
See Appendix C
CONVERTTO() 38
grepo function 90
COUNTY 219
GROUP BY 211 - 217, 303
COUNTER 86
grouping tables 303
DAYO 94
and aggregate functions 227 - 228
DOW0 93
HAVING 229
EXTRACT() 94
NULLs 217
FLIP() 90 grouped tables 211
getdateo 93 aggregate functions 218
grep0 90
NULLs 218
LENGTH 89 common problems with 216
LOWER 87 HAVING 211
MAKEDATE0 94 UNIQUE 215
MAX( 224 PRIMARY KEY 215
MIN0 224
MONTH 83
NULLIFY 105
381
Index
H interactive tools 27
ASCII files 27
hashing 293, 297 - 298
connecting to example database 28
HAVING interval data type 92
and subqueries 248
IS FALSE 176
GROUP BY restriction 211, 229
IS NULL 176
as a correlated query 260
IS TRUE 176
IS UNKNOWN 176
I
impedance mismatch J
embedded SQL 271
join 159
IN 172
joining tables 183 - 207
CHECKO constraint 173
AS and correlation name 192
indexing 293 - 297
cross joins 185 - 186
binary tree 296
equi joins 187 - 190
empid value 296
joining a table to a copy of itself 192
clustered 295
non-equi joins 190
defining 294
SELECT
techniques 297
Cartesian product 184
keys 294
self joins 191 - 196
tables 293
and aliases 191
indicator variables 273
SQL-92 syntax 200
inner joins 185 - 200
theta joins 191
cross join 185
three table joins 197 - 200
equi join 187
INNER JOIN 200
self join 191 - 192
non-equi join 190 K
ON 200
theta join 191 key
three table joins 197 - 200 and indexing 294
INSERT INTO 145 - 146 definition of 114 - 115
and subqueries 250 types of 115 - 116
candidate 115
syntax 145
foreign 116
value list insertion 146
intelligent 115
integer
multi-value 120
string equality 87 primary 116
integrity 26 super 116
referential 27 surrogate 115
intelligent key 115 keywords 32
382
Id
L N
languages non-equi joins 190
FORTRAN 44 non-terminal 32
algebraic model 44 normalization 117 - 124
procedural versus declarative 12 CASCADE 132
sets and SQL 44 constraints 126
diagram 44 ALTER TABLE 127
underlying models 44 CHECK() 127
legal conversions 49. See also Appendix FOREIGN KEY 130
E PRIMARY KEY 130
LENGTHO function 89 UNIQUE 128
LIKE 170 first normal form (lnf) 118
and queries 171 delete update and insertion anomalies
null and empty strings 172 120
wildcard 170 redundant information 117
literals 46 RESTRICT 132
logical expressions 97 second normal form (2nf) 120
FALSE 97 multi-value key 120
third normal form (3nf) 123
SQL and Boolean operators 97
transitive dependencies 123
TRUE 97
NOT NULL constraint 60
UNKNOWN 97
syntax 61
LOWERO function 87
NULL 97, 99 - 107, 168, 172, 217
and SELECT 168
concatenation 106
M conversion to and from 105
grouping tables 217
MAKEDATE0 function 94 NULLIFY 105
mantissa 55 row comparisons 107
math functions 84 numeric data types 54
vendor 86 approximate 55
MAX() function 224 comparison operators 95
MIN0 function 224 conversion 96
missing values. See NULL CAST or CONVERT 83
MONTHO function 83 date/time 91 - 93
multi-value key 120 functions 93 - 94
displaying dates 91
exact
precision P 55
scale S 55
fold functions 88
383
Index
384
IIn
syntax 63 R
privileges
GRANT in Watcom SQL radix
See Appendix C precision P 55
procedural versus declarative languages references 64
12 referential integrity 66, 113
READ and WRITE statements 14 syntax 65
referential actions 134
projection 160
SQL-89 vs SQL-92 134
PUBLIC
table events 134
privileges in DCL
referential integrity 27, 113, 126
See Appendix C
constraints 27
FOREIGN KEY 130
PRIMARY KEY 130
0 UNIQUE 128
drop behavior 132
quantified predicates keys 113
ALL 239, 242 - 243 referential actions 134
and subqueries 239 relational databases
ANY 239 - 242 SQL 15
queries 158 - 178 RESTRICT 132
BETWEEN 174 - 176
restriction 166
hints on how to write 299 - 308
result table 45
the HAVING clause 303
REVERSE() function 90
table names and FROM 301
REVOKE
joining tables 302
test and repair 307 security in a multi-user environment
IN 172 - 173 See Appendix C
IS NULL 176 rolling back 26
IS TRUE 176 ROUND() function 86
IS FALSE 176 row
IS UNKNOWN 176 definition 21
LIKE 170 - 171 spreadsheet analogy 22
NULLs and empty strings 172 union compatibility 49
ORDER BY 177 - 178 row comparisons 107 - 109
WHERE 166 - 169
query insertion 146
385
Index
386
In
387
Index
388
In
U VIEW 257
view column list 263
union compatibility 49
CHECKO option clause 263 - 264
UNIQUE virtual table. See views
grouped tables 215
constraints 63, 129
Universal Co-ordinated Time (UTC)
ANSI SQL-92 58 W
UNKNOWN 97
Watcom SQL engine
and NULL 97
manipulation of database information 145
logical values 99
WEEKDAY( function 93
UPDATE 150 - 151
WHERE 151 - 154
and subqueries 251
and SELECT 166
positioned in embedded SQL 280
predicates 169
syntax 280
inner joins 186
UPPER() function 87
subqueries 235
V
Y
value list insertion 146
YEAR() function 94
vendor data types 54
vendor string functions 90
views 45, 255 - 266
comparison with other database schema
objects 256
CREATE SCHEMA 266
CREATE VIEW syntax 257
definition of 255
dropping 265
handling 256
hints on using 266
in-line 259 - 260
materialised 257 - 258
HAVING as a correlated query 260
read only 261 - 262
updatable 261 - 262
389
INSTANT DELPHI PROGRAMMING
This book provides a fast guide to the essentials of Borland's new development
tool. Borland have put together fast executable Pascal code with a truly
intuitive event-driven environment The result is a powerful, yet easy to use
program, and this book caters for programmers who want to master its
fundamental advantages. Taking developers through the strengths of the
innovative Object Pascal code, its access to a database engine, as well as
advanced features such as using VBX controls, this book will help
programmers from many different backgrounds move successfully into
Borland's strong new package.
C++
The runtime version doesn't allow ALTER, CALL, COMMENT, CREATE, DROP,
user-defined trigger commands or stored procedures to be performed. GRANT
and REvoiE allow you to add new users and change passwords, but the
runtime database engine prevents a user from changing the permissions on
the tables. In addition, to simplify database administration, the runtime
system has an integrated transaction log.
The Watcom SQL Server product has networking features which are beyond
the scope of the stand-alone version of Watcom SQL. As a result, features
such as DBWATCH, DBCLIENW and DBSERVEW are only available on the Watcom
SQL Server product line and aren't included in Watcom SQL or Watcom
SQL runtime.
Once you have used runtime version you may decide that you wish to
upgrade to the full version of the Watcom SQL engine. You can contact
Watcom at the following addresses:
Complete and return the bounce back card and you will:
Name What influenced you in the How did you rate the overall
Address purchase of this book? contents of this book?
Cover Design Excellent 1JGood
Contents Average [ Poor
Postcode/Zip F-1Other (please specify)
Occupation
How did you hear about this book? What did you find most useful about this book?
[] Book review (name)
E] Advertisement (name) What did you find least useful about this book?
D Recommendation
E] Catalogue Please add any additional comments.
[] Other
Where did you buy this book? What other subjects will you buy a computer
L] Bookstore (name) book on soon?
D Computer Store (name)
SMail Order What is the best computer book you have used this year?
[-I Other
I would be interested in receiving information about Wrox Press titles Note: This information will only be used to keep you updated about
by email in future. My email/Internet address is: new Wrox Press titles and will not be used for any other purpose or
passed to any other third party,
S - - -
Compuserve 100063,2152.
UK Tel. (44121) 706 6826 Fax (44121) 706 2967
NB. If you post the bounce back card below in the UK, please send it to:
Wrox Press Ltd. Unit 16, Sapcote Industrial Estate, 20 James Road, Birmingham, Bll 2BA
NO POSTAGE
111111NECESSARY
IF MAILED
IN THE
UNITED STATES
WROX PRESS
2710 WEST TOUHY AVE
CHICAGO IL 60645-9911
I,[Link].I
I- IL ..Wralk-
i ng
Instant SQL Programming
T
= his is the fastest
developers to theguide
database management
mostfor
common
language.
features everything you need:
A Master the key database language
A Learn database theory in an instant
If you want to get the most out of your
-
' W R ASCESS.,
-:L i'
14.7
E ISBN 1-874416-50-8
110079 23.
INSTANT SQL
'The Revolutionary 'The Revolutionary 'Instant Delphi Programming' PC BOOK PROGRAMMING
GuidetlPswerbuilder' Guide to Access'
JBCOE: D50748
ISBN 1.874416-60-5 ISBN 1-874416-39-7 ISBN 1-874416-57-5
W-oxPetnIc.2710W Tahy,C g linois60645. WroxPrcLtd., Se 1I, Bimnh
[Link], B112BA.