How to use CTEs in SQL. How to use a Common Table Expression to... | by Jason Lee | Towards Data Science

If you have ever used SQL, you know how important it is when you create a well-written query that would be helpful to use later. Usually, when you query something, you use it once, but there are times you need to reference old queries — this is where Common Table Expressions (CTEs) come in!
A CTE allows you to define a temporary named result set that available temporarily in the execution scope of a statement such as SELECT, INSERT, UPDATE, DELETE, or MERGE.
For this tutorial, I will assume you have basic — intermediate SQL experience. If you don’t, here are some great resources to get started.
Datacamp
Weekender Crash Course
Learn MS SQL Server + PostgreSQL
I will be working with a movie database from Datacamp’s SQL course. (If you are a student with an edu email, and want to get three months of free Datacamp visit — GitHub Student Developer Pack).
The Movie database consists of the following tables (+ columns):
Films — (id, title, release_year, country_duration, language, certification, gross, & budget)
People — (id, name, birthdate, & death date)
Reviews — (film_id, num_user, num_critic, imdb_score, num_votes, & facebook_likes)
Roles — (id, film_id, person_id, role)
We can see looking at the tables and columns; several keys can be used to join the tables together. Below are snapshots of each table.
As mentioned earlier CTEs are temporarily available in the execution scope of a statement.
Here is the basic layout of a CTE.
WITH CTE_NAME(column_1, column_2, column_3)AS    (--normal SQl query       SELECT *       FROM table)
In the code above, the CTE must start with a WITH. This tells your query that it is a CTE statement. Next, CTE_NAME is the name you want to call it. You can name it anything, but since this is generally used for a reference later, it should make sense. The column_1, column_2, & column_3 are the names you want your columns to be aliased. Finally, the AS, is the start of the SQL statement, which I labeled as #normal SQL query as a note reference.
Let’s see it in action!
Our goal is to write several queries that can be built into a CTE. We are trying to find the following,
Highest rated IMDB moving in 2014, that had an actor with a name starting with ‘Matt’
Immediately we can see the query can be broken down into several parts.
Names = ‘Matt’Films, and IMDB ratingYear 20143rd Highest IMDB
Names = ‘Matt’
Films, and IMDB rating
Year 2014
3rd Highest IMDB
For this example, we will create two CTEs.
We will first write a query without the CTE syntax to call all people in the entire database that have “Matt” in their name. We also want to join the roles table to find out the exact film_id that actor was in.
SELECT p.name, r.film_idFROM people AS pJOIN roles AS rON p.id = r.person_idWHERE p.name LIKE 'Matt%'
Now we have a query that calls all Matt’s we can use this later when building the CTE.
We want to build the next query to find each film in 2014 and join the reviews table to find their IMDB rating.
SELECT f.id, f.title, f.release_year, r.imdb_scoreFROM films AS fJOIN reviews AS rON f.id = r.film_idWHERE f.release_year = 2014
We now have all the necessary information from both of our queries to find the answer to find out which movie had the highest IMDB rating with an actor named ‘Matt.’
Let’s combine both our previous queries to create this CTE. When we want to add a second CTE in the same statement, we put a comma and add a second AS statement as seen below.
WITH    MATT_cte(actor, film_id)        AS(            SELECT p.name, r.film_id            FROM people AS p            JOIN roles AS r                ON p.id = r.person_id            WHERE p.name LIKE 'Matt%'),IMDB_cte(film_id, movie, release_year, IMDB_Score)        AS(            SELECT f.id, f.title, f.release_year, r.imdb_score            FROM films AS f            JOIN reviews AS r                ON f.id = r.film_id            WHERE f.release_year = 2014)
Our CTE has built and saved the two queries under the alias MATT_cte and IMDB_cte. These aliases will be a reference when we run the final code.
It is important to note that if you plan on joining the two CTEs, you create later, you will need to have a foreign key in both CTEs. In this case, our foreign keys are film_id in both tables.
We are all set to use our CTEs and get our answer! Let’s join our new temporary result and find out which movie it is. Reminded, we are trying to get the following. The CTE will need to be referenced in the same query, so we add our final query underneath it.
Highest rated IMDB moving in 2014, that had an actor with a name starting with ‘Matt’
WITH    MATT_cte(actor, film_id)        AS(            SELECT p.name, r.film_id            FROM people AS p            JOIN roles AS r                ON p.id = r.person_id            WHERE p.name LIKE 'Matt%'),IMDB_cte(film_id, movie, release_year, IMDB_Score)        AS(            SELECT f.id, f.title, f.release_year, r.imdb_score            FROM films AS f            JOIN reviews AS r                ON f.id = r.film_id            WHERE f.release_year = 2014)SELECT *FROM MATT_cte AS mJOIN IMDB_cte AS i    ON m.film_id = i.film_idORDER BY i.IMDB_Score DESC
We have successfully found all the names that contain “Matt” in it, movie title, IMDB score, and release year.
Our final answer would be that Interstellar as the highest-rated IMDB moving in 2014, that had an actor with a name starting with ‘Matt.’
I hope you see the value CTEs bring to your SQL queries. It is best used if you plan on calling the same query over and over again. For example, if we only wanted IMDB rated movies with a score higher than 8, we could make a temporary result to make sure that CTE is always available.
Another method used in SQL that can be helpful is subqueries. I plan on sharing a tutorial on how to use subqueries, so make sure to follow me for more lessons!
Connect with me on Linkedin or Github