NULL
values
Care is needed when retrieving rows in tables that may contain NULL
values. As the reading shows, NULL
has some unique
characteristics when compared to other values.
Multiple table queries
The following reading from your textbook Multiple Table Queries shows how to join tables together to retrieve information from multiple tables.
WHERE
clause for each pair of tables
being joined. Thus, if two tables are to be combined, one condition
would be necessary, but if three tables (A, B and C) are to be
combined, then two conditions would be necessary because there are two
pairs of tables (A-B and B-C), and so forth.
A summary of the different types of Joins is provided opposite .
Understanding the order in which the different parts of a SELECT
query are executed will help you write more complicated queries and
work out why other queries don't work as you expected.
Handling DATE
s
Oracle's treatment of DATE
s does not follow the SQL
standard. This short reading explains how DATE
s are
implemented and how to use them. If you wish, you can skip this section
until you come to the queries below that use DATE
s.
Invoice database 1
Now try the following queries on the
INVOICE
database.
These activities relate to the PowerPoint Presentation “Multi-table queries” refer to “get your hands dirty”
1. Find the customer details for invoice 12
CUSTABN | CUSTNAME | CUSTSUBURB | CUSTNAME | CUSTSTATE | CUSTPOSTCODE |
---|---|---|---|---|---|
17652258874 | Adams Tractors | 65 Earl St | Terara | NSW | 2540 |
2. Find the names of all customers who brought an item with Tube in its description
CUSTNAME |
---|
Victor Panels |
Astian Court Holdings |
3. Find the customers who live in NSW or Qld who brought Screens
CUSTNAME |
---|
Victor Panels |
Mellow Yellow Lighting |
Australian Drums |
Australian Drums |
4. Find the number of Screens brought by customers in NSW and Qld
CUSTNAME | SUM(QUANTITY) |
---|---|
Australian Drums | 10 |
Victor Panels | 5 |
Mellow Yellow Lighting | 4 |
Check (invoice database 1)
The Answers to these will be available in Week Tasks multiple table queries.
The specific day is mentioned in announcements.
Invoice database 2
Now try the following queries on the
INVOICE
database.
1. For each customer that has an invoice, what is the customer's
name and invoice number?
2. Show which customers have received an invoice and sort by name.
Remove any duplicates.
3. Show the customer ABN and name and list all of the items as well
as a quantity, description and price for invoice number 21.
4. Find the number of invoices for each customer name.
5. Find the customer name, invoice number and value of each invoice.
6. Find the number of invoices for each customer whose name starts
with A to E. Try writing this query two ways: once without the HAVING
clause, once with the HAVING
clause.
7. Find all the customers who have more than one invoice. Could you write this query without the HAVING clause?
8. For each customer, find out how much their invoices are worth.
9. Find all the customers who have more than one invoice and have the letter t or T in their name.
10. Find all customers who have invoices dated before August 2005.
11. Find the customer name, invoice no and date (format MON 22 AUGUST 2005) for invoices in August 2005.
Check (invoice database 2)
1. For each customer that has an invoice, what is the customer's name and invoice number?
SELECT CUSTOMER.CustABN, CustName, InvoiceNo
FROM CUSTOMER, INVOICE
WHERE CUSTOMER.CustABN = INVOICE.CustABN;
Notice in the query we had to declare which table we
wanted the CustABN
to come from, and in the WHERE
statement declare the fields in both tables.
This is because there is ambiguity, as CustABN
exists
in both tables. We must specify which table SQL Plus should pick the
field from. This is only a problem when we have the same field names
(notice we did not worry about CustName
and InvoiceNo
).
Remember that we can also uses aliases for our field names. We can
also do the same for our tables. If the above query was a more involved
one we would soon get sick of typing CUSTOMER.Something
or INVOICE.Something
.
We can make life easier with something like this:
SELECT CUSTOMER.CustABN AS ABN, CustName AS Name, InvoiceNo AS
"Invoice Number"
FROM CUSTOMER c, INVOICE i
WHERE c.CustABN = i.CustABN;
This returns the same but with meaningful column headers. Note the
use of double quotes as there is a space in the string.
2. Show which customers have received an
invoice and sort by name. Remove any duplicates.
SELECT DISTINCT c.CustABN AS ABN, CustName AS Name
FROM CUSTOMER c, INVOICE i
WHERE c.CustABN = i.CustABN
ORDER BY CustName;
3. Show the customer ABN and name and list all
of the items as well as a quantity, description and price for invoice
number 21.
SELECT c.CustABN, CustName, inv.InvoiceNo, Quantity,
init.ItemNo, ItemDesc, ItemPrice
FROM CUSTOMER c, INVOICE inv, INVOICEITEM init, ITEM it
WHERE c.CustABN = inv.CustABN
AND init.InvoiceNo = inv.InvoiceNo
AND it.ItemNo = init.ItemNo
AND inv.InvoiceNo = 21;
4. Find the number of invoices for each
customer name.
SELECT CustABN, COUNT(InvoiceNo)
FROM INVOICE
GROUP BY CustABN;
As we want to display the name of the customers instead of the ABN,
we need to join the CUSTOMER
and the INVOICE
tables. The linking field for the two tables is CustABN
.
SELECT c.CustName, COUNT(i.InvoiceNo)
FROM CUSTOMER c, INVOICE i
WHERE c.CustABN = i.CustABN
GROUP BY c.CustName;
We could also use aliases for CustName
and COUNT(i.InvoiceNo)
5. Find the customer name, invoice number and
value of each invoice.
SELECT SUM(Quantity * ItemPrice) as InvTotal, CustName,
inv.InvoiceNo
FROM InvoiceItem ii, Item i, Invoice inv, Customer c
WHERE ii.ItemNo = i.ItemNo
AND inv.InvoiceNo = ii.InvoiceNo
AND inv.CustABN = c.CustABN
GROUP BY inv.InvoiceNo, CustName;
6. Find the number of invoices for each
customer whose name starts with A to E. Try writing this query two
ways: once without the HAVING
clause, once
with the HAVING
clause.
Without the HAVING
clause:
SELECT c.CustName, COUNT(i.InvoiceNo)
FROM CUSTOMER c, INVOICE i
WHERE c.CustABN = i.CustABN
AND c.CustName < 'F'
GROUP BY c.CustName;
With the HAVING
clause:
SELECT c.CustName, COUNT(i.InvoiceNo)
FROM CUSTOMER c, INVOICE i
WHERE c.CustABN = i.CustABN
GROUP BY c.CustName
HAVING c.CustName < 'F';
7. Find all the customers who have more than
one invoice. Could you write this query without the HAVING
clause?
SELECT c.CustName, COUNT(i.InvoiceNo)
FROM CUSTOMER c, INVOICE i
WHERE c.CustABN = i.CustABN
GROUP BY c.CustName
HAVING COUNT(i.InvoiceNo) > 1;
Would this query work?
SELECT c.CustName, COUNT(i.InvoiceNo)
FROM CUSTOMER c, INVOICE i
WHERE c.CustABN = i.CustABN
AND COUNT(i.InvoiceNo) > 1
GROUP BY c.CustName;
COUNT(i.InvoiceNo) > 1
will generate a syntax error.
WHERE
works on
individual rows - only rows that satisfy the condition(s) are included. HAVING
works on groups - only groups that
satisfy the condition(s) are included. Thus WHERE
conditions cannot include grouping functions and HAVING
cannot include conditions that operate at the row and not the group
level.
8. For each customer, find out how much their invoices are worth.
SELECT c.CustName, SUM(Quantity*ItemPrice)
FROM CUSTOMER c, INVOICE i, INVOICEITEM ii, ITEM it
WHERE c.CustABN = i.CustABN
AND i.InvoiceNo = ii.InvoiceNo
AND ii.ItemNo = it.ItemNo
GROUP BY c.CustName;
9. Find all the customers who have more than one invoice and have the letter t or T in their name.
SELECT c.CustName, COUNT(i.InvoiceNo)
FROM CUSTOMER c, INVOICE i
WHERE c.CustABN = i.CustABN
AND (c.CustName LIKE '%t%' OR c.CustName LIKE '%T%')
GROUP BY c.CustName;
Note the use of the ( )s. This ensures that the only rows included satisfy:
- the join condition
(c.CustABN = i.CustABN),
and - have a 't' in their name
(c.CustName LIKE '%t%' OR c.CustName LIKE '%T%')
Without the ( )s, namely:
SELECT c.CustName, COUNT(i.InvoiceNo)
FROM CUSTOMER c, INVOICE i
WHERE c.CustABN = i.CustABN
AND c.CustName LIKE '%t%' OR c.CustName LIKE '%T%'
GROUP BY c.CustName;
The WHERE
clause is interpreted as rows that satisfy
either:
- the first condition
(c.CustABN = i.CustABN AND c.CustName LIKE '%t%'),
or - the second condition
(c.CustName LIKE '%T%')
.
This gives a rubbish answer.
AND
has higher precedence than OR
. So use ( )s
in statements that have both AND
and OR
conditions.
10. Find all customers who have invoices dated before August 2005.
SELECT DISTINCT CustName
FROM CUSTOMER c, INVOICE i
WHERE c.CustABN = i.CustABN
AND TRUNC(InvDate) < DATE '2005-8-1';
11. Find the customer name, invoice no and date (format MON 22 AUGUST 2005) for invoices in August 2005.
SELECT CustName, InvoiceNo, To_CHAR(InvDate,
'DY DD MONTH YYYY')
FROM CUSTOMER c, INVOICE i
WHERE c.CustABN = i.CustABN
AND TRUNC(InvDate) >= DATE '2005-8-1' AND TRUNC(InvDate) <= DATE
'2005-8-31';
Invoice database 3
Now try the following queries on the
INVOICE
database.
These activities relate to the PowerPoint Presentation “Create Tables” refer to “get your hands dirty”
1. For customers living in Queensland, find the customer name, invoice number and invoice date
CUSTNAME | INVOICENO | INVDATE |
---|---|---|
Victor Panels | 20 | 28/AUG/05 |
Victor Panels | 5 | 13/APR/05 |
Lenton Holdings | 16 | 25/AUG/05 |
Australian Drums | 17 | 25/AUG/05 |
Astian Court Holdings | 10 | 10/JUN/05 |
2. For customers living in Queensland, find the customer name, invoice number and invoice date for invoices with numbers greater than 18 or less than 11
CUSTNAME | INVOICENO |
---|---|
Victor Panels | 5 |
Victor Panels | 20 |
Astian Court Holdings | 10 |
3. Find the number of invoices for each customer in Queensland
CUSTNAME | COUNT(*) |
---|---|
Australian Drums | 1 |
Victor Panels | 2 |
Lenton Holdings | 1 |
Astian Court Holdings | 1 |
4. Can you find the answer to number 3, both using the having clause and not using it?
CUSTNAME | COUNT(*) |
---|---|
Australian Drums | 1 |
Victor Panels | 2 |
Lenton Holdings | 1 |
Astian Court Holdings | 1 |
Check (invoice database 3)
The Answers to these will be available in Week Tasks multiple table queries.
The specific day is mentioned in announcements.
Student database - more SQL queries
1. Which students have Fred Flintstone as their course coordinator?
2 Which students are doing the course Project?
3. For the coordinator with Employee_no AS1234, find his/her name and the average number of units the students he/she advises have completed.
4. For all the coordinators whose employee number start with AS find his/her name and the average number of units the students he/she advises have completed.
Check (Student database)
1. Which students have Fred Flintstone as their course coordinator?
SELECT Student.Name
FROM Student, Coordinator
WHERE Student.Coord_No=Coordinator.Coord_No
AND Coordinator.Name = 'Fred Flinstone';
Note that for this query all the columns had to be qualified (e.g. Student.Name
).
The columns by themselves (e.g. Name
or Coord_No
)
would have been ambiguous as they appeared in both tables. If the
column name is unique in all tables involved in the query it need not
be qualified e.g. the columns Employee_No
or Total_Units
are unique across both Student
and Coordinator
.
2 Which students are doing the course Project?
SELECT Student.Name
FROM Student, Course, Grade
WHERE Student.Student_No=Grade.Student_No
AND Course.Course_No = Grade.Course_No
AND Course.Name = 'Project';
3. For the coordinator with Employee_no AS1234, find his/her name and the average number of units the students he/she advises have completed
SELECT c.Name, AVG(Total_Units)
FROM Student s, Coordinator c
WHERE s.Coord_No = c. Coord_No
AND Employee_No = 'AS1234'
GROUP BY c.name;
Note that with this example the tables are joined together and the
conditions calculated (the effect of the WHERE
and AND
clauses are computed) before the grouping and aggregations functions
are applied.
4. For all the coordinators whose employee number start with AS find his/her name and the average number of units the students he/she advises have completed.
SELECT c.Name, AVG(Total_Units)
FROM Student s, Coordinator c
WHERE s.Coord_No = c. Coord_No
AND Employee_No LIKE 'AS%'
GROUP BY c.name;
Or another alternate way to write the query would be:
SELECT c.Name, AVG(Total_Units)
FROM Student s, Coordinator c
WHERE s.Coord_No = c. Coord_No
GROUP BY c.name, Employee_No
HAVING Employee_No LIKE 'AS%';
Note to use the Employee_No
column in the HAVING
clause it needs to appear in the GROUP BY
clause.