Advanced SQL Queries Lab (AutoRentals)

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

What this lesson covers

The lesson, slide by slide

1. Advanced SQL Queries Lab

Title

CSIS 325

AutoRentals database - all 25 queries, one lesson. Type each query, run it in SQL Server, screenshot, paste.

2. What you'll be able to do

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.

One query at a time
Every question slide gives you the exact query to type.
Verified results
Each result table matches what SQL Server returns.
Concept first
New SQL ideas are taught right before the query that needs them.

3. Which is which: What you'll be able to do

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.

  • c1. One query at a time
  • c2. Verified results
  • c3. Concept first
  • b1. Every question slide gives you the exact query to type.
  • b2. Each result table matches what SQL Server returns.
  • b3. New SQL ideas are taught right before the query that needs them.

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.

4. Setup & the AutoRentals database

Section

Start here

5. Picture it first: The three tables

Picture it

Figure (svg): Entity diagram: Customer (key CID) links to Rentals via CID; Rentals links to Rentcost via Make.

Customer.CID feeds Rentals.CID; Rentcost.Make feeds Rentals.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.

6. The three tables

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.

Customer.CID feeds Rentals.CID; Rentcost.Make feeds Rentals.Make.
TablePrimary keyLinks to
CustomerCID-
RentalsRtnCID -> Customer, Make -> Rentcost
RentcostMake-

7. Fill in: Primary key for The three tables

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.

TablePrimary keyLinks to
CustomerCID-
RentalsRtnCID -> Customer, Make -> Rentcost
RentcostMake-

8. Guess the shape of the answer: Setup - build & populate the database

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.

9. Setup - build & populate the database

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 instructions

After it runs you should have these row counts:

TableRows
Customer7
Rentcost5
Rentals8

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.

10. What each one costs: Setup - build & populate the database

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.

TableRows
Customer7
Rentcost5
Rentals8

11. How to turn in each answer

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.

  1. Type the SQL query into the Template document, below the question.
  2. Execute it in SQL Server (SSMS): highlight the query, press F5.
  3. Screenshot the query and the result grid together in one image.
  4. Paste the screenshot below the typed query in the Template.

This deck gives you the exact query and the exact rows to expect for all 25. Q1 below is the worked model.

12. Break it if you can: How to turn in each answer

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.

13. What has to be given first: Q1 - The model answer (given)

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.

14. Q1 - The model answer (given)

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 Customer

Run it. Result (7 rows):

CNameAge
Black40
Green25
Jones30
Martin35
Simon22
Vernon60
Wilson25

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.

15. Watch it run: Q1 - The model answer (given)

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?

  1. Step 1: CName is Black
  2. Step 2: CName is Green
  3. Step 3: CName is Jones
  4. Step 4: CName is Martin
  5. Step 5: CName is Simon
  6. Step 6: CName is Vernon
  7. Step 7: CName is Wilson

16. Part 1 - Querying one table

Section

Questions 2-5

17. SELECT ... FROM ... WHERE

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.

18. By analogy: SELECT ... FROM ... WHERE

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).

19. Predict the next row: Q2 - Filter with 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

CNameResid_City
BlackErie
JonesHemet
MartinHemet

Why: The relationship between the columns, not the individual numbers, is what generates the next row. WHERE decides which rows; SELECT decides which columns.

20. Q2 - Filter with WHERE

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):

CNameResid_City
BlackErie
JonesHemet
MartinHemet

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.

21. Inspect it line by line: Q2 - Filter with WHERE

Error analysis

Annotate

Walk the callouts on Q2 - Filter with WHERE. Each one is a place this is easy to get subtly wrong.

  • WHERE decides which rows; SELECT decides which columns. They are independent.
  • The grader wants to see the query you ran and the rows it returned in the same screenshot.

22. Ranges, ordering, and aliases

Concept

Three tools you need for Q3 at once:

23. Predict the next row: Q3 - BETWEEN, ORDER BY, alias

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

CIDCustomer Name
7Wilson
4Martin
3Jones
2Green
1Black

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).

24. Q3 - BETWEEN, ORDER BY, alias

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 desc

Run it. Result (5 rows):

CIDCustomer Name
7Wilson
4Martin
3Jones
2Green
1Black

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).

25. Watch it run: Q3 - BETWEEN, ORDER BY, alias

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?

  1. Step 1: CID is 7
  2. Step 2: CID is 4
  3. Step 3: CID is 3
  4. Step 4: CID is 2
  5. Step 5: CID is 1

26. Something is wrong here: > and < drop the endpoints

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.

27. Trap: > and < drop the endpoints

Trap

The 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.

The fix

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.

28. Q4 - Pattern match with LIKE

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):

CIDCNameAgeResid_CityBirthPlace
2Green25CaryErie

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.

29. Work backwards from the answer: Q4 - Pattern match with LIKE

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.

30. DISTINCT removes duplicate rows

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.

31. Take the definitions apart: WHERE vs DISTINCT

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.

WHERE
Keeps only the rows where the condition is true.; Text literals go in single quotes
DISTINCT
Applied to the whole SELECT list; keeps one copy of each unique combination of the selected columns.
b1
Keeps only the rows where the condition is true. Text literals go in single quotes: 'Tampa'.
b2
Applied to the whole SELECT list; keeps one copy of each unique combination of the selected columns.

32. Q5 - DISTINCT + ORDER BY

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 Make

Run 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).

33. Rebuild the recipe: Pattern: the shape of every SELECT

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.

  1. SELECT the columns (add DISTINCT to dedupe, AS to rename)
  2. FROM the table
  3. WHERE the row condition (=, BETWEEN, LIKE, ...)
  4. 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.

34. Pattern: the shape of every SELECT

Pattern

Every single-table query you just wrote is the same skeleton in a fixed order:

  1. SELECT the columns (add DISTINCT to dedupe, AS to rename)
  2. FROM the table
  3. WHERE the row condition (=, BETWEEN, LIKE, ...)
  4. 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.

35. Rule out three: Check: inclusive ranges

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.

  • A. where Age between 25 and 40
  • B. where Age > 25 and Age < 40
  • C. where Age >= 25 or Age <= 40
  • D. where Age in (25, 40)

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.

36. Check: inclusive ranges

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?

  • A. where Age between 25 and 40 (correct)
  • B. where Age > 25 and Age < 40
  • C. where Age >= 25 or Age <= 40
  • D. where Age in (25, 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.

Why B tempts people
Strict > and < exclude the endpoints 25 and 40, losing three customers.
Why C tempts people
OR with >= and <= is true for every possible age, so it returns all 7 customers.
Why D tempts people
IN (25, 40) matches only ages that equal 25 or 40 exactly, skipping 30 and 35.

37. Part 2 - Joining tables

Section

Questions 6-9

38. INNER JOIN: match rows across tables

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.

An inner join keeps only the overlap: rows that match on both sides.

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.

39. Teach it back: INNER JOIN: match rows across tables

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.

40. Why a join, not two queries

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).

41. What has to happen first: Q6 - Two-table INNER JOIN

Ranking

Put in order

Put the moves of Q6 - Two-table INNER JOIN into the order they have to happen.

  1. Start from Customer and join Rentals on the shared key CID
  2. Select the three requested columns: CName, Make, and Pickup
  3. Add ORDER BY r.Pickup to sort the output ascending
  4. Deliverable: screenshot the query and its results, then paste it below Question 6 in the Template.

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.

42. Q6 - Two-table INNER JOIN

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.Pickup

Run it. Result (8 rows):

CNameMakePickup
BlackFordCary
JonesFordCary
MartinFordCary
BlackFordErie
JonesGMErie
SimonGMErie
BlackGMTampa
GreenNissanTampa

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.

43. Fill in: Pickup for Q6 - Two-table INNER JOIN

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.

CNameMakePickup
BlackFordCary
JonesFordCary
MartinFordCary
BlackFordErie
JonesGMErie
SimonGMErie
BlackGMTampa
GreenNissanTampa

44. Something is wrong here: a join with no ON condition

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.

45. Trap: a join with no ON condition

Trap

The 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.

The fix

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.

46. Q7 - JOIN + IN list

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.

47. Draw the shape of it: Q7 - JOIN + IN list

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.

48. Joining three tables

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.

49. Plan first: Q8 - Three-table JOIN

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:

  1. Join Customer to Rentals on CID, then Rentals to Rentcost on Make
  2. Select CName, Age, Make, and Cost across the joined tables
  3. Order the whole result by rc.Cost
  4. Deliverable: screenshot the query and its results, then paste it below Question 8 in the Template.

50. Q8 - Three-table JOIN

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.Cost

Run it. Result (8 rows):

CNameAgeMakeCost
Black40Ford30
Black40Ford30
Green25Nissan30
Jones30Ford30
Martin35Ford30
Black40GM40
Jones30GM40
Simon22GM40

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).

51. Watch it run: Q8 - Three-table JOIN

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?

  1. Step 1: CName is Black
  2. Step 2: CName is Black
  3. Step 3: CName is Green
  4. Step 4: CName is Jones
  5. Step 5: CName is Martin
  6. Step 6: CName is Black
  7. Step 7: CName is Jones
  8. Step 8: CName is Simon

52. Q9 - JOIN + DISTINCT on a filter

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.

53. Answer it before you see the options: Check: what INNER JOIN returns

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.

54. Check: what INNER JOIN returns

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?

  • A. They are left out of the result entirely (correct)
  • B. They appear once with NULL rental columns
  • C. They appear paired with every rental
  • D. The query raises an error

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.

Why B tempts people
That describes a LEFT OUTER JOIN, which keeps unmatched left rows with NULLs - not an inner join.
Why C tempts people
Pairing with every rental is a Cartesian product, which happens only when the ON condition is missing.
Why D tempts people
An unmatched row is a normal case, not an error; it is simply dropped.

55. Part 3 - Dates, aggregates & grouping

Section

Questions 10-15

56. Pulling the year from a date

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.

57. Q10 - Filter by year + DISTINCT

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) = 2009

Run it. Result (2 rows):

CNameAge
Black40
Jones30

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.

58. DATEDIFF: days between two dates

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.

59. Term to definition: Advanced SQL Queries Lab (AutoRentals)

Matching

Match the pairs

Match each term to the definition this lesson gave it — not the one you would guess from the word.

  • t1. WHERE
  • t2. DISTINCT
  • t3. INNER JOIN ... ON
  • t4. YEAR(date)
  • t5. DATEDIFF(day, d1, d2)
  • d1. Keeps only the rows where the condition is true. Text literals go in single quotes: 'Tampa'.
  • d2. Applied to the whole SELECT list; keeps one copy of each unique combination of the selected columns.
  • d3. Returns only rows where the ON condition matches in both tables. Unmatched rows are dropped.
  • d4. A T-SQL function returning the 4-digit year. DATEPART(year, date) does the same thing.
  • d5. Counts day boundaries crossed from d1 to d2. The unit 'day' can also be month, year, etc.

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.

60. Guess the shape of the answer: Q11 - DATEDIFF for a rental length

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.

61. Q11 - DATEDIFF for a rental length

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.

62. Q12 - DATEDIFF x Cost

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.

63. Where the cost goes: Q12 - DATEDIFF x Cost

Cost model

Annotate

In Q12 - DATEDIFF x Cost, before reading the notes: mark where the time actually goes. Which line dominates?

  • 4 days x $40/day for a GM = $160. The join to Rentcost supplies the $40 rate.
  • The grader wants to see the query you ran and the rows it returned in the same screenshot.

64. SUM adds a column across rows

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.

65. Plan first: Q13 - SUM over all rows

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:

  1. Join Rentals to Rentcost so each rental knows its daily rate
  2. Multiply DATEDIFF(day, Date_Out, Date_returned) by Cost per rental
  3. Wrap the whole expression in SUM to total every rental
  4. Deliverable: screenshot the query and its results, then paste it below Question 13 in the Template.

66. Q13 - SUM over all rows

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.Make

Run 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.

67. GROUP BY: one summary row per group

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.

68. Q14 - AVG with 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.Make

Run it. Result (2 rows):

MakeAvgDays
Ford13
GM4

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.

69. Something is wrong here: AVG on an integer column truncates

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.

70. Trap: AVG on an integer column truncates

Trap

The 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.

The fix

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.

71. Break it on purpose: AVG on an integer column truncates

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.

72. State the rule before it runs: Q15 - AVG without truncation (CAST)

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.

73. Q15 - AVG without truncation (CAST)

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_City

Run it. Result (4 rows):

Resid_CityAvgAge
Cary42.5
Denver25
Erie31
Hemet32.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.

74. What each one costs: Q15 - AVG without truncation (CAST)

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_CityAvgAge
Cary42.5
Denver25
Erie31
Hemet32.5

75. Check: keeping the decimal in AVG

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?

  • A. avg(cast(Age as float)) (correct)
  • B. avg(Age)
  • C. cast(avg(Age) as int)
  • D. round(avg(Age), 0)

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.

Why B tempts people
With an integer column, AVG uses integer division and truncates 42.5 to 42.
Why C tempts people
Averaging as integers already lost the .5; casting the 42 result to int changes nothing.
Why D tempts people
ROUND runs after the integer AVG already truncated to 42, so it rounds 42, not 42.5.

76. Part 4 - NULLs & comparing columns

Section

Questions 16, 21, 22

77. Comparing two columns in the same row

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.

78. Q16 - Column vs column

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 = BirthPlace

Run 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.

79. Q21 - Column vs column across a join

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_City

Run 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.

80. Testing for NULL

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.

81. Something is wrong here: = NULL never matches

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.

82. Trap: = NULL never matches

Trap

The 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.

The fix

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.

83. Q22 - IS NULL

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 null

Run 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.

84. How sure are you: Check: testing for a missing value

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.

85. Check: testing for a missing value

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)?

  • A. where Date_returned is null (correct)
  • B. where Date_returned = null
  • C. where Date_returned = ''
  • D. where Date_returned <> Date_returned

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.

Why B tempts people
= NULL is never true, even for a NULL value, so this returns no rows.
Why C tempts people
An empty string '' is a real value different from NULL; the date columns hold NULL, not ''.
Why D tempts people
Comparing a column to itself is unknown when the value is NULL, so this also matches nothing.

86. Part 5 - Outer joins & subqueries

Section

The 'never' problems: Q17-20, 23-24

87. Picture it first: OUTER JOIN keeps the unmatched rows

Picture it

Figure (svg): Venn diagram with the entire left circle shaded, showing a left outer join keeps all left rows including unmatched.

A left outer join keeps every left row; unmatched rows arrive with NULLs.

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.

88. OUTER JOIN keeps the unmatched rows

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.

A left outer join keeps every left row; unmatched rows arrive with NULLs.

89. The anti-join trick

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.

90. Q17 - LEFT OUTER JOIN anti-join

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 null

Run 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.

91. Type I subquery: uncorrelated (NOT IN)

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.

92. Teach it back: Type I subquery: uncorrelated (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).

93. What has to be given first: Q18 - Type I: NOT IN

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.

94. Q18 - Type I: NOT IN

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.

95. What has to happen first: Q19 - RIGHT OUTER JOIN anti-join

Ranking

Put in order

Put the moves of Q19 - RIGHT OUTER JOIN anti-join into the order they have to happen.

  1. Put Customer on the right and RIGHT OUTER JOIN Rentals to it
  2. Keep only the rows where r.CID IS NULL
  3. Select the distinct customer names that remain
  4. Customer is on the right, so RIGHT keeps every customer.
  5. Deliverable: screenshot the query and its results, then paste it below Question 19 in the Template.

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.

96. Q19 - RIGHT OUTER JOIN anti-join

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 null

Run 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.

97. Type II subquery: correlated (NOT EXISTS)

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.

98. By analogy: Type II subquery: correlated (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?'

99. Plan first: Q20 - Type II: NOT EXISTS

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:

  1. Test each customer with a correlated NOT EXISTS subquery
  2. Point the subquery at Rentals where r.CID equals the outer c.CID
  3. Keep customers for whom the subquery finds nothing
  4. Deliverable: screenshot the query and its results, then paste it below Question 20 in the Template.

100. Q20 - Type II: NOT EXISTS

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.

101. Draw the shape of it: Q20 - Type II: NOT EXISTS

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.

102. Something is wrong here: NOT IN vs NOT EXISTS with NULLs

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.

103. Trap: NOT IN vs NOT EXISTS with NULLs

Trap

The 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.

The fix

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.

104. Which of these survive contact with Advanced SQL Queries Lab (AutoRentals)?

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.

Holds up
The lab uses one database, AutoRentals, with three related tables. Know the keys and you know how they join.; Every one of the 25 questions is delivered the same way. Miss a step and you lose the point even with a correct query.; Every query names the columns you want (SELECT), the table (FROM), and optionally a row filter (WHERE).
Breaks
Task says 25 to 40 inclusive.; Listing tables with no match condition:
sound
These are stated as this lesson states them — each one survives the edge cases Advanced SQL Queries Lab (AutoRentals) puts it through.
flawed
Each of these is lifted from a trap in this deck: reasonable-sounding, and wrong in a way that only shows up once you rely on it.

105. Guess the shape of the answer: Q23 - Type I with IN

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.

106. Q23 - Type I with IN

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 CName

Run it. Result (3 rows):

CNameAge
Black40
Jones30
Martin35

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.

107. Fill in: Age for Q23 - Type I with IN

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.

CNameAge
Black40
Jones30
Martin35

108. Plan first: Q24 - Type II with EXISTS

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:

  1. Test each customer with a correlated EXISTS subquery
  2. Reference the outer c.CID inside the subquery on Rentals
  3. Keep customers for whom a Cary pickup exists
  4. Deliverable: screenshot the query and its results, then paste it below Question 24 in the Template.

109. Q24 - Type II with EXISTS

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.

110. Work backwards from the answer: Q24 - Type II with EXISTS

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.

111. Answer it before you see the options: Check: Type I vs Type II

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.

112. Check: Type I vs Type II

Check

Solve it in your head first, then click.

Check your understanding

What makes a subquery 'Type II' (correlated) rather than 'Type I'?

  • A. It references a column from the outer query, so it runs once per outer row (correct)
  • B. It uses IN instead of EXISTS
  • C. It returns more than one column
  • D. It is written on a single line

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.

Why B tempts people
IN is typically used with Type I subqueries; EXISTS is the usual Type II form - the opposite of this claim.
Why C tempts people
Column count doesn't define correlation; EXISTS subqueries often select * and are still correlated.
Why D tempts people
Formatting on one line or many has no effect on whether a subquery is correlated.

113. Part 6 - Putting it all together

Section

Question 25

114. FULL OUTER JOIN keeps everything

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.

115. Break it if you can: FULL OUTER JOIN keeps everything

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.

116. What has to happen first: Q25 - FULL OUTER JOIN - all three tables

Ranking

Put in order

Put the moves of Q25 - FULL OUTER JOIN - all three tables into the order they have to happen.

  1. Full outer join Customer to Rentals, then join Rentcost
  2. Select each field exactly once across the three tables
  3. Order the result by Customer.CID and Rentcost.Make
  4. Deliverable: screenshot the query and its results, then paste it below Question 25 in the Template.

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.

117. Q25 - FULL OUTER JOIN - all three tables

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.Make

Run it. Result (12 rows):

CIDCNameAgeResid_CityBirthPlaceRtnMakeDate_OutPickupDate_returnedReturn_cityCost
NULLNULLNULLNULLNULLNULLNULLNULLNULLNULLNULL20
NULLNULLNULLNULLNULLNULLNULLNULLNULLNULLNULL50
1Black40ErieTampa1Ford2010-10-10Cary2010-10-12Cary30
1Black40ErieTampa3Ford2009-01-01Erie2009-01-10Erie30
1Black40ErieTampa2GM2009-11-01Tampa2009-11-05Cary40
2Green25CaryErie4Nissan2010-11-07TampaNULLNULL30
3Jones30HemetTampa5Ford2010-10-01Cary2010-10-31Erie30
3Jones30HemetTampa6GM2009-08-01Erie2009-08-05Erie40
4Martin35HemetTampa7Ford2010-08-01Cary2010-08-12Erie30
5Simon22ErieErie8GM2010-09-01ErieNULLNULL40
6Vernon60CaryCaryNULLNULLNULLNULLNULLNULLNULL
7Wilson25DenverAustinNULLNULLNULLNULLNULLNULLNULL

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).

118. Inspect it line by line: Q25 - FULL OUTER JOIN - all three tables

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.

  • A full outer join keeps matched rows and unmatched rows from every table, which is how all three kinds of leftover row appear.
  • Make lives in both Rentals and Rentcost, so it is listed a single time to satisfy the 'each field once' rule.
  • Ordering by customer id and then by make gives the 12-row, 12-column layout the question specifies.

119. Rebuild the recipe: Pattern: how to attack any of these 25

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.

  1. Which tables hold the columns I need? One table -> plain SELECT. Two or three -> JOIN on the keys.
  2. Am I filtering rows? Use WHERE (=, BETWEEN, LIKE, IS NULL, or column-vs-column).
  3. Am I summarizing? Use SUM/AVG, add GROUP BY for 'broken out by', and CAST to float to keep decimals.
  4. Am I looking for what NEVER happened? Outer join + IS NULL, or a Type I (NOT IN) / Type II (NOT EXISTS) subquery.
  5. Did I match the exact wording? row count, ordering, DISTINCT, and column headings.

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.

120. Pattern: how to attack any of these 25

Pattern

Every question in this lab fits one of a handful of shapes. Ask, in order:

  1. Which tables hold the columns I need? One table -> plain SELECT. Two or three -> JOIN on the keys.
  2. Am I filtering rows? Use WHERE (=, BETWEEN, LIKE, IS NULL, or column-vs-column).
  3. Am I summarizing? Use SUM/AVG, add GROUP BY for 'broken out by', and CAST to float to keep decimals.
  4. Am I looking for what NEVER happened? Outer join + IS NULL, or a Type I (NOT IN) / Type II (NOT EXISTS) subquery.
  5. Did I match the exact wording? row count, ordering, DISTINCT, and column headings.

121. Where does it stop working: Pattern: how to attack any of these 25

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:

122. Rule out three: Check: which join gives 12 rows

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.

  • A. FULL OUTER JOIN across all three tables
  • B. INNER JOIN across all three tables
  • C. LEFT OUTER JOIN from Rentals only
  • D. A Cartesian product of the three tables

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.

123. Check: which join gives 12 rows

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?

  • A. FULL OUTER JOIN across all three tables (correct)
  • B. INNER JOIN across all three tables
  • C. LEFT OUTER JOIN from Rentals only
  • D. A Cartesian product of the three tables

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.

Why B tempts people
INNER JOIN drops unmatched rows, giving only the 8 rentals - it loses Vernon, Wilson, Toyota, and Volvo.
Why C tempts people
A LEFT JOIN anchored on Rentals keeps the 8 rentals but never introduces the customers or makes that have no rental.
Why D tempts people
A Cartesian product multiplies the tables into dozens of meaningless rows, not the 12 required.

124. Connect it up: Advanced SQL Queries Lab (AutoRentals)

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.

125. You just finished the whole lab

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.

Sources

  1. CSIS 325 Lab: Advanced SQL Queries - Assignment Instructions & Template — Liberty University CSIS 325, AutoRentals dataset
  2. Microsoft T-SQL reference (DATEDIFF, YEAR, OUTER JOIN, EXISTS)

Want this taught 1-on-1? Alexander tutors Advanced SQL (CSIS 325) — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108