This deck walks through all 25 queries of the CSIS 325 Advanced SQL lab on the AutoRentals database. Each concept is taught right before the query that needs it, with the exact T-SQL to type and the results to expect.
Subject: Advanced SQL (CSIS 325) · 125 slides · code lesson
Open the interactive version of this deck · Homework for this lesson
Title
CSIS 325
AutoRentals database - all 25 queries, one lesson. Type each query, run it in SQL Server, screenshot, paste.
Objectives
This lab has 25 queries against a car-rental database. Work through this deck top to bottom and every answer gets written for you - with the concept behind it.
Matching
Match the pairs
From What you'll be able to do — match each one to what it actually does. The descriptions have been shuffled.
Why: One query at a time, Verified results, Concept first are easy to tell apart while they are sitting next to their descriptions and much harder afterwards, which is what this checks.
Section
Start here
Picture it
Figure (svg): Entity diagram: Customer (key CID) links to Rentals via CID; Rentals links to Rentcost via Make.
Discussion prompt
Read the picture before the words. What is this showing, and what is the one thing it is built to make obvious? Commit to an answer, then read on.
Hint: Name the parts, then say what changes between them — and if nothing changes, say what is being held still.
Answer:
The lab uses one database, AutoRentals, with three related tables. Know the keys and you know how they join.
Concept
The lab uses one database, AutoRentals, with three related tables. Know the keys and you know how they join.
Figure (svg): Entity diagram: Customer (key CID) links to Rentals via CID; Rentals links to Rentcost via Make.
| Table | Primary key | Links to |
|---|---|---|
| Customer | CID | - |
| Rentals | Rtn | CID -> Customer, Make -> Rentcost |
| Rentcost | Make | - |
CID, CName, Age, Resid_City, BirthPlaceRtn, CID, Make, Date_Out, Pickup, Date_returned, Return_cityMake, CostDate_returned and Return_city are NULL (rentals 4 and 8).Comparison
Comparison matrix
From The three tables: refill the Primary key column from what you know. The rest of the table is as it appeared.
| Table | Primary key | Links to |
|---|---|---|
| Customer | CID | - |
| Rentals | Rtn | CID -> Customer, Make -> Rentcost |
| Rentcost | Make | - |
Estimation
Predict first
Before question 2, run the setup script from the instructions once. It creates the database, the three tables, and inserts every row. The first query (Q1) is already answered for you.
Commit before you compute: what does Setup - build & populate the database come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.
Correct: Check the row counts before writing queries.
Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. If a table is empty an INSERT failed - fix it now, or every later result will be wrong.
Worked example
Before question 2, run the setup script from the instructions once. It creates the database, the three tables, and inserts every row. The first query (Q1) is already answered for you.
Run this (plus the INSERTs) in a new query window:
create database AutoRentals
go
use AutoRentals
go
create table Customer
(CID integer, CName varchar(20), Age integer,
Resid_City varchar(20), BirthPlace varchar(20),
constraint PK_Customer primary key (CID))
create table Rentcost
(Make varchar(20), Cost float,
constraint PK_Rentcost primary key (Make))
create table Rentals
(Rtn integer, CID integer, Make varchar(20),
Date_Out smalldatetime, Pickup varchar(20),
Date_returned smalldatetime, Return_city varchar(20),
constraint PK_Rentals primary key (Rtn),
constraint FK_CustomerRentals foreign key (CID) references Customer,
constraint FK_RentCostRentals foreign key (Make) references Rentcost)
-- then run all the INSERT statements from the instructionsAfter it runs you should have these row counts:
| Table | Rows |
|---|---|
| Customer | 7 |
| Rentcost | 5 |
| Rentals | 8 |
Check the row counts before writing queries.
Why: If a table is empty an INSERT failed - fix it now, or every later result will be wrong.
Trade off
Comparison matrix
From Setup - build & populate the database: every row here is a choice with a cost. Fill the Rows column, then say which row you would actually pick and what you give up for it.
| Table | Rows |
|---|---|
| Customer | 7 |
| Rentcost | 5 |
| Rentals | 8 |
Concept
Every one of the 25 questions is delivered the same way. Miss a step and you lose the point even with a correct query.
This deck gives you the exact query and the exact rows to expect for all 25. Q1 below is the worked model.
Counterexample
Discussion prompt
Every one of the 25 questions is delivered the same way. Miss a step and you lose the point even with a correct query.
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Missing information
Discussion prompt
Question 1. Return the name and age of all customers. You should have 7 rows.
What do you need to know — or decide — before the first line can be written? List everything the problem has to hand you.
Hint: Anything you would have to invent to get started is a thing the problem must supply.
Answer:
Q1 is already filled in for you in the Template - it shows the exact format to copy for Q2-Q25.
Worked example
Question 1. Return the name and age of all customers. You should have 7 rows.
Type this query in SQL Server:
select CName, Age
from CustomerRun it. Result (7 rows):
| CName | Age |
|---|---|
| Black | 40 |
| Green | 25 |
| Jones | 30 |
| Martin | 35 |
| Simon | 22 |
| Vernon | 60 |
| Wilson | 25 |
Deliverable: screenshot the query and its results, then paste it below Question 1 in the Template.
Why: Q1 is already filled in for you in the Template - it shows the exact format to copy for Q2-Q25.
Pattern
Step through it
Step through Q1 - The model answer (given) one row at a time. What is driving the change, and what would the row after the last one be?
Section
Questions 2-5
Concept
Every query names the columns you want (SELECT), the table (FROM), and optionally a row filter (WHERE).
WHERE — Keeps only the rows where the condition is true. Text literals go in single quotes: 'Tampa'.
Column and table names aren't case sensitive; string values are compared as stored.
Analogy
Discussion prompt
Explain SELECT ... FROM ... WHERE by analogy to something with no Advanced SQL (CSIS 325) in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
Every query names the columns you want (SELECT), the table (FROM), and optionally a row filter (WHERE).
Pattern
Predict first
The table runs: Black | Erie · Jones | Hemet
In Q2 - Filter with WHERE, given the rows so far: what is the next one — the row where CName is Martin?
Correct: Martin | Hemet
| CName | Resid_City |
|---|---|
| Black | Erie |
| Jones | Hemet |
| Martin | Hemet |
Why: The relationship between the columns, not the individual numbers, is what generates the next row. WHERE decides which rows; SELECT decides which columns.
Worked example
Question 2. Display the name and resid_city of all customers who were born in Tampa. 3 rows.
Filter on BirthPlace, but SELECT the columns asked for.
Why: WHERE decides which rows; SELECT decides which columns. They are independent.
Type this query in SQL Server:
select CName, Resid_City
from Customer
where BirthPlace = 'Tampa'Run it. Result (3 rows):
| CName | Resid_City |
|---|---|
| Black | Erie |
| Jones | Hemet |
| Martin | Hemet |
Deliverable: screenshot the query and its results, then paste it below Question 2 in the Template.
Why: The grader wants to see the query you ran and the rows it returned in the same screenshot.
Error analysis
Annotate
Walk the callouts on Q2 - Filter with WHERE. Each one is a place this is easy to get subtly wrong.
Concept
Three tools you need for Q3 at once:
Age between 25 and 40 keeps 25 and 40.DESC is high-to-low or Z-to-A.CName as [Customer Name].Pattern
Predict first
The table runs: 7 | Wilson · 4 | Martin · 3 | Jones · 2 | Green
In Q3 - BETWEEN, ORDER BY, alias, given the rows so far: what is the next one — the row where CID is 1?
Correct: 1 | Black
| CID | Customer Name |
|---|---|
| 7 | Wilson |
| 4 | Martin |
| 3 | Jones |
| 2 | Green |
| 1 | Black |
Why: The relationship between the columns, not the individual numbers, is what generates the next row. Five customers fall in 25-40 inclusive (Green and Wilson are exactly 25, Black is exactly 40).
Worked example
Question 3. Show CID and name of customers aged 25 to 40 (inclusive). Order by CName descending. Head the name column 'Customer Name'.
Type this query in SQL Server:
select CID, CName as [Customer Name]
from Customer
where Age between 25 and 40
order by CName descRun it. Result (5 rows):
| CID | Customer Name |
|---|---|
| 7 | Wilson |
| 4 | Martin |
| 3 | Jones |
| 2 | Green |
| 1 | Black |
Deliverable: screenshot the query and its results, then paste it below Question 3 in the Template.
Why: Five customers fall in 25-40 inclusive (Green and Wilson are exactly 25, Black is exactly 40).
Pattern
Step through it
Step through Q3 - BETWEEN, ORDER BY, alias one row at a time. What is driving the change, and what would the row after the last one be?
Anomaly
Predict first
A student writes this, and it looks reasonable:
Task says 25 to 40 inclusive.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: This silently drops Green (25), Wilson (25), and Black (40) - you'd get 2 rows, not 5.
Task says 25 to 40 inclusive.
Why: This silently drops Green (25), Wilson (25), and Black (40) - you'd get 2 rows, not 5.
Trap
Task says 25 to 40 inclusive.
where Age > 25 and Age < 40
Uses strict > and <
Why: This silently drops Green (25), Wilson (25), and Black (40) - you'd get 2 rows, not 5.
Task says 25 to 40 inclusive.
where Age between 25 and 40
BETWEEN includes both ends
Why: 25 and 40 are kept. Five rows, exactly as required.
Worked example
Question 4. List any customers whose names begin with the letter 'G'. 1 row.
LIKE 'G%' means starts-with-G.
Why: % matches any run of characters; 'G%' anchors G at the start. * selects every column.
Type this query in SQL Server:
select *
from Customer
where CName like 'G%'Run it. Result (1 row):
| CID | CName | Age | Resid_City | BirthPlace |
|---|---|---|---|---|
| 2 | Green | 25 | Cary | Erie |
Deliverable: screenshot the query and its results, then paste it below Question 4 in the Template.
Why: The grader wants to see the query you ran and the rows it returned in the same screenshot.
Reverse engineer
Discussion prompt
Work backwards. The example finished here:
Deliverable: screenshot the query and its results, then paste it below Question 4 in the Template.
What was it asked to do, and what must it have been given? Reconstruct the problem from its answer.
Hint: Every quantity in the result had to enter somewhere. Account for each one.
Answer:
Question 4. List any customers whose names begin with the letter 'G'. 1 row.
Concept
A make can be rented many times, so the Rentals table repeats makes. DISTINCT collapses identical output rows to one each.
DISTINCT — Applied to the whole SELECT list; keeps one copy of each unique combination of the selected columns.
Definition probe
Sort into buckets
Every line below is part of the definition of WHERE or of DISTINCT — one or the other, never both. Put each where it belongs.
'Tampa'.Worked example
Question 5. Display a unique list of all automobile makes that have ever been rented. Order by Make. 3 rows.
Type this query in SQL Server:
select distinct Make
from Rentals
order by MakeRun it. Result (3 rows):
| Make |
|---|
| Ford |
| GM |
| Nissan |
Deliverable: screenshot the query and its results, then paste it below Question 5 in the Template.
Why: Only Ford, GM, and Nissan appear in Rentals - Toyota and Volvo have never been rented (that comes back in Q17-18).
Ranking
Put in order
These are the steps of Pattern: the shape of every SELECT, scrambled. Put them back in order before the next slide shows you.
SELECT the columns (add DISTINCT to dedupe, AS to rename)FROM the tableWHERE the row condition (=, BETWEEN, LIKE, ...)ORDER BY column(s) (ASC default, DESC to reverse)Why: This is the order the recipe itself gives. Recalling the sequence without the slide in front of you is the difference between recognising the method and being able to run it — most of what goes wrong in practice is a step done out of turn.
Pattern
Every single-table query you just wrote is the same skeleton in a fixed order:
SELECT the columns (add DISTINCT to dedupe, AS to rename)FROM the tableWHERE the row condition (=, BETWEEN, LIKE, ...)ORDER BY column(s) (ASC default, DESC to reverse)SQL Server always runs them in that written order. Get the skeleton right and only the middle changes per question.
Elimination
Eliminate the wrong options
Which WHERE clause returns customers aged 25 through 40, including 25 and 40?
3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.
Survives elimination: A
Why: BETWEEN is inclusive of both endpoints, so ages 25 and 40 are kept along with everything in between - exactly what 'inclusive' asks for.
Check
Solve it in your head first, then click.
Check your understanding
Which WHERE clause returns customers aged 25 through 40, including 25 and 40?
Answer: A
Why: BETWEEN is inclusive of both endpoints, so ages 25 and 40 are kept along with everything in between - exactly what 'inclusive' asks for.
Section
Questions 6-9
Concept
When the columns you need live in two tables, join them on the key they share. Customer.CID matches Rentals.CID.
Figure (svg): Venn diagram of Customer and Rentals with only the overlap shaded, labeled rows kept by inner join.
INNER JOIN ... ON — Returns only rows where the ON condition matches in both tables. Unmatched rows are dropped.
Give each table a short alias (Customer c, Rentals r) so you can write c.CName, r.Make.
Explain it
Discussion prompt
Explain INNER JOIN: match rows across tables to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
When the columns you need live in two tables, join them on the key they share. Customer.CID matches Rentals.CID.
Intuition
Think of each Rentals row reaching over to grab the one Customer row with the same CID, then laying the two side by side as a single wider row.
A customer with three rentals produces three joined rows; a customer with none produces zero (that's what INNER means).
Ranking
Put in order
Put the moves of Q6 - Two-table INNER JOIN into the order they have to happen.
Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. Every rental stores the CID of the customer who made it, so matching Customer.CID to Rentals.CID places each person beside their own rentals.
Worked example
Question 6. Display every customer who has ever rented a car: customer name, make, and pickup location. Order by pickup ascending. 8 rows.
Start from Customer and join Rentals on the shared key CID
Why: Every rental stores the CID of the customer who made it, so matching Customer.CID to Rentals.CID places each person beside their own rentals.
Select the three requested columns: CName, Make, and Pickup
Why: Only the customer name, the car make, and the pickup city were asked for, so those are the only columns the SELECT list needs.
Add ORDER BY r.Pickup to sort the output ascending
Why: Ascending order by pickup city groups the Cary rows first, then Erie, then Tampa, matching the required arrangement.
Type this query in SQL Server:
select c.CName, r.Make, r.Pickup
from Customer c
join Rentals r on c.CID = r.CID
order by r.PickupRun it. Result (8 rows):
| CName | Make | Pickup |
|---|---|---|
| Black | Ford | Cary |
| Jones | Ford | Cary |
| Martin | Ford | Cary |
| Black | Ford | Erie |
| Jones | GM | Erie |
| Simon | GM | Erie |
| Black | GM | Tampa |
| Green | Nissan | Tampa |
Deliverable: screenshot the query and its results, then paste it below Question 6 in the Template.
Why: 8 rentals means 8 rows - Black appears 3 times because Black has 3 rentals.
Comparison
Comparison matrix
From Q6 - Two-table INNER JOIN: refill the Pickup column from what you know. The rest of the table is as it appeared.
| CName | Make | Pickup |
|---|---|---|
| Black | Ford | Cary |
| Jones | Ford | Cary |
| Martin | Ford | Cary |
| Black | Ford | Erie |
| Jones | GM | Erie |
| Simon | GM | Erie |
| Black | GM | Tampa |
| Green | Nissan | Tampa |
Anomaly
Predict first
A student writes this, and it looks reasonable:
Listing tables with no match condition:
It is wrong. Say what breaks — and say it before you turn the page.
Correct: 7 customers x 8 rentals = 56 meaningless rows.
Always state how the tables match:
Why: 7 customers x 8 rentals = 56 meaningless rows. This is a Cartesian product.
Trap
Listing tables with no match condition:
from Customer c, Rentals r (no ON / no WHERE)
Pairs every customer with every rental
Why: 7 customers x 8 rentals = 56 meaningless rows. This is a Cartesian product.
Always state how the tables match:
from Customer c join Rentals r on c.CID = r.CID
ON ties each rental to its own customer
Why: You get 8 correct rows - one per rental.
Worked example
Question 7. Return the names of customers who rented a Ford or GM. 4 rows.
DISTINCT matters here.
Why: Black rented Ford twice and GM once. Without DISTINCT you'd get duplicate 'Black' rows and more than 4 rows.
Type this query in SQL Server:
select distinct c.CName
from Customer c
join Rentals r on c.CID = r.CID
where r.Make in ('Ford', 'GM')Run it. Result (4 rows):
| CName |
|---|
| Black |
| Jones |
| Martin |
| Simon |
Deliverable: screenshot the query and its results, then paste it below Question 7 in the Template.
Why: The grader wants to see the query you ran and the rows it returned in the same screenshot.
Blank canvas
Draw it
Draw what Q7 - JOIN + IN list just did — the shape of it, not the line-by-line working. One picture, labels only where you need them. Then check it against the steps: anything you could not draw is a step you followed rather than understood.
Concept
Chain another JOIN to reach Rentcost. Rentals.Make matches Rentcost.Make, which carries the daily Cost.
Each additional table adds one more join ... on ... clause. Order of the joins doesn't change the result for inner joins.
Step zero
Discussion prompt
Q8 - Three-table JOIN — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: Join Customer to Rentals on CID, then Rentals to Rentcost on Make
Answer:
Worked example
Question 8. Display customer names, ages, makes, and the daily cost of each automobile they rented. Order by cost. 8 rows.
Join Customer to Rentals on CID, then Rentals to Rentcost on Make
Why: Each daily rate lives in Rentcost, reached through the Make that every rental records, so a second join chains the third table in.
Select CName, Age, Make, and Cost across the joined tables
Why: These four requested fields come from three different tables, which is exactly why all three must be joined before selecting.
Order the whole result by rc.Cost
Why: Sorting by the daily rate lists the cheaper makes before the more expensive ones, as the question requires.
Type this query in SQL Server:
select c.CName, c.Age, r.Make, rc.Cost
from Customer c
join Rentals r on c.CID = r.CID
join Rentcost rc on r.Make = rc.Make
order by rc.CostRun it. Result (8 rows):
| CName | Age | Make | Cost |
|---|---|---|---|
| Black | 40 | Ford | 30 |
| Black | 40 | Ford | 30 |
| Green | 25 | Nissan | 30 |
| Jones | 30 | Ford | 30 |
| Martin | 35 | Ford | 30 |
| Black | 40 | GM | 40 |
| Jones | 30 | GM | 40 |
| Simon | 22 | GM | 40 |
Deliverable: screenshot the query and its results, then paste it below Question 8 in the Template.
Why: Cost comes from Rentcost, joined through Rentals.Make. The $30 makes (Ford, Nissan) sort before the $40 make (GM).
Pattern
Step through it
Step through Q8 - Three-table JOIN one row at a time. What is driving the change, and what would the row after the last one be?
Worked example
Question 9. Return the unique list of birth places of everyone who has ever rented a Ford. 1 row: Tampa.
Type this query in SQL Server:
select distinct c.BirthPlace
from Customer c
join Rentals r on c.CID = r.CID
where r.Make = 'Ford'Run it. Result (1 row):
| BirthPlace |
|---|
| Tampa |
Deliverable: screenshot the query and its results, then paste it below Question 9 in the Template.
Why: Every Ford renter (Black, Jones, Martin) was born in Tampa, so the unique list is a single row.
Prediction
Predict first
In 'Customer c join Rentals r on c.CID = r.CID', what happens to a customer who has no rentals?
Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.
Correct: They are left out of the result entirely
Why: An INNER JOIN keeps only rows that match on both sides. A customer with no matching Rentals row has nothing to pair with, so they are excluded - which is why Q6 returns 8 rows, not 10.
Check
Solve it in your head first, then click.
Check your understanding
In 'Customer c join Rentals r on c.CID = r.CID', what happens to a customer who has no rentals?
Answer: A
Why: An INNER JOIN keeps only rows that match on both sides. A customer with no matching Rentals row has nothing to pair with, so they are excluded - which is why Q6 returns 8 rows, not 10.
Section
Questions 10-15
Concept
Date_Out is a date. YEAR(Date_Out) returns just the year as a number, so YEAR(Date_Out) = 2009 keeps 2009 rentals.
YEAR(date) — A T-SQL function returning the 4-digit year. DATEPART(year, date) does the same thing.
Worked example
Question 10. Names and ages of customers who rented any automobile during 2009. List each customer only once. 2 rows.
DISTINCT enforces 'only once'.
Why: Black has two 2009 rentals; DISTINCT collapses them so Black is listed a single time.
Type this query in SQL Server:
select distinct c.CName, c.Age
from Customer c
join Rentals r on c.CID = r.CID
where year(r.Date_Out) = 2009Run it. Result (2 rows):
| CName | Age |
|---|---|
| Black | 40 |
| Jones | 30 |
Deliverable: screenshot the query and its results, then paste it below Question 10 in the Template.
Why: The grader wants to see the query you ran and the rows it returned in the same screenshot.
Concept
DATEDIFF(day, start, end) returns the whole number of days from start to end. Nov 1 to Nov 5 is 4.
DATEDIFF(day, d1, d2) — Counts day boundaries crossed from d1 to d2. The unit 'day' can also be month, year, etc.
A rental that's not back yet has a NULL Date_returned, and DATEDIFF of a NULL is NULL - it simply won't count.
Matching
Match the pairs
Match each term to the definition this lesson gave it — not the one you would guess from the word.
'Tampa'.Why: These are the working definitions of WHERE, DISTINCT, INNER JOIN ... ON, YEAR(date), DATEDIFF(day, d1, d2) as Advanced SQL Queries Lab (AutoRentals) uses them. Pairing them correctly is the test of whether you could state each one with the slide switched off.
Estimation
Predict first
Question 11. Total number of days Black rented a GM on November 1, 2009. One cell: 4.
Commit before you compute: what does Q11 - DATEDIFF for a rental length come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.
Correct: Deliverable: screenshot the query and its results, then paste it below Question 11 in the Template.
Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. Nov 1 out, Nov 5 back: DATEDIFF(day, ...) = 4.
Worked example
Question 11. Total number of days Black rented a GM on November 1, 2009. One cell: 4.
Type this query in SQL Server:
select datediff(day, r.Date_Out, r.Date_returned)
from Rentals r
join Customer c on r.CID = c.CID
where c.CName = 'Black'
and r.Make = 'GM'
and r.Date_Out = '11/1/2009'Run it. Result (1 row):
| Days |
|---|
| 4 |
Deliverable: screenshot the query and its results, then paste it below Question 11 in the Template.
Why: Nov 1 out, Nov 5 back: DATEDIFF(day, ...) = 4.
Worked example
Question 12. Total cost of the automobile rented by Black on November 1, 2009. One cell: 160.
Cost = days x daily rate.
Why: 4 days x $40/day for a GM = $160. The join to Rentcost supplies the $40 rate.
Type this query in SQL Server:
select datediff(day, r.Date_Out, r.Date_returned) * rc.Cost
from Rentals r
join Customer c on r.CID = c.CID
join Rentcost rc on r.Make = rc.Make
where c.CName = 'Black'
and r.Make = 'GM'
and r.Date_Out = '11/1/2009'Run it. Result (1 row):
| TotalCost |
|---|
| 160 |
Deliverable: screenshot the query and its results, then paste it below Question 12 in the Template.
Why: The grader wants to see the query you ran and the rows it returned in the same screenshot.
Cost model
Annotate
In Q12 - DATEDIFF x Cost, before reading the notes: mark where the time actually goes. Which line dominates?
Concept
SUM(expr) totals a value over all matching rows. To total every rental's cost, sum days x rate across the whole table.
Unreturned rentals contribute NULL, and SUM ignores NULLs - so they drop out automatically.
Step zero
Discussion prompt
Q13 - SUM over all rows — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: Join Rentals to Rentcost so each rental knows its daily rate
Answer:
Worked example
Question 13. Total cost of all the automobiles that have ever been rented. One cell: 1880.
Join Rentals to Rentcost so each rental knows its daily rate
Why: Total cost needs both the length of each rental and the rate for its make, and that rate comes from Rentcost through Make.
Multiply DATEDIFF(day, Date_Out, Date_returned) by Cost per rental
Why: Days rented times the daily rate gives the cost of one rental, the per-row quantity that then gets totaled.
Wrap the whole expression in SUM to total every rental
Why: SUM adds the per-rental costs into one grand total and quietly skips the NULLs produced by rentals not yet returned.
Type this query in SQL Server:
select sum(datediff(day, r.Date_Out, r.Date_returned) * rc.Cost)
from Rentals r
join Rentcost rc on r.Make = rc.MakeRun it. Result (1 row):
| GrandTotal |
|---|
| 1880 |
Deliverable: screenshot the query and its results, then paste it below Question 13 in the Template.
Why: The two unreturned rentals (4 and 8) produce NULL and are skipped by SUM, so only completed rentals are totaled.
Concept
GROUP BY col splits rows into groups and applies the aggregate (AVG, SUM, COUNT) to each group separately.
GROUP BY — Every column in the SELECT that isn't inside an aggregate must appear in GROUP BY.
Worked example
Question 14. Average number of days automobiles are rented, broken out by make. Exclude cars not yet returned. Ford 13, GM 4.
Filter out unreturned rentals first, then group.
Why: Date_returned is not null drops the open rentals; GROUP BY Make averages each make on its own.
Type this query in SQL Server:
select r.Make, avg(datediff(day, r.Date_Out, r.Date_returned)) as AvgDays
from Rentals r
where r.Date_returned is not null
group by r.MakeRun it. Result (2 rows):
| Make | AvgDays |
|---|---|
| Ford | 13 |
| GM | 4 |
Deliverable: screenshot the query and its results, then paste it below Question 14 in the Template.
Why: The grader wants to see the query you ran and the rows it returned in the same screenshot.
Anomaly
Predict first
A student writes this, and it looks reasonable:
Average age per city, Cary has 25 and 60:
It is wrong. Say what breaks — and say it before you turn the page.
Correct: (25+60)/2 is computed as integers and truncated to 42 - the .5 is lost.
avg(cast(Age as float))
Why: (25+60)/2 is computed as integers and truncated to 42 - the .5 is lost.
Trap
Average age per city, Cary has 25 and 60:
avg(Age) with Age stored as an integer
SQL Server does integer division
Why: (25+60)/2 is computed as integers and truncated to 42 - the .5 is lost.
Force decimal math:
avg(cast(Age as float))
CAST to float keeps the fraction
Why: (25.0+60.0)/2 = 42.5, exactly as the task requires.
Break the constraint
Discussion prompt
The rule this trap just fixed:
(25.0+60.0)/2 = 42.5, exactly as the task requires.
Now break it on purpose. Build a case that violates it and follow the consequences until something visibly fails. Where does the failure first show up — and would you have noticed it if you had not been looking?
Hint: The dangerous rules are the ones whose violation still produces an answer. If yours fails loudly, try to find one that fails quietly.
Answer:
(25+60)/2 is computed as integers and truncated to 42 - the .5 is lost.
Hypothesis
Predict first
Q15 - AVG without truncation (CAST) is about to be worked. State your hypothesis first: which rule or definition decides this one, and what is the first move it forces? Then watch whether the example agrees with you.
Correct: Group the customers by Resid_City
Why: Grouping by residence city produces one output row per city so each city receives its own average age.
A hypothesis you wrote down is falsifiable; a vague sense of how it will go is not. If the example opens somewhere else, that gap is the thing worth chasing.
Worked example
Question 15. Average age of customers broken out by city of residence. Do not truncate to an integer. 4 rows.
Group the customers by Resid_City
Why: Grouping by residence city produces one output row per city so each city receives its own average age.
Cast Age to float before averaging it
Why: Age is an integer column, and averaging integers truncates, so casting to float keeps the .5 in results such as 42.5.
Select Resid_City alongside the averaged age
Why: Listing the grouping column next to its aggregate labels each average with the city it belongs to.
Type this query in SQL Server:
select Resid_City, avg(cast(Age as float)) as AvgAge
from Customer
group by Resid_CityRun it. Result (4 rows):
| Resid_City | AvgAge |
|---|---|
| Cary | 42.5 |
| Denver | 25 |
| Erie | 31 |
| Hemet | 32.5 |
Deliverable: screenshot the query and its results, then paste it below Question 15 in the Template.
Why: CAST to float is what keeps Cary at 42.5 and Hemet at 32.5 instead of truncating to 42 and 32.
Trade off
Comparison matrix
From Q15 - AVG without truncation (CAST): every row here is a choice with a cost. Fill the AvgAge column, then say which row you would actually pick and what you give up for it.
| Resid_City | AvgAge |
|---|---|
| Cary | 42.5 |
| Denver | 25 |
| Erie | 31 |
| Hemet | 32.5 |
Check
Solve it in your head first, then click.
Check your understanding
Age is an integer column. Which expression averages ages as 42.5 rather than 42?
Answer: A
Why: Casting Age to float before averaging makes SQL Server use decimal arithmetic, so (25+60)/2 evaluates to 42.5. Casting inside the AVG is what preserves the fraction.
Section
Questions 16, 21, 22
Concept
A WHERE condition can compare two columns, not just a column to a literal. Resid_City = BirthPlace is true when a customer's two cities match.
This is still a row filter - it just tests one column against another within each row.
Worked example
Question 16. List customers who reside in the same city in which they were born. 2 rows: Simon and Vernon.
Type this query in SQL Server:
select CName
from Customer
where Resid_City = BirthPlaceRun it. Result (2 rows):
| CName |
|---|
| Simon |
| Vernon |
Deliverable: screenshot the query and its results, then paste it below Question 16 in the Template.
Why: Only Simon (Erie/Erie) and Vernon (Cary/Cary) live where they were born.
Worked example
Question 21. List customers who picked up their rental from the same city in which they reside. 2 rows: Black and Simon.
The two columns now live in different tables.
Why: Pickup is in Rentals, Resid_City in Customer, so you join first, then compare r.Pickup = c.Resid_City.
Type this query in SQL Server:
select distinct c.CName
from Customer c
join Rentals r on c.CID = r.CID
where r.Pickup = c.Resid_CityRun it. Result (2 rows):
| CName |
|---|
| Black |
| Simon |
Deliverable: screenshot the query and its results, then paste it below Question 21 in the Template.
Why: The grader wants to see the query you ran and the rows it returned in the same screenshot.
Concept
A car not yet returned has Date_returned = NULL. You cannot test NULL with =; you must use IS NULL.
IS NULL / IS NOT NULL — The only correct way to test for a missing value. x = NULL is never true, not even when x is NULL.
Anomaly
Predict first
A student writes this, and it looks reasonable:
Find rentals not yet returned:
It is wrong. Say what breaks — and say it before you turn the page.
Correct: NULL is 'unknown', so = NULL is never true.
Find rentals not yet returned:
Why: NULL is 'unknown', so = NULL is never true. This returns zero rows every time.
Trap
Find rentals not yet returned:
where Date_returned = NULL
Compares with =
Why: NULL is 'unknown', so = NULL is never true. This returns zero rows every time.
Find rentals not yet returned:
where Date_returned is null
Uses IS NULL
Why: This is the only test that matches missing values. Returns the open rentals.
Worked example
Question 22. List customers who have not returned their rentals. 2 rows: Green and Simon.
Type this query in SQL Server:
select distinct c.CName
from Customer c
join Rentals r on c.CID = r.CID
where r.Date_returned is nullRun it. Result (2 rows):
| CName |
|---|
| Green |
| Simon |
Deliverable: screenshot the query and its results, then paste it below Question 22 in the Template.
Why: Rentals 4 (Green) and 8 (Simon) have a NULL Date_returned - those are the open rentals.
Commit first
Predict first
Which WHERE clause correctly finds rentals that have not been returned (Date_returned is missing)?
Commit to an answer, then rate it — certain, fairly sure, or guessing — and write the rating down before you turn the page.
Correct: where Date_returned is null
Why: IS NULL is the only operator that matches a missing value. A NULL means 'unknown', so any comparison with = or <> against it evaluates to unknown, never true.
The rating matters as much as the answer: confident-and-wrong is the combination that survives revision, because nothing about it feels like it needs revisiting.
Check
Solve it in your head first, then click.
Check your understanding
Which WHERE clause correctly finds rentals that have not been returned (Date_returned is missing)?
Answer: A
Why: IS NULL is the only operator that matches a missing value. A NULL means 'unknown', so any comparison with = or <> against it evaluates to unknown, never true.
Section
The 'never' problems: Q17-20, 23-24
Picture it
Figure (svg): Venn diagram with the entire left circle shaded, showing a left outer join keeps all left rows including unmatched.
Discussion prompt
Read the picture before the words. What is this showing, and what is the one thing it is built to make obvious? Commit to an answer, then read on.
Hint: Name the parts, then say what changes between them — and if nothing changes, say what is being held still.
Answer:
An INNER JOIN drops rows with no match. An OUTER JOIN keeps them and fills the other table's columns with NULL.
Concept
An INNER JOIN drops rows with no match. An OUTER JOIN keeps them and fills the other table's columns with NULL.
Figure (svg): Venn diagram with the entire left circle shaded, showing a left outer join keeps all left rows including unmatched.
Intuition
To find things that NEVER matched: outer-join the two tables, then keep only the rows where the other side came back NULL.
'Makes never rented' = start from Rentcost, LEFT JOIN Rentals, keep rows where the Rentals side is NULL. No rental ever matched, so the join left it blank.
Worked example
Question 17. Using a LEFT OUTER JOIN, display a unique list of makes that have never been rented. 2 rows: Toyota and Volvo.
Rentcost on the left, keep the NULL matches.
Why: Toyota and Volvo have no matching Rentals row, so r.Make is NULL for them - that's the filter.
Type this query in SQL Server:
select distinct rc.Make
from Rentcost rc
left outer join Rentals r on rc.Make = r.Make
where r.Make is nullRun it. Result (2 rows):
| Make |
|---|
| Toyota |
| Volvo |
Deliverable: screenshot the query and its results, then paste it below Question 17 in the Template.
Why: The grader wants to see the query you ran and the rows it returned in the same screenshot.
Concept
A Type I subquery runs once, on its own, and hands a list back to the outer query. The classic form is WHERE col NOT IN (subquery).
Type I subquery — Independent of the outer query - you could run it by itself. Compared with IN / NOT IN.
Explain it
Discussion prompt
Explain Type I subquery: uncorrelated (NOT IN) to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
A Type I subquery runs once, on its own, and hands a list back to the outer query. The classic form is WHERE col NOT IN (subquery).
Missing information
Discussion prompt
Question 18. Using a Type I query, display a unique list of makes that have never been rented. 2 rows: Toyota and Volvo.
What do you need to know — or decide — before the first line can be written? List everything the problem has to hand you.
Hint: Anything you would have to invent to get started is a thing the problem must supply.
Answer:
Same answer as Q17, different tool: the subquery lists rented makes; NOT IN keeps the makes absent from that list.
Worked example
Question 18. Using a Type I query, display a unique list of makes that have never been rented. 2 rows: Toyota and Volvo.
Type this query in SQL Server:
select Make
from Rentcost
where Make not in (select Make from Rentals)Run it. Result (2 rows):
| Make |
|---|
| Toyota |
| Volvo |
Deliverable: screenshot the query and its results, then paste it below Question 18 in the Template.
Why: Same answer as Q17, different tool: the subquery lists rented makes; NOT IN keeps the makes absent from that list.
Ranking
Put in order
Put the moves of Q19 - RIGHT OUTER JOIN anti-join into the order they have to happen.
Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. Keeping every customer means a customer with no rental still appears, with the Rentals side of the row left blank.
Worked example
Question 19. Using a RIGHT OUTER JOIN, display a unique list of customers who have never rented an automobile. 2 rows: Vernon and Wilson.
Put Customer on the right and RIGHT OUTER JOIN Rentals to it
Why: Keeping every customer means a customer with no rental still appears, with the Rentals side of the row left blank.
Keep only the rows where r.CID IS NULL
Why: A NULL on the Rentals side marks a customer that no rental ever referenced, which is exactly what 'never rented' means.
Select the distinct customer names that remain
Why: The names that survive the filter are Vernon and Wilson, the two customers absent from every rental.
Customer is on the right, so RIGHT keeps every customer.
Why: Vernon and Wilson have no Rentals row, so r.CID is NULL for them.
Type this query in SQL Server:
select distinct c.CName
from Rentals r
right outer join Customer c on r.CID = c.CID
where r.CID is nullRun it. Result (2 rows):
| CName |
|---|
| Vernon |
| Wilson |
Deliverable: screenshot the query and its results, then paste it below Question 19 in the Template.
Why: The grader wants to see the query you ran and the rows it returned in the same screenshot.
Concept
A Type II subquery references the outer row and runs once per outer row. WHERE NOT EXISTS (select ... where r.CID = c.CID) asks 'does this customer have any rental?'
Type II subquery — Correlated: it names an outer column (like c.CID), so it cannot run on its own. Compared with EXISTS / NOT EXISTS.
Analogy
Discussion prompt
Explain Type II subquery: correlated (NOT EXISTS) by analogy to something with no Advanced SQL (CSIS 325) in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
A Type II subquery references the outer row and runs once per outer row. WHERE NOT EXISTS (select ... where r.CID = c.CID) asks 'does this customer have any rental?'
Step zero
Discussion prompt
Q20 - Type II: NOT EXISTS — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: Test each customer with a correlated NOT EXISTS subquery
Answer:
Worked example
Question 20. Using a Type II query, display a unique list of customers who have never rented an automobile. 2 rows: Vernon and Wilson.
Test each customer with a correlated NOT EXISTS subquery
Why: NOT EXISTS asks, for this one customer, whether any rental row anywhere references their CID.
Point the subquery at Rentals where r.CID equals the outer c.CID
Why: Referencing the outer customer's CID is what makes the subquery correlated, and therefore a Type II query.
Keep customers for whom the subquery finds nothing
Why: When no rental exists for a customer the NOT EXISTS test is true, returning Vernon and Wilson.
Type this query in SQL Server:
select CName
from Customer c
where not exists
(select * from Rentals r where r.CID = c.CID)Run it. Result (2 rows):
| CName |
|---|
| Vernon |
| Wilson |
Deliverable: screenshot the query and its results, then paste it below Question 20 in the Template.
Why: Same answer as Q19. NOT EXISTS is true for a customer only when the inner query finds no rental for them.
Blank canvas
Draw it
Draw what Q20 - Type II: NOT EXISTS just did — the shape of it, not the line-by-line working. One picture, labels only where you need them. Then check it against the steps: anything you could not draw is a step you followed rather than understood.
Anomaly
Predict first
A student writes this, and it looks reasonable:
If the subquery's column can contain NULL:
It is wrong. Say what breaks — and say it before you turn the page.
Correct: If any subquery CID were NULL, NOT IN returns no rows at all - a silent wrong answer.
Correlated existence test is NULL-safe:
Why: If any subquery CID were NULL, NOT IN returns no rows at all - a silent wrong answer.
Trap
If the subquery's column can contain NULL:
where CID not in (select CID from Rentals)
A NULL in the list breaks NOT IN
Why: If any subquery CID were NULL, NOT IN returns no rows at all - a silent wrong answer.
Correlated existence test is NULL-safe:
where not exists (select * from Rentals r where r.CID = c.CID)
NOT EXISTS just checks for a matching row
Why: It is unaffected by NULLs in the subquery, so it stays correct.
Two truths and a lie
Sort into buckets
Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.
Estimation
Predict first
Question 23. Using a Type I query, show names and ages of customers who rented an automobile and returned it to Erie. Sort by name ascending. 3 rows.
Commit before you compute: what does Q23 - Type I with IN come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.
Correct: Deliverable: screenshot the query and its results, then paste it below Question 23 in the Template.
Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. The grader wants to see the query you ran and the rows it returned in the same screenshot.
Worked example
Question 23. Using a Type I query, show names and ages of customers who rented an automobile and returned it to Erie. Sort by name ascending. 3 rows.
The subquery lists CIDs that returned to Erie; IN keeps those customers.
Why: It runs once, independently - the mark of a Type I (uncorrelated) query.
Type this query in SQL Server:
select CName, Age
from Customer
where CID in (select CID from Rentals where Return_city = 'Erie')
order by CNameRun it. Result (3 rows):
| CName | Age |
|---|---|
| Black | 40 |
| Jones | 30 |
| Martin | 35 |
Deliverable: screenshot the query and its results, then paste it below Question 23 in the Template.
Why: The grader wants to see the query you ran and the rows it returned in the same screenshot.
Comparison
Comparison matrix
From Q23 - Type I with IN: refill the Age column from what you know. The rest of the table is as it appeared.
| CName | Age |
|---|---|
| Black | 40 |
| Jones | 30 |
| Martin | 35 |
Step zero
Discussion prompt
Q24 - Type II with EXISTS — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: Test each customer with a correlated EXISTS subquery
Answer:
Worked example
Question 24. Using a Type II query, show customers who rented an automobile and picked it up in Cary. 3 rows: Black, Jones, Martin.
Test each customer with a correlated EXISTS subquery
Why: EXISTS asks, for this one customer, whether any of their rentals was picked up in Cary.
Reference the outer c.CID inside the subquery on Rentals
Why: Naming the outer customer's CID makes the subquery correlated, which is the defining trait of a Type II query.
Keep customers for whom a Cary pickup exists
Why: As soon as one matching rental is found the EXISTS test is true and the customer is returned - Black, Jones, Martin.
Type this query in SQL Server:
select CName
from Customer c
where exists
(select * from Rentals r
where r.CID = c.CID and r.Pickup = 'Cary')Run it. Result (3 rows):
| CName |
|---|
| Black |
| Jones |
| Martin |
Deliverable: screenshot the query and its results, then paste it below Question 24 in the Template.
Why: EXISTS is true as soon as the customer has at least one Cary pickup - correlated on c.CID, so it's Type II.
Reverse engineer
Discussion prompt
Work backwards. The example finished here:
Deliverable: screenshot the query and its results, then paste it below Question 24 in the Template.
What was it asked to do, and what must it have been given? Reconstruct the problem from its answer.
Hint: Every quantity in the result had to enter somewhere. Account for each one.
Answer:
Question 24. Using a Type II query, show customers who rented an automobile and picked it up in Cary. 3 rows: Black, Jones, Martin.
Prediction
Predict first
What makes a subquery 'Type II' (correlated) rather than 'Type I'?
Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.
Correct: It references a column from the outer query, so it runs once per outer row
Why: A Type II (correlated) subquery names an outer column - such as c.CID - so it cannot run alone and is re-evaluated for each outer row. A Type I subquery is independent and runs once.
Check
Solve it in your head first, then click.
Check your understanding
What makes a subquery 'Type II' (correlated) rather than 'Type I'?
Answer: A
Why: A Type II (correlated) subquery names an outer column - such as c.CID - so it cannot run alone and is re-evaluated for each outer row. A Type I subquery is independent and runs once.
Section
Question 25
Concept
A FULL OUTER JOIN keeps every row from both tables: matched rows join up, and unmatched rows from either side come back with NULLs.
For Q25 that means all 8 rentals, plus the 2 customers with no rental (Vernon, Wilson), plus the 2 makes never rented (Toyota, Volvo) = 12 rows.
'Each field only once' means show Make a single time even though it's in both Rentals and Rentcost.
Counterexample
Discussion prompt
A FULL OUTER JOIN keeps every row from both tables: matched rows join up, and unmatched rows from either side come back with NULLs.
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Answer:
For Q25 that means all 8 rentals, plus the 2 customers with no rental (Vernon, Wilson), plus the 2 makes never rented (Toyota, Volvo) = 12 rows.
Ranking
Put in order
Put the moves of Q25 - FULL OUTER JOIN - all three tables into the order they have to happen.
Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. A full outer join keeps matched rows and unmatched rows from every table, which is how all three kinds of leftover row appear.
Worked example
Question 25. Display all information in Customer, Rentals, and Rentcost in one resultset, each field once. Order by Customer.CID and Rentcost.Make. 12 rows, 12 columns.
Full outer join Customer to Rentals, then join Rentcost
Why: A full outer join keeps matched rows and unmatched rows from every table, which is how all three kinds of leftover row appear.
Select each field exactly once across the three tables
Why: Make lives in both Rentals and Rentcost, so it is listed a single time to satisfy the 'each field once' rule.
Order the result by Customer.CID and Rentcost.Make
Why: Ordering by customer id and then by make gives the 12-row, 12-column layout the question specifies.
Type this query in SQL Server:
select c.CID, c.CName, c.Age, c.Resid_City, c.BirthPlace,
r.Rtn, r.Make, r.Date_Out, r.Pickup, r.Date_returned, r.Return_city,
rc.Cost
from Customer c
full outer join Rentals r on c.CID = r.CID
full outer join Rentcost rc on r.Make = rc.Make
order by c.CID, rc.MakeRun it. Result (12 rows):
| CID | CName | Age | Resid_City | BirthPlace | Rtn | Make | Date_Out | Pickup | Date_returned | Return_city | Cost |
|---|---|---|---|---|---|---|---|---|---|---|---|
| NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 20 |
| NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 50 |
| 1 | Black | 40 | Erie | Tampa | 1 | Ford | 2010-10-10 | Cary | 2010-10-12 | Cary | 30 |
| 1 | Black | 40 | Erie | Tampa | 3 | Ford | 2009-01-01 | Erie | 2009-01-10 | Erie | 30 |
| 1 | Black | 40 | Erie | Tampa | 2 | GM | 2009-11-01 | Tampa | 2009-11-05 | Cary | 40 |
| 2 | Green | 25 | Cary | Erie | 4 | Nissan | 2010-11-07 | Tampa | NULL | NULL | 30 |
| 3 | Jones | 30 | Hemet | Tampa | 5 | Ford | 2010-10-01 | Cary | 2010-10-31 | Erie | 30 |
| 3 | Jones | 30 | Hemet | Tampa | 6 | GM | 2009-08-01 | Erie | 2009-08-05 | Erie | 40 |
| 4 | Martin | 35 | Hemet | Tampa | 7 | Ford | 2010-08-01 | Cary | 2010-08-12 | Erie | 30 |
| 5 | Simon | 22 | Erie | Erie | 8 | GM | 2010-09-01 | Erie | NULL | NULL | 40 |
| 6 | Vernon | 60 | Cary | Cary | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
| 7 | Wilson | 25 | Denver | Austin | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
Deliverable: screenshot the query and its results, then paste it below Question 25 in the Template.
Why: 12 rows x 12 columns. The all-NULL-CID rows at the top are Toyota/Volvo (never rented); the all-NULL-rental rows are Vernon/Wilson (never rented a car).
Error analysis
Annotate
Walk the callouts on Q25 - FULL OUTER JOIN - all three tables. Each one is a place this is easy to get subtly wrong.
Ranking
Put in order
These are the steps of Pattern: how to attack any of these 25, scrambled. Put them back in order before the next slide shows you.
Why: This is the order the recipe itself gives. Recalling the sequence without the slide in front of you is the difference between recognising the method and being able to run it — most of what goes wrong in practice is a step done out of turn.
Pattern
Every question in this lab fits one of a handful of shapes. Ask, in order:
Edge cases
Discussion prompt
Pattern: how to attack any of these 25 works on the cases you have just seen. Push it to the edge: what is the most degenerate input it still handles — empty, zero, one item, everything equal — and what is the first case where it stops being true? Name the case, not just "it breaks".
Hint: Try the smallest legal input, then the largest, then the one where two things collide. Methods are specified at their edges; the middle takes care of itself.
Answer:
Every question in this lab fits one of a handful of shapes. Ask, in order:
Elimination
Eliminate the wrong options
Q25 must return 12 rows including customers with no rental and makes never rented. Which join accomplishes that?
3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.
Survives elimination: A
Why: A FULL OUTER JOIN keeps matched rows plus unmatched rows from both sides, so the 8 rentals, 2 rental-less customers, and 2 never-rented makes all appear - 12 rows in total.
Check
Solve it in your head first, then click.
Check your understanding
Q25 must return 12 rows including customers with no rental and makes never rented. Which join accomplishes that?
Answer: A
Why: A FULL OUTER JOIN keeps matched rows plus unmatched rows from both sides, so the 8 rentals, 2 rental-less customers, and 2 never-rented makes all appear - 12 rows in total.
Connect it up
Draw it
One page, no notation unless you need it: draw how these connect — Setup & the AutoRentals database · Part 1 - Querying one table · Part 2 - Joining tables · Part 3 - Dates, aggregates & grouping · Part 4 - NULLs & comparing columns · Part 5 - Outer joins & subqueries. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.
Recap
Work each question slide into the Template and you've completed all 25 - query typed, executed, and screenshotted.
Before you submit: confirm each result's row count matches the assignment, and that every screenshot shows the query and its results together.
Want this taught 1-on-1? Alexander tutors Advanced SQL (CSIS 325) — $55/session, free consultation.