L2DTS SQL With Joins
L2DTS SQL With Joins
The Relationships between the tables linked by primary and foreign keys:
Whenever there is a primary key in one table that links to a foreign key, a primary key in another
table, a relationship exists between the two tables and we can perform queries that make use of that
relationship to gain meaningful information.
The primary/foreign key relationships that exist in this database are:
ActNum primary key in tblActs links to the foreign key ActNum in tblEntrants; the
relationship is indicated as one-to-many, indicating that one act may have many entrants.
ActNum primary key in tblActs links to the foreign key ActNum in tblPoints; the
relationship is indicated as one-to-one, indicating that one act only has one set of points.
When there is a one-to-one relationship between primary keys of two tables, the
primary key of each table is considered to be a foreign key in the other table.
We are going to investigate many queries that use the primary/foreign key relationships between
these tables. For example, there is no direct link between tblEntrants and tblPoints, but what if we
wanted to know how many points were attributed to an entrant? We simply use the fact that
tblEntrants is linked to tblActs through the primary/foreign key relationship on ActNum in each
table; then we link the primary/foreign keys ActNum between tblActs and tblPoints to create a
relationship ‘path’ from tblEntrants to tblPoints.
ACTIVITY 1
Open the WhoGotTalent database and open tblEntrants and tblActs in datasheet view.
Take note of the number of records (rows) there are in each of the tables. If you are using an
original version of the database, you should see that tblEntrants has 47 records and tblActs
has 28 records.
Code the following query join the two tables together:
SELECT FirstName, Surname, ActName, Category
FROM tblEntrants, tblActs
Run the query and you should notice a Result Set similar to the following screenshot:
A total of 1316 records were generated in the Result Set which is exactly the product of the number of
records in the tblEntrants table (47) and the tblActs table (28).
This is known as a Cartesian Product, where EVERY row in one table 'maps' to EVERY row in
another table. As can be seen 47x28 rows produces 1316 rows.
A Cartesian product occurs when the query is not given any specific criteria to HOW to match the
data in the one to table to the other. By specifying how to join the two tables, we can reduce the
number of rows and only match rows that are linked using common fields.
ACTIVITY 2
Add the following WHERE clause to the query:
SELECT FirstName, Surname, ActName, Category
FROM tblEntrants, tblActs
WHERE [Link] = [Link]
The Result Set now shows exactly the number of records in tblEntrants (47), which indicates that
each entrant has been matched with her/his ActName. When the fields in two tables are combined
in this way we say the tables have been JOINED.
Note: when two tables have the same field name you must include the table name for each field. For
example:
The condition:
WHERE ActNum = ActNum
All fields from both tables are shown in the order that they appear in their respective tables; not all of
the fields are displayed here because of width restrictions on this page.
The query produces 47 records as each Entrant successfully links the existing data about the
Entrant with his/her Act.
Change the SQL statement to list only three fields and add an ORDER BY clause to produce
the Result Set in alphabetical order according to entrant surname and then entrant first name:
SELECT Surname, FirstName, ActName
FROM tblEntrants, tblActs
WHERE [Link] = [Link]
ORDER BY Surname, FirstName
Run the query again to check that the outputs are correct.
Notice that the order is consistent with what was requested and the number of records remains at 47.
To list the name of the act in tblActs together with the first name and surnames of the entrants in
tblEntrants we can see that these fields come from different tables. We need to specify the names of
the fields, the two tables and HOW to join the table.
ACTIVITY 3
Type in the following query to view the entrants together with their acts.
SELECT [Link], [Link], [Link]
FROM tblActs,tblEntrants
WHERE [Link] = [Link]
Suppose we only wanted to see the entrants who are in grade 10.
Change the query to add the WHERE clause to find grade 10 entrants using the Grade field.
Include the Grade field after the SELECT to view the grades.
SELECT [Link], [Link], [Link],
Grade
FROM tblActs,tblEntrants
WHERE [Link] = [Link] AND Grade = 10
Change the query to find the entrants who can sing or dance. You can remove the table names
that prefix fields that are unique in the database.
SELECT FirstName, Surname, ActName, Category
FROM tblActs,tblEntrants
WHERE [Link] = [Link] AND Category = 'Singing' OR
Category = 'Dancing'
The Result Set contains 245 records with Leda Burnside being involved in six acts. From our previous
query, she was only involved in the ‘Tweetie Birds’ act.
The error is caused by the lack of brackets around the conditions in the WHERE clause to isolate the
singing or dancing categories. We need to force SQL to perform the OR condition before the AND.
Change the query to include brackets.
SELECT FirstName, Surname, ActName, Category
FROM tblActs,tblEntrants
WHERE [Link] = [Link] AND
(Category = 'Singing' OR Category = 'Dancing')
Check that your solution produces 19 records.
EXERCISE 1
1. List the entrants who have more than R10 000 in sponsorship money and require no props.
Display the entrant’s name, the name of the act, the sponsorship money and the PropsNeeded
field.
2. List the acts that have more than 8 points for Judge1. List the name of the act and the values for
the Judge1 field.
3. List the acts that have entrants in Grade 10,11 or 12 using the IN operator. Display the relevant
fields.
4. List the acts that have entrants in Grade 10A or 10B.
4.1. Would it possible to use the IN operator for this query?
A couple of general rules that will guarantee success when joining many tables:
For the fields chosen in the SELECT clause, the corresponding tables must be included in
the FROM clause
The WHERE clause establishes the correct links of primary to foreign keys across the
tables
The number of links between tables are one less than the number of tables. For example,
if there are three tables then there must be two links.
Even if fields from a particular table are not used in the SELECT clause, the tables must
be included in the FROM clause if they form part if the WHERE clause links
ACTIVITY 4
We may want to list the points achieved by each entrant.
Run the following query:
SELECT Surname, FirstName, ActName, Judge1, Judge2, Judge3
FROM tblEntrants, tblActs, tblPoints
WHERE [Link] = [Link]
AND [Link] = [Link]
Notice that fields from all three tables are SELECTed; which means that all three tables must be listed
in the FROM clause, and all three tables must be correctly linked in the WHERE clause.
We may want to determine the total points achieved by each entrant. Under normal circumstances,
we would use GROUP BY, but the way the tables have been designed here, each entrant only
participates in one act, so GROUP BY would not make any difference. Also, the sum of the points for
each entrant would not be an aggregate function SUM as SUM calculates a total for a single field not
across three different fields. The total can be determined by adding the values in the fields Judge1,
Judge2 and Judge3 as a calculated field.
Run the query:
SELECT Surname, FirstName, Judge1 + Judge2 + Judge3 AS TotalPoints
FROM tblEntrants, tblActs, tblPoints
WHERE [Link] = [Link]
AND [Link] = [Link]
We could include ORDER BY to list the records by Surname.
SELECT Surname, FirstName, Judge1 + Judge2 + Judge3 AS TotalPoints
FROM tblEntrants, tblActs, tblPoints
WHERE [Link] = [Link] AND [Link] =
[Link]
ORDER BY Surname
The generated field TotalPoints can be rounded to the nearest whole number.
Change the first line of the query to include the ROUND function:
SELECT Surname, FirstName, ROUND(Judge1 + Judge2 + Judge3,0) AS
TotalPoints
FROM tblEntrants, tblActs, tblPoints
WHERE [Link] = [Link] AND [Link] =
[Link]
ORDER BY Surname
Notice that no field from tblActs was SELECTed, so tblActs may not have been needed in the
FROM clause, but it was needed because it’s the only way to create a link between tblEntrants and
tblPoints, as required in the WHERE clause.
ACTIVITY 5
Run the following query to get the summary to one decimal place:
SELECT Category,
ROUND(AVG((Judge1+Judge2+Judge3)/3),1) AS JudgesAve
FROM tblActs, tblPoints
We can work out which acts were solo acts with only one entrant
using HAVING (the WHERE for aggregates) to restrict the rows
based on the aggregate.
Run the following query to list the ‘solo’ acts:
SELECT ActName, COUNT(*) AS NumEntrants
FROM tblActs, tblEntrants
WHERE [Link] = [Link]
GROUP BY ActName
HAVING COUNT(*) = 1
Remove the COUNT(*) AS NumEntrants clause as all the values are 1:
SELECT ActName AS SoloActs
FROM tblActs, tblEntrants
WHERE [Link] = [Link]
GROUP BY ActName
HAVING COUNT(*) = 1
ACTIVITY 6
Type in the following query that identifies those that do qualify using a HAVING to limit the rows
to those Categories that have an average >=80%,
SELECT Category, ROUND(AVG((Judge1+Judge2 + Judge3)/3),1) AS JudgesAve
FROM tblActs, tblPoints
WHERE [Link] = [Link]
GROUP BY Category
HAVING ROUND(AVG((Judge1+Judge2 + Judge3)/3),1) >= 8
Note that the list of categories produced do not contain categories ‘Çomedy’,
‘Drumming’, ‘Singing’ and ‘Ventriloquist’. however many are repeated.
Add DISTINCT after SELECT to ensure the list is unique.
SELECT DISTINCT Category AS Disqualified
FROM tblActs
WHERE
Category NOT IN
(SELECT Category
FROM tblActs, tblPoints
WHERE [Link] = [Link]
GROUP BY Category
HAVING ROUND(AVG((Judge1+Judge2 + Judge3)/3),1) >= 8)
It is important to exclude any other data fields in the embedded query since a Category in the
WHERE NOT IN condition can only be compared to a set of Category names.
ACTIVITY 7
One of the first queries that link two tables that we performed was:
SELECT Surname, FirstName, ActName
FROM tblEntrants, tblActs
WHERE [Link] = [Link]
Change the query to the following and run it to check that the outcome is the same:
SELECT Surname, FirstName, ActName
FROM
tblEntrants
INNER JOIN tblActs
ON [Link] = [Link]
Notice that there is no longer a WHERE clause that links the primary/foreign keys. The ON keyword
precedes the link.
A later query that linked all three tables was:
SELECT Surname, FirstName, ActName, Judge1, Judge2, Judge3
FROM tblEntrants, tblActs, tblPoints
WHERE [Link] = [Link]
AND [Link] = [Link]
When you link more than two tables, the second table is replaced by a “virtual table” enclosed in
brackets.
SELECT field1, field2, field3,…
FROM first_table
INNER JOIN (virtual_table)
ON first_table.keyfield = second_table.foreign_keyfield
The “virtual table” is a table produced by joining the second and third table.
(tblActs
INNER JOIN
tblPoints
ON [Link] = [Link])
Type in the following query to join three tables using a nested join:
SELECT Surname, FirstName, ActName, Judge1, Judge2, Judge3
FROM tblEntrants
INNER JOIN
(tblActs
INNER JOIN
tblPoints
ON [Link] = [Link])
ON [Link] = [Link]
Run the query to check that the result set is the same.
Adapt the query to find all the entrants, and the acts they belong to, that had a result of 8 and
above for ALL of the judges.
Employees Orders
ACTIVITY 8
Rewrite the above statement using an INNER JOIN.
Result Set
Name Product
Hansen, Ola Printer
Note that the employees that did not place any orders are not included in the table.
Example 2
Who ordered a printer?
SELECT [Link]
FROM Employees, Orders
WHERE Employees.Employee_ID=Orders.Employee_ID
AND [Link]='Printer'
Result
Name
Hansen, Ola
Name Product
Hansen, Ola Printer
Result
Name Product
Hansen, Ola Printer
Tove Svendson and Kari Pettersen have not placed any orders. There is no match for these records
in the Orders table. The LEFT JOIN includes all the records from the Employees table even if no
match is found and places null (a blank) in the Product column.
The previous SQL statement can be changed to list the employees who have not placed any orders:
SELECT [Link], [Link]
FROM Employees
LEFT JOIN Orders
ON Employees.Employee_ID=Orders.Employee_ID
WHERE [Link] is null
The last line check if the Product field contains a null using the operator is instead of the = sign.
Many LEFT JOINS can be written using NOT IN.
SELECT [Link], [Link]
FROM Employees
WHERE Employees.Employee_ID NOT IN
(SELECT Orders.Employee_ID
FROM Orders)
Check if this is right?
Result
Name Product
Hansen, Ola Printer
A RIGHT JOIN is the exact opposite of the LEFT JOIN and can be written as a LEFT JOIN by
swopping the tables around.
SELECT [Link], [Link]
FROM Employees
RIGHT JOIN Orders
ON Employees.Employee_ID=Orders.Employee_ID
and
SELECT [Link], [Link]
FROM Orders
LEFT JOIN Employees
ON Employees.Employee_ID=Orders.Employee_ID
produce the same results. So, it is simpler to always use a LEFT JOIN. Make sure that the table that
has extra fields is always listed first. This will include fields from the first table that do not have
matches in the second table.
SPID This field contains a unique numerical value for each salesperson (primary key).
SPName This field contains the name of the salesperson.
SPPhone This field contains the 'phone number of the salesperson.
tblClients stores the details of the clients and the sales person they deal with. Each client can only
have one sales person.
clientID This field contains a unique number (primary key) for each client recorded
in the database.
clientName This is a text field contains the client's name.
SPID This numeric field contains the identity number of the sales person who
attended to this client. This field is a foreign key for the tblSalesPeople
table.
tblItems stores the details of all the items sold by the company.
itemID This field contains a unique number for each item on sale (primary key).
itemDescription This text field contains a description of the sales item.
itemCategory This field contains either the value 'Interior' or 'Exterior'. It is used to classify
each sales item.
itemCostPrice This real number field contains the actual cost of the sales item.
tblInvoices records the information that would be stored on an invoice. Each client can have many
invoices and each item can appear many times on the invoice. For each item, we need to record the
number of items bought as well as the date of the invoice.
itemID This field contains the number of the item that was purchased. This field is a
foreign key for the tblItems table.
clientID This field contains the number of the client who purchased the item. This
field is a foreign key for the tblClients table.
quantity This integer field contains the quantity of the item that was purchased.
invoiceDate The date of the invoice in the format yyyy/mm/dd.
ACTIVITY 9
Open the database called HomeDecor.
Click on the Database Tools tab and click on the Relationship button.
Create relationships between all four tables using the primary and foreign keys. The
relationships should be as follows:
Looking at the HomeDecor database, we can create a query to list all the sales people with their
clients and the date of their invoices.
ACTIVITY 10
Create the following query to join the tables tblSalesPeople, tblClients and tblInvoices using
WHERE to join the tables.
SELECT SPName, ClientName, InvoiceDate
FROM tblClients, tblInvoices, tblSalesPeople
WHERE [Link] = [Link]
AND [Link] = [Link]
If we were to use INNER JOINS, we will need to create embedded queries where a sub query is used
to join two tables and then the resulting table is joined to the third table.
Adapt the above query to use an embedded query with INNER JOINS check that it returns the
same result as the previous SQL statement.
SELECT SPName, ClientName, InvoiceDate
FROM tblSalesPeople
INNER JOIN
(tblClients
INNER JOIN tblInvoices
ON [Link] = [Link])
ON [Link] = [Link]
The sub query in brackets first combines tblClients and tblInvoices using the link between the
clientID fields producing a new virtual table. This new table is then joined to tblSalesPeople using
the SPID field as a link.
EXERCISE 2
The SQL queries in this exercise contain joins and revise other queries.
Are some of these too difficult for this exercise – should they be placed later?
1. List all the sales people and their clients.
2. Are there any sales people without clients?
3. List all the clients and the items that have ordered.
4. How many clients have ordered ‘interior’ items?
5. What is the average value of each invoice?
6. Which client has spent the most?
7. List all the clients who bought something in July.
8. The ‘Diffusion Lighting System’ is available for exterior as well as interior. Write an INSERT with
a SELECT to add a second ‘Diffusion Lighting System’ with the same values as the first except
for itemCategory which is ‘exterior’.
If you haven't performed any updates (INSERT, UPDATE or DELETE) on this database, then the total
number of items sold should be 642.
We want to display the sales person's name (in tblSalesPeople, field SPName) and the quantity (in
tblInvoices, aggregate field SUM(Quantity)) s/he has sold.
Note: we want to know the total (SUM) of items the sales person has sold, not the number of times
(COUNT) the sales person performed a sale. Also, we want to return results for each sales person,
so a GROUP BY would need to be included.
Some of the query is then easy to code:
SELECT SPName, SUM(Quantity) AS [Total Items Sold]
FROM ???
WHERE ???
GROUP BY SPName
In order to replace the '???' items in the query, we need to establish the relationships between the
tables to avoid incorrect queries and/or queries that have partial or full Cartesian products involved.
Looking at the structures of the relationships between the tables concerned, it is clear that there's no
direct relationship between tblSalesPeople (for SPName) and tblInvoices (for Quantity), so we will
have to make use of any other relationships to get the required Result Set for the query.
The WHERE clause is must specify how to link the tables together by using a compound AND
combination that describes the relationships above.
SELECT SPName, SUM(Quantity) AS [Total Items Sold]
Note the ORDER BY clause is always after the GROUP BY clause. The GROUP BY clause sorts
according to the item it groups. If you want the Result Set in a different order, you will need to add
and ORDER BY clause.
Change the SQL statement to list the top three sales persons and their number of sales.
Add the bold type to the query:
SELECT TOP 3 SPName, SUM(Quantity) AS [Total Items Sold]
FROM tblClients, tblInvoices, tblSalesPeople
WHERE [Link] = [Link]
AND [Link] = [Link]
GROUP BY SPName
ORDER BY SUM(Quantity) DESC
List the sales persons (alphabetically) and number of sales for those sales people who sold 20 or
more items:
Remove the Top 3 part of the SELECT statement and the ORDER BY clause and include the
bold line at the end:
SELECT SPName, SUM(Quantity) AS [Total Items Sold]
FROM tblClients, tblInvoices, tblSalesPeople
WHERE [Link] = [Link]
AND clientID = clientID
GROUP BY SPName
HAVING SUM(Quantity) >= 20
The HAVING clause removes rows that have been produced by the GROUP BY clause. The
HAVING clause is not to be confused with the WHERE clause which remove rows before the GROUP
BY clause is executed.
Give a bonus of R10.00 per item sold to each sales person who sold more than (or equal to) 20 items:
Change the SQL statement again to include a calculated field as part of the SELECT clause.
SELECT SPName, SUM(Quantity)*10 AS Bonus
FROM tblClients, tblInvoices, tblSalesPeople
WHERE [Link] = [Link]
AND clientID = clientID
GROUP BY SPName
HAVING SUM(Quantity) >= 20
ACTIVITY 12
Consider a query that would also involve the missing table tblItems to show all of the sales people's
names and the sales items they sold (itemDescription) and how many of each item was sold by
each sales person'.
Code the query as follows:
SELECT SPName, itemDescription, SUM(Quantity) AS [Total Items Sold]
FROM tblClients, tblInvoices, tblItems, tblSalesPeople
WHERE [Link] = [Link]
AND [Link] = [Link]
AND [Link] = [Link]
GROUP BY SPName, itemDescription
Notice that we have to include the relationship between tblInvoices and tblItems to be able to
access the itemDescription field in tblItems; this means that tblItems must then be included in the
FROM clause and the relationship [Link] = [Link] must be included in the
compound WHERE clause. Also, showing each sales person and each item sold by each sales
person requires the itemDescription field to be added in the GROUP BY clause. Remember that we
can include additional HAVING and ORDER BY clauses after the GROUP BY to enhance the query.
ACTIVITY 13
We will complete this activity by performing a number of analysis steps:
Step 1: Identify the field/s required and the tables containing those fields:
The SELECT clause must contain the required fields; client names from tblClients and the selling
prices of sales items from tblItems. We may require additional information/calculations to determine
how much each client spent, in total, but the selling price of each item purchased will certainly be
required as a starting point. The client's name field is clientName in tblClients and the selling price
field is itemCostPrice in tblItems.
Unfortunately, this query will generate a Cartesian product of all clients (70) and all sales items (22)
(giving a meaningless Result Set of 70 x 22 = 1540 rows) as no criteria have been set for the actual
sales items that were purchased by each client.
Step 2: Identify the required tables according to the primary/foreign key relationships:
We've already identified the tables of the required fields; tblClients and tblItems. These tables are
essential as at least one of their fields is required for the query. But, is there no direct relationship
between the tables, so we need to find a 'path' that links these tables using other table/s that do have
separate primary/foreign key relationships to these tables, otherwise the query can't be performed.
Let's look again at where the relationships do exist:
The rest of this query doesn't relate to the relationships between the tables; those have correctly been
established above and the query will work, but it will show each client multiple times for each sales
item that was purchased. This isn't exactly what the original query statement requires.
Step 3: Analyse the original query statement to determine any further coding (grouping,
calculations and/or ordering) required:
We only want each client mentioned once with his/her total amount spent. This requires a GROUP
BY on the clients' name field and a SUM of the items' sales prices. By only using a GROUP BY
clause you will not see the total of the individual items' sales prices, you will need the aggregate
function (SUM).
Change the query to include the bold code:
SELECT clientName, SUM(itemCostPrice) AS [Total Spend]
FROM tblClients, tblItems, tblInvoices
WHERE [Link] = [Link]
AND [Link] = [Link]
GROUP BY clientName
ACTIVITY 14
Create the following trace query to determine how much Mr. A Moosajee, ID 15, has spent.
SELECT clientName, itemDescription, itemCostPrice AS [Total Spend],
Quantity, itemCostPrice * Quantity AS [Grand Total]
FROM tblClients, tblItems, tblInvoices
WHERE [Link] = [Link]
AND [Link] = [Link]
AND [Link] = 'Mr A Moosajee'
We need to add each value in the Total Spend and the Grand Total fields. We can do this by
exporting the query to Excel and using the SUM function, by adapting the SQL query to add the fields
or by simply using a calculator.
Exporting to Excel:
Make sure the query that produced the above result set is visible.
In MS Access version 2010, click on the External Data tab.
Select the Export to Excel button to generate a spreadsheet using the table.
Open the file in Excel and use the SUM function to add the items in a column.
Alternatively, adapt the query to find the SUM of the itemCostPrice field and the itemCostPrice *
Quantity fields.
Adapt the query to include the SUM function and to Group BY the client's name limited to only
'Mr. A Moosajee' using the HAVING clause.
SELECT clientName, SUM(itemCostPrice ) AS [Total Spend],
SUM(itemCostPrice * Quantity) AS [Grand Total]
FROM tblClients, tblItems, tblInvoices
WHERE [Link] = [Link]
AND [Link] = [Link]
GROUP BY clientName
HAVING [Link] = 'Mr A Moosajee'
You can see that the sum of the values in the Total Spend column amounts to 48807 and the sum of
the values in the Grand Total column amounts to 81879. The original query request stated that the
total amount spent by each client should be indicated in the Result Set, so the original query doesn't
deliver the correct results – it only shows the total spent by each client if the client bought only 1 of
each item. The quantity of each item purchased has been ignored as it wasn't included in any of the
calculations. We need to include a calculation to calculate the product of itemCostPrice and
Quantity and use that result as the parameter to the SUM aggregate function.
Change the SELECT clause of the query as follows:
SELECT clientName, SUM(itemCostPrice * Quantity) AS [Total Spend]
FROM tblClients, tblItems, tblInvoices
WHERE [Link] = [Link]
AND [Link] = [Link]
GROUP BY clientName;
and you will see that Mr A Moosajee (at least) has the correct total of 81879.
If you notice, the name of the field itemCostPrice indicates that it stores the cost of the item and not
the selling price of the item. If each item is increased by 35% to get the selling price then we would
have the correct result.
Change the query to multiply the itemCostPrice field by 1.35 to add 35% to the itemCostPrice.
SELECT clientName, SUM(itemCostPrice * 1.35 * Quantity) AS [Total
Spend]
FROM tblClients, tblItems, tblInvoices
WHERE [Link] = [Link]
AND [Link] = [Link]
GROUP BY clientName;
What if we wanted to list the clients in highest to lowest order of total spent?
Simply add an ORDER BY to the query:
SELECT clientName, SUM(itemCostPrice * 1.35 * Quantity) AS [Total
Spend]
FROM tblClients, tblItems, tblInvoices
WHERE [Link] = [Link]
AND [Link] = [Link]
GROUP BY clientName
ORDER BY SUM(itemCostPrice * Quantity) DESC
Remember in MS Access, if you want to ORDER BY a derived field or aggregate function, you
must include the complete code of the derived field or aggregate function as it was stated in the
SELECT clause (you can't use an alias (AS) reference in an ORDER BY clause).
If we only want results for a total spend of >= R100 000.00?
Add a HAVING clause (because it relates to an aggregate function result):
SELECT clientName, SUM(itemCostPrice * 1.35 * Quantity) AS [Total
Spend]
FROM tblClients, tblItems, tblInvoices
WHERE [Link] = [Link]
AND [Link] = [Link]
GROUP BY clientName
HAVING SUM(itemCostPrice * Quantity) >= 100000
ORDER BY SUM(itemCostPrice * Quantity) DESC
and only for purchases of 'Interior' items?
Add another AND condition to the WHERE clause:
SELECT clientName, SUM(itemCostPrice * Quantity) AS [Total Spend]
FROM tblClients, tblItems, tblInvoices
WHERE clientID = clientID AND itemID = itemID
AND itemCategory = "Interior"
GROUP BY clientName
HAVING SUM(itemCostPrice * Quantity) >= 100000
ORDER BY SUM(itemCostPrice * Quantity) DESC
The itemCategory field can be included in the WHERE clause as it is a field in one of the tables
required for the original query; it is not included in a HAVING clause as it is not an aggregate
function. Interestingly, the query only produces 3 results as there is only 3 people who purchased
interior sales items to a value of >= R100,000.00.
The last 3 examples show that you can make simple additions to queries that produce meaningful and
varied Result Sets; but, you need to follow the analysis steps to get the original query correct first.
EXERCISE 3
so that it produces a Result Set of clients that qualify for a discount. A 10% discount applies to
all total spends of >= R100,000.00. The query must only show the clients' names and the value
of the discount. You should use the ROUND(x,2) function to produce results that have 2
decimal places only for the discounts.
2. Write a query that will show how many of each sales item was sold during the promotion. The
query must have column headings 'Sales Item' and 'Number Sold'. Show the results in rank
order (highest to lowest) of number sold.
3. Adapt the previous query to show only items that are in the 'Interior' category.
4. Write a query that will determine the total profit generated by each sales person. The query
must have columns headings 'Sales Person Name' and 'Total Profit'. Remember, the profit
portion is 35% of the itemCostPrice.
5. Write a query that will determine how many clients each sales person sold to during the sales
promotion. The query must have column headings 'Sales Person' and 'Number of Clients'.
6. Write a query that will display each sales person and each client that the sales person sold to,
together with the number of items that were sold by each sales person to each client and the
total sales value (including 35% profit) of the items sold. The query must have column headings
'Sales Person', 'Client Name', 'Number of Items' and 'Total Value'.
tblAccount tblCustomer
AccNo CustID BranchID Amount CustID CustName AccNo
12345 1 Sandton 5000 1 Modise 12345
63547 3 City 10000 2 Thompson 98765
ACTIVITY 15
Write down
the primary key of tblAccount
the primary key of tblCustomer
the foreign key of tblAccount
What field links the 2 tables?
Load MS Access and create a new database called Bank.
Create a query to create a table using a SQL statement for the table tblAccount.
CREATE TABLE tblAccount
(AccNo CHAR(15),
CustID CHAR(5),
BranchID CHAR(20),
Amount INTEGER,
PRIMARY KEY (AccNo));
The data type for each field must be specified. For String data, use CHAR with the maximum number
of characters in brackets. For numeric data, use INTEGER or DOUBLE. The primary keys are also
specified when the tables are created.
The table should be created and can be viewed in design view. Note that the AccNo field has been
set as the primary key.
Create another query to create the table tblCustomers. The CustID field is set to
AUTOINCREMENT to generate sequential numeric values.
CREATE TABLE tblCustomer
(CustID AUTOINCREMENT,
CustName CHAR(20),
PRIMARY KEY (CustID));
EXERCISE 4
The following exercise is based on the Bank database you have just created.
1. Using the Database Tools relationship function in MS Access, link the tables using the CustNo
field to join the tables. Enforce referential integrity.
2. Exit the Database Tools and save each table using the names given.
3. In MS Access, in the Datasheet view of each table, add the data that is shown in the tables
above. You will have to add the data to tblCustomer first as tblAccount requires a related
customer ID.
4. Save the tables again once the data has been added.
5. Create a SQL statement to display each customer's name together with the name of the branch
their account is at, without using a Join.
6. Change the SQL statement to display each customer's name, together with the amount that
each has in his/her bank account.
7. Change the code to perform an Inner Join on Customer and Account Tables. You should get
the following result:
Modise 5000
Thompson 2500
Naidoo 10000
8. Change the inner join SQL query so that it returns the customer names and the Branch ID, as
well as the amount each customer has.
9. Add customers Adams and Vos to tblCustomer, with CustID's 4 and 5, respectively.
Assume for the purposes of this exercise that the details of Adams and Vos have not yet been
recorded in the table, tblAccount, because they are new bank customers, so do NOT make
any changes to the tblAccount table.
10. Run a left join on the Customer and Account Tables, which should produce:
Modise 2
Thompson 3
Naidoo 1
Adams
Vos
11. Add the following record to tblAccount:
12. Perform a right join on tblCustomer and tblAccount so that ALL the account numbers are
displayed.
13. Create a new table called tblBranch, using SQL statements. This table must contain the fields,
BranchID, BranchName and City. The primary key is BranchID. Use appropriate data types
for each of the fields. Note: The data type for the BranchID must be set to 'AUTOINCREMENT'
so that any inserts to the table must automatically allocate an auto-number value for the primary
key.
14. Check that the table has been created and that it contains no data.
15. Check the fields of each field in design view and that a primary key has been created.
16. Create a SQL statement to insert data into tblBranch. Use an INSERT statement that will
automatically assign a value for the primary key. Insert a record called 'City' located in
Johannesburg.
17. Change the SQL statement to insert more branches.
ACTIVITY 16
Load the Jeppe database.
View the relationships between each of the four tables to remind yourself how the tables are
linked.
EXERCISE 5
Open the Jeppe database and type in SQL statements to do the following:
1. Display the average time per school across all the races.
2. Display a list of all the runners' names who ran the Johnson Crane 25, with their finishing times.
3. Display all runners (and their times) who participated in any Johnson Crane races and have
times that are above average for all races.
4. Display a list of all the runners' names in Andy's school together with the rate that the school
runs at.
5. Display the average distance run per runner (correct to two decimal places).
6. Generate a code for each runner's name using two random digits (0 - 9 incl.), followed by the
second, third and fourth letter of their name, followed by the remainder of his/her age after
dividing by seven.
7. Display the total number of runners in each school only if there are more than 5 runners in the
school.
8. Find the oldest runner to have run 'Springs Striders 32'. Display the name and the time taken.
9. Find all the runners whose ages are 20, 30, 40, 50, 60 years old.
10. Find the runners who have not run any races.
11. Find the number of runners in each school.
12. List all the names of the races without duplicates.