Full SELECT with DVD Rental
Complete SELECT syntax using the DVD Rental database as a real-world context.
๐ฏLearning Objectives
- Describe the DVD Rental database schema and identify the main tables
- Write a basic
SELECT โฆ FROM โฆ WHEREquery to retrieve specific columns and rows - Use
ORDER BYwith multiple columns and bothASC/DESCdirections - Apply
LIMITandOFFSETto paginate query results - Create column aliases with
ASand use expressions inside theSELECTlist - Explain when
SELECT *is appropriate and when it should be avoided - Use
DISTINCTto 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:
| Table | What it contains |
|---|---|
film | Movie catalogue โ title, description, release year, rating, rental price, length |
actor | Actor names โ first and last name |
film_actor | Junction table linking films to actors (many-to-many) |
customer | Store customers โ name, email, address, active flag |
rental | Each 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 BYcolumns: primary sort is price, secondary is title. - An arithmetic expression (
* 100) in theSELECTlist. LIMIT 20keeps 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_nameis used in the result but not inWHEREorORDER BY(we sort by the original column names to stay safe). - Filtering by
active = 1before 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 30skips the first 30 rows, so the result starts at the 31st film.- The formula for page
nwith page sizesis:LIMIT s OFFSET (n-1) * s. ORDER BY title ASCensures the same films always appear on the same page.
โ๏ธPractice Tasks
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;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;The DVD Rental website shows films in pages of 12. Write two queries:
- A query that returns films on page 4 (rows 37โ48), showing
film_id,title,length, andrental_rate, ordered byfilm_id. - A query that returns distinct
ratingvalues and distinctrental_ratevalues โ 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
\dtin 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 5so students can see the column names. Then switch to an explicit column list. Add aWHEREclause. 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, thenLIMIT/OFFSET. Do the pagination example live โ ask "how do I get to page 2?" and let a student answer before running the query. Finish withDISTINCTon theratingcolumn. - [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_sizeon 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 DESCand two films have the same rate, what order do they appear in? How do you control that?"