MOO WebTech
SELECT Queriestheory-practicebeginner

Full SELECT with DVD Rental

Complete SELECT syntax using the DVD Rental database as a real-world context.

80 minL26

๐ŸŽฏLearning Objectives

  • Describe the DVD Rental database schema and identify the main tables
  • Write a basic SELECT โ€ฆ FROM โ€ฆ WHERE query to retrieve specific columns and rows
  • Use ORDER BY with multiple columns and both ASC / DESC directions
  • Apply LIMIT and OFFSET to paginate query results
  • Create column aliases with AS and use expressions inside the SELECT list
  • Explain when SELECT * is appropriate and when it should be avoided
  • Use DISTINCT to eliminate duplicate values from a result set

๐Ÿ“–Theory

1. The DVD Rental Schema

DVD Rental is a sample PostgreSQL database that models a movie rental store. You will use it throughout this course. The key tables for this lesson are:

TableWhat it contains
filmMovie catalogue โ€” title, description, release year, rating, rental price, length
actorActor names โ€” first and last name
film_actorJunction table linking films to actors (many-to-many)
customerStore customers โ€” name, email, address, active flag
rentalEach rental transaction โ€” which customer rented which inventory item and when

You can explore any table's columns with:

SQL
-- List all columns in a table
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'film'
ORDER BY ordinal_position;

2. The Basic SELECT Statement

The most fundamental SQL statement retrieves rows from a table. The minimal syntax is:

SQL
SELECT column1, column2
FROM table_name;

PostgreSQL reads this as: "go to table_name, and for each row return only column1 and column2."

SQL
-- Retrieve three columns from the film table
SELECT title, rental_rate, length
FROM film;

This returns one row per film (1 000 rows in DVD Rental) with only the three requested columns. Without a WHERE clause, every row is returned.

Tip: Always list specific columns in production code. Selecting only what you need is faster and makes your query self-documenting.

3. SELECT * vs. Explicit Column Lists

SELECT * means "give me every column." It is useful for quick exploration but problematic in application code.

SQL
-- Exploration: fine in psql during development
SELECT * FROM film LIMIT 5;

-- Production: always name your columns
SELECT film_id, title, rental_rate, rating, length
FROM film;

Warning: If someone adds or reorders columns in the table later, SELECT * returns them in a new order โ€” breaking any code that depends on column position. Explicit lists never change unless you change them intentionally.

4. Filtering Rows with WHERE

WHERE restricts which rows come back. The database evaluates the condition for every row and only returns rows where it is TRUE.

SQL
-- Only active customers (active = 1 means true in this schema)
SELECT first_name, last_name, email
FROM customer
WHERE active = 1;

Comparison operators available: =, <> (not equal), <, <=, >, >=.

SQL
-- Films with rental rate below $2
SELECT title, rental_rate
FROM film
WHERE rental_rate < 2.00;

-- Films with exactly the 'PG-13' rating
SELECT title, rating
FROM film
WHERE rating = 'PG-13';

Tip: String values in SQL use single quotes ('PG-13'), not double quotes. Double quotes are reserved for identifiers (table and column names that contain spaces or uppercase letters).

5. Sorting Results with ORDER BY

Without ORDER BY, PostgreSQL returns rows in an unspecified order โ€” it can vary between runs. Use ORDER BY whenever order matters.

SQL
-- Actors sorted alphabetically by last name, then first name
SELECT last_name, first_name
FROM actor
ORDER BY last_name ASC, first_name ASC;

ASC (ascending, Aโ†’Z, 1โ†’9) is the default, so you can omit it. Use DESC to reverse:

SQL
-- Most expensive films first
SELECT title, rental_rate
FROM film
ORDER BY rental_rate DESC;

You can sort by a column that is not in the SELECT list โ€” the column just needs to exist in the table.

6. Limiting Results with LIMIT and OFFSET

LIMIT n tells the database to return at most n rows. OFFSET k tells it to skip the first k rows before returning results. Together they implement pagination.

SQL
-- Page 1: films 1โ€“10 alphabetically
SELECT title
FROM film
ORDER BY title
LIMIT 10 OFFSET 0;

-- Page 3: films 21โ€“30 (skip first 20)
SELECT title
FROM film
ORDER BY title
LIMIT 10 OFFSET 20;

Warning: LIMIT without ORDER BY is non-deterministic โ€” you might get a different "first 10" each time the query runs. Always pair LIMIT with ORDER BY for predictable pagination.

7. Column Aliases with AS

AS renames a column in the result set. The alias appears as the column header and can be used in ORDER BY, but not in WHERE (the database evaluates WHERE before it applies aliases).

SQL
-- Rename rental_rate to something friendlier
SELECT title,
       rental_rate        AS price,
       rental_duration    AS days_to_keep
FROM film;

Aliases are especially useful when you compute expressions in the SELECT list:

SQL
-- Convert dollars to cents using arithmetic
SELECT title,
       rental_rate * 100  AS rate_cents,
       length / 60.0      AS length_hours
FROM film;

PostgreSQL evaluates any valid expression here: arithmetic (*, /, +, -), string concatenation (||), and function calls.

8. Removing Duplicates with DISTINCT

DISTINCT removes duplicate rows from the result. It compares the entire row โ€” or in SELECT DISTINCT col, just that column.

SQL
-- How many unique ratings exist?
SELECT DISTINCT rating
FROM film
ORDER BY rating;

Result: G, NC-17, PG, PG-13, R โ€” five distinct values from 1 000 rows.

SQL
-- Unique combinations of rating AND rental_rate
SELECT DISTINCT rating, rental_rate
FROM film
ORDER BY rating, rental_rate;

Tip: SELECT COUNT(DISTINCT rating) FROM film tells you the count without listing the values. We cover aggregate functions in lesson 29.

๐Ÿ’ปCode Examples

Example A โ€” Browsing the Film Catalogue

SQL
-- Show all films sorted by rental price (cheapest first),
-- then alphabetically within the same price tier.
-- Display price in cents for easier reading.

SELECT title,
       rating,
       rental_rate             AS price_usd,
       rental_rate * 100       AS price_cents,
       length                  AS length_min
FROM film
ORDER BY rental_rate ASC, title ASC
LIMIT 20;

What to notice:

  • Two ORDER BY columns: primary sort is price, secondary is title.
  • An arithmetic expression (* 100) in the SELECT list.
  • LIMIT 20 keeps the result manageable for review.

Example B โ€” Finding Active Customers

SQL
-- List all active customers with their emails,
-- sorted alphabetically by last name then first name.

SELECT customer_id,
       last_name || ', ' || first_name  AS full_name,
       email
FROM customer
WHERE active = 1
ORDER BY last_name ASC, first_name ASC;

What to notice:

  • String concatenation with || to build a formatted full name.
  • The alias full_name is used in the result but not in WHERE or ORDER BY (we sort by the original column names to stay safe).
  • Filtering by active = 1 before sorting avoids processing inactive customers.

Example C โ€” Paginated Film List

SQL
-- Simulate a "Films" page: show page 3 of films sorted by title.
-- Page size = 15 films per page.
-- Page 3 starts at row 31 (OFFSET = (page - 1) * page_size = 2 * 15 = 30).

SELECT film_id,
       title,
       rating,
       rental_rate
FROM film
ORDER BY title ASC
LIMIT 15 OFFSET 30;

What to notice:

  • OFFSET 30 skips the first 30 rows, so the result starts at the 31st film.
  • The formula for page n with page size s is: LIMIT s OFFSET (n-1) * s.
  • ORDER BY title ASC ensures the same films always appear on the same page.

โœ๏ธPractice Tasks

Task 1Top Rental Films
EASY

Write a query that lists the top 10 most expensive films to rent. Show title, rating, and rental_rate. Sort by rental_rate descending, then by title ascending when prices are equal.

Hint

Use ORDER BY rental_rate DESC, title ASC and add LIMIT 10. Your SELECT list needs only three columns from the film table.

SQL
SELECT title, rating, rental_rate
FROM film
ORDER BY rental_rate DESC, title ASC
LIMIT 10;
Task 2Actor Name Formatting
MEDIUM

Write a query that returns a single column called actor_name containing each actor's full name in the format "LAST, First" (last name in uppercase, first name in title case). Use the upper() and initcap() functions. Sort alphabetically by last name.

Tip: upper('hello') โ†’ 'HELLO'; initcap('JOHN') โ†’ 'John'; concatenate with ||.

Hint

Combine upper(), initcap(), and string concatenation inside the SELECT list. Give the expression an alias.

SQL
SELECT upper(last_name) || ', ' || initcap(first_name) AS actor_name
FROM actor
ORDER BY last_name ASC;
Task 3Film Catalogue with Pagination Formula
HARD

The DVD Rental website shows films in pages of 12. Write two queries:

  1. A query that returns films on page 4 (rows 37โ€“48), showing film_id, title, length, and rental_rate, ordered by film_id.
  2. A query that returns distinct rating values and distinct rental_rate values โ€” separately โ€” to understand how many unique pricing tiers and rating categories exist.
Hint

For page 4 with page size 12: OFFSET = (4 - 1) * 12 = 36.

SQL
-- Query 1: page 4
SELECT film_id, title, length, rental_rate
FROM film
ORDER BY film_id
LIMIT 12 OFFSET 36;

-- Query 2a: distinct ratings
SELECT DISTINCT rating
FROM film
ORDER BY rating;

-- Query 2b: distinct rental rates
SELECT DISTINCT rental_rate
FROM film
ORDER BY rental_rate;

โš ๏ธCommon Mistakes

Using double quotes for string values

Writing WHERE rating = "PG-13" causes a syntax error. PostgreSQL interprets double quotes as identifiers (table/column names), not strings. Always use single quotes for literal values: WHERE rating = 'PG-13'.

Forgetting ORDER BY with LIMIT

SELECT title FROM film LIMIT 10 returns 10 rows, but which 10? Without an ORDER BY, the database engine picks whatever is fastest to retrieve โ€” this can differ between runs. Add ORDER BY title (or any meaningful column) to get consistent, deterministic results.

Column alias in WHERE clause

Writing WHERE price_cents > 200 after aliasing rental_rate * 100 AS price_cents fails because WHERE is evaluated before the SELECT list is processed. Use the original expression: WHERE rental_rate * 100 > 200, or move the condition to a subquery.

Assuming SELECT * is always equivalent

SELECT * and SELECT col1, col2, col3 may return the same data today, but if a column is added to the table next month, SELECT * will include it automatically โ€” potentially breaking downstream code that expects a fixed number of columns.

Not quoting mixed-case identifiers

PostgreSQL folds unquoted identifiers to lowercase. If a column was created as "firstName" (with quotes), you must always refer to it as "firstName" โ€” not firstname or firstName. The DVD Rental schema uses lowercase snake_case throughout, so this is not an issue here, but keep it in mind for your own schemas.

๐ŸŽ“Instructor Notes

How to run this lesson (~80 min)

  • [10 min] Schema overview. Open the DVD Rental ER diagram or run \dt in psql. Walk through the five key tables. Ask the class: "If you were running a video rental store, what data would you track?" Let them name things โ€” film titles, customers, prices โ€” then show where each lives in the schema.
  • [20 min] Live code: sections 2โ€“4. Start with SELECT * FROM film LIMIT 5 so students can see the column names. Then switch to an explicit column list. Add a WHERE clause. Each step, ask: "What do you expect the result to look like before I run it?"
  • [15 min] Live code: sections 5โ€“8. Add ORDER BY, then LIMIT/OFFSET. Do the pagination example live โ€” ask "how do I get to page 2?" and let a student answer before running the query. Finish with DISTINCT on the rating column.
  • [25 min] Task 1 in class. Task 1 is straightforward โ€” most students finish in 5 minutes. Use the extra time to try variations: "What if you want the 10 cheapest films instead?" Walk around while they work.
  • [10 min] Wrap-up. Review Tasks 2โ€“3 as homework. Write the pagination formula OFFSET = (page - 1) * page_size on the board โ€” students will need it for the assignment.

Discussion questions

  • "Why would a web application use LIMIT and OFFSET instead of fetching all 1 000 films at once?"
  • "Can you think of a situation where SELECT * is actually the right choice?"
  • "If you sort by rental_rate DESC and two films have the same rate, what order do they appear in? How do you control that?"