25 Advanced SQL Query Examples
25 Advanced SQL Query Examples
25 Advanced SQL Query Examples
com
One of the best ways to learn advanced SQL is by studying example queries. In this
Table of
article, we'll show 25 examples of advanced SQL queries from medium to high
Contents
complexity. You can use them to refresh your knowledge of advanced SQL or to
25
review before a SQL interview.
Advanced
SQL
Query
Examples
with
Explanations
Examp
#1 -
Rankin
https://learnsql.com/blog/25-advanced-sql-query-examples/ 1/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
ManyRows
of the examples in this article will be based on the following employee
Based
table. Only a few examples will be based on other tables; in these cases, the tables
on a
will be explained along with the example.
Specific
Orderin
employee_id first_name last_name dept_id
Criteria
100Examp John White IT
#2 -
101 Mary Danner Account
List
102The Ann Lynn Sales
First
103 Peter O'connor IT
5
106Rows Sue Sanchez Sales
of a
107Result Marta Doe Sales
Set
109 Ann Danner Account
Examp
110#3 - Simon Yang CEO
List
111 Juan Graue Sales
the
Last
Even for persons with SQL expertise, a good online interactive SQL course can be
a real help. You can find the most complete set of interactive SQL courses in our
SQL from A to Z track. It contains 7 interactive SQL courses with over 850(!)
exercises logically arranged to take you from a complete beginner to an advanced
SQL user. The beginner courses cover the foundations of SQL and are a perfect
way to review and refresh your basic SQL knowledge. The advanced SQL courses
will teach you concepts like window functions, recursive queries, and complex SQL
reports. Our platform offers many ways to practice advanced SQL online. Create a
free LearnSQL.com account and try our interactive courses without having to spend
any money. Then, if you like what you’re learning, you can buy full access to our
platform.
https://learnsql.com/blog/25-advanced-sql-query-examples/ 2/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
https://learnsql.com/blog/25-advanced-sql-query-examples/ 3/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
Code
SELECT
employee_id,
last_name,
first_name,
salary,
RANK() OVER (ORDER BY salary DESC) as ranking
FROM employee
ORDER BY ranking
In the above query, we use the function RANK() . It is a window function that
returns each row’s position in the result set, based on the order defined in the
OVER clause (1 for the highest salary, 2 for the second-highest, and so on). We
need to use an ORDER BY ranking clause at the end of the query to indicate the
order on which the result set will be shown.
If you want to know more about ranking functions in SQL, I recommend our article
What Is the RANK() Function in SQL, and How Do You Use It?
Code
WITH employee_ranking AS (
SELECT
employee_id,
last_name,
first_name,
https://learnsql.com/blog/25-advanced-sql-query-examples/ 4/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
salary,
RANK() OVER (ORDER BY salary DESC) as ranking
FROM employee
)
SELECT
employee_id,
last_name,
first_name,
salary
FROM employee_ranking
WHERE ranking <= 5
ORDER BY ranking
The WITH clause in the previous query creates a CTE called employee_ranking ,
which is a kind of virtual table that’s consumed in the main query. The subquery in
the CTE uses the function RANK() to obtain the position of each row in the ranking.
The clause OVER (ORDER BY salary DESC) indicates how the RANK() value must be
calculated. The RANK() function for the row with the highest salary will return 1,
and so on.
Finally, in the WHERE of the main query we ask for those rows with a ranking value
smaller or equal than 5. This lets us obtain only the top 5 rows by ranking value.
Again, we use an ORDER BY clause to show the result set, which is ordered by rank
ascending.
Code
WITH employee_ranking AS (
SELECT
employee_id,
last_name,
first_name,
salary,
RANK() OVER (ORDER BY salary ASC) as ranking
https://learnsql.com/blog/25-advanced-sql-query-examples/ 5/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
FROM employee
)
SELECT
employee_id,
last_name,
Book
Back toLovers
articlesDay
list Sale - 26 hours only!
Articles Cookbook
first_name,
salary Get 70+ online SQL courses and save up to $450
FROM employee_ranking 25h : 45m : 14s
WHERE ranking <= 5
ORDER BY ranking
In the main query, we use WHERE ranking <= 5 to filter the rows with the 5 lowest
salaries. After that, we use ORDER BY ranking to order the rows of the report by
ranking value.
Code
WITH employee_ranking AS (
SELECT
employee_id,
last_name,
first_name,
salary,
RANK() OVER (ORDER BY salary DESC) as ranking
FROM employee
)
SELECT
employee_id,
last_name,
first_name,
salary
FROM employee_ranking
WHERE ranking = 2
The WHERE condition ranking = 2 is used to filter the rows with the salary in
position 2. Note that we can have more than one employee in position 2 if they
https://learnsql.com/blog/25-advanced-sql-query-examples/ 6/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
Code
WITH employee_ranking AS (
SELECT
employee_id,
last_name,
first_name,
salary,
dept_id
RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) as ranking
FROM employee
)
SELECT
dept_id,
employee_id,
last_name,
first_name,
salary
FROM employee_ranking
WHERE ranking = 2
ORDER BY dept_id, last_name
The main change introduced in this query is the PARTITION BY dept_id clause in
OVER . This clause groups rows with the same dept_id , ordering the rows in each
group by salary DESC . Then the RANK() function is calculated for each
department.
https://learnsql.com/blog/25-advanced-sql-query-examples/ 7/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
In the main query, we return the dept_id and the employee data for those
employees in position 2 of their departmental ranking.
You can read more about combining RANK() with PARTITION BY in How to Use
the SQL RANK OVER (PARTITION BY). For those readers who want to find out more
about finding the Nth highest row in a group, I recommend the article How to Find
the Nth-Highest Salary by Department with SQL.
Code
WITH employee_ranking AS (
SELECT
employee_id,
last_name,
first_name,
salary,
NTILE(2) OVER (ORDER BY salary ) as ntile
FROM employee
)
SELECT
employee_id,
last_name,
https://learnsql.com/blog/25-advanced-sql-query-examples/ 8/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
first_name,
salary
FROM employee_ranking
WHERE ntile = 1
ORDER BY salary
The above query returns only the rows in the first half of a report of employees
ordered by salary in ascending order. We use the condition ntile = 1 to filter
only those rows in the first half of the report. If you are interested in the NTILE()
window function, see the article Common SQL Window Functions: Using Partitions
With Ranking Functions.
Code
WITH employee_ranking AS (
SELECT
employee_id,
last_name,
first_name,
salary,
NTILE(4) OVER (ORDER BY salary) as ntile
FROM employee
)
SELECT
employee_id,
last_name,
first_name,
salary
FROM employee_ranking
WHERE ntile = 4
ORDER BY salary
https://learnsql.com/blog/25-advanced-sql-query-examples/ 9/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
The WHERE ntile = 4 condition filters only the rows in the last quarter of the
report. The last clause ORDER BY salary orders the result set to be returned by the
query, while OVER (ORDER BY salary) orders the rows before dividing them into 4
subsets using NTILE(4) .
Code
SELECT
employee_id,
last_name,
first_name,
salary,
ROW_NUMBER() OVER (ORDER BY employee_id) as ranking_position
FROM employee
If you want to learn about different advanced ranking functions, I recommend the
article Overview of Ranking Functions in SQL.
https://learnsql.com/blog/25-advanced-sql-query-examples/ 10/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
We have a product table with 3 records (corn flakes, sugared corn flakes and rice
flakes) and another table called box_size with 3 records one for 1 pound and two
records for 3 and 5 pounds, respectively. If we want to create a report with the
price list for our nine combinations, we can use the following query:
Code
SELECT
grain.product_name,
box_size.description,
grain.price_per_pound * box_size.box_weight
FROM product
CROSS JOIN box_sizes
The CROSS JOIN clause without any condition produces a table with all row
combinations from both tables. Note we calculate the price based on the per-
pound price stored in the product table and the weight from box_sizes with the
expression:
Code
https://learnsql.com/blog/25-advanced-sql-query-examples/ 11/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
grain.price_per_pound * box_size.box_weight
A deep dive into the CROSS JOIN can be found in An Illustrated Guide to the SQL
CROSS JOIN.
Code
SELECT
e1.first_name ||' '|| e1.last_name AS manager_name,
e2.first_name ||' '|| e2.last_name AS employee_name
FROM employee e1
JOIN employee e2
ON e1.employee_id = e2.manager_id
In the above query, we can see the table employee is referenced twice as e1 and
e2 , and the join condition is e1.employee_id = e2.manager_id . This condition
links each employee row with the manager row. The article What Is a Self Join in
SQL? An Explanation With Seven Examples will give you more ideas about when
you can apply self joins in your SQL queries.
https://learnsql.com/blog/25-advanced-sql-query-examples/ 12/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
Code
SELECT
first_name,
last_name,
salary
FROM employee
WHERE salary > ( SELECT AVG(salary) FROM employee )
You can see the subquery that obtains the average salary in the WHERE clause. In
the main query, we select the employee name and salary. You can read more about
subqueries in the article How to practice SQL subqueries.
Code
SELECT
first_name,
https://learnsql.com/blog/25-advanced-sql-query-examples/ 13/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
last_name,
salary
FROM employee e1
WHERE salary >
(SELECT AVG(salary)
FROM employee e2
WHERE e1.departmet_id = e2.department_id)
Code
SELECT
first_name,
last_name
FROM employee e1
WHERE department_id IN (
SELECT department_id
FROM department
WHERE manager_name=‘John Smith’)
The previous subquery is a multi-row subquery: it returns more than one row. In
fact, it will return several rows because John Smith manages many departments.
When working with multi-row subqueries, you need to use specific operators (like
IN) in the WHERE condition involving the subquery.
https://learnsql.com/blog/25-advanced-sql-query-examples/ 14/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
Code
SELECT
employee_id,
last_name,
first_name,
dept_id,
manager_id,
salary
FROM employee
GROUP BY
employee_id,
last_name,
first_name,
dept_id,
manager_id,
salary
HAVING COUNT(*) > 1
The rows that are not duplicated will have a COUNT(*) equal to 1, but those rows
that exist many times will have a COUNT(*) returning the number of times that the
row exists. I suggest the article How to Find Duplicate Values in SQL if you want to
find more details about this technique.
Code
SELECT
employee_id,
https://learnsql.com/blog/25-advanced-sql-query-examples/ 15/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
last_name,
first_name,
dept_id,
manager_id,
salary,
COUNT(*) AS number_of_rows
FROM employee
GROUP BY
employee_id,
last_name,
first_name,
dept_id,
manager_id,
salary
HAVING COUNT(*) > 1
Again, you can find valuable information about how to manage duplicate records in
the article How To Find Duplicate Records in SQL.
Code
SELECT
last_name,
first_name
FROM employee
INTERSECT
SELECT
last_name,
first_name
FROM employee_2020_jan
As a result, we will obtain a list of employees that appear in both tables. Perhaps
they’ll have different values on the columns like salary or dept_id . In other
https://learnsql.com/blog/25-advanced-sql-query-examples/ 16/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
words, we are obtaining those employees who worked for the company in Jan
2020 and who are still working for the company.
If you are interested in finding more about set operators, I suggest the article
Introducing SQL Set Operators: Union, Union All, Minus, and Intersect.
Code
SELECT
dept_id,
expertise,
SUM(salary) total_salary
FROM employee
GROUP BY dept_id, expertise
The GROUP BY has the optional clause ROLLUP , which allows it to include
additional groupings in one query. Adding the ROLLUP clause to our example could
give us the total sum of salaries for each department (no matter what expertise
level the employee has) and the total sum of salaries for the whole table (no matter
the employee’s department and expertise level). The modified query is:
Code
SELECT
dept_id,
expertise,
SUM(salary) total_salary
FROM employee
GROUP BY ROLLUP (dept_id, expertise)
https://learnsql.com/blog/25-advanced-sql-query-examples/ 17/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
IT Senior 250000
IT NULL 250000
The rows in the result set with a NULL are the extra rows added by the ROLLUP
clause. A NULL value in the column expertise means a group of rows for a
specific value of dept_id but without a specific expertise value. In other words,
it is the total amount of salaries for each dept_id . In the same way, the last row of
the result having a NULL for columns dept_id and expertise means the grand
total for all departments in the company.
If you want to learn more about the ROLLUP clause and other similar clauses like
CUBE , the article Grouping, Rolling, and Cubing Data has lots of examples.
Code
https://learnsql.com/blog/25-advanced-sql-query-examples/ 18/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
SELECT
SUM (CASE
WHEN dept_id IN ('SALES','HUMAN RESOURCES')
THEN salary
ELSE 0 END) AS total_salary_sales_and_hr,
SUM (CASE
WHEN dept_id IN ('IT','SUPPORT')
THEN salary
ELSE 0 END) AS total_salary_it_and_support
FROM employee
The query returns a single row with two columns. The first column shows the total
salary for the Sales and Human Resources departments. This value is calculated
using the SUM() function on the salary column – but only when the employee
belongs to the Sales or Human Resources department. A zero is added to the sum
when the employee belongs to any other department. The same idea is applied for
the total_salary_it_and_support column.
The article How to Use CASE WHEN with SUM() in SQL provides more details
about this technique.
Code
https://learnsql.com/blog/25-advanced-sql-query-examples/ 19/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
SELECT
CASE
WHEN salary <= 750000 THEN 'low'
WHEN salary > 750000 AND salary <= 100000 THEN 'medium'
WHEN salary > 100000 THEN 'high'
END AS salary_category,
COUNT(*) AS number_of_employees
FROM employee
GROUP BY
CASE
WHEN salary <= 750000 THEN 'low'
WHEN salary > 750000 AND salary <= 100000 THEN 'medium'
WHEN salary > 100000 THEN 'high'
END
In this query, we use CASE to define the salary range for each employee. You can
see the same CASE statement twice. The first one defines the ranges, as we just
said; the second one in the GROUP BY aggregates records and applies the
COUNT(*) function to each group of records. You can use the CASE statement in
the same way to compute counts or sums for other custom-defined levels.
How to Use CASE in SQL explains other examples of CASE statements like the one
used in this query.
Do you want to take your SQL skills to the next level? Check out our
Advanced SQL track.
When you have a table that stores any daily metric, such as a sales table with the
columns day and daily_amount , you can calculate the running total as the
cumulative sum of all previous daily_amount values. SQL provides a window
function called SUM() to do just that.
In the following query, we’ll calculate the cumulative sales for each day:
https://learnsql.com/blog/25-advanced-sql-query-examples/ 20/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
Code
SELECT
day,
daily_amount,
SUM (daily_amount) OVER (ORDER BY day) AS running_total
FROM sales
The SUM() function uses the OVER() clause to define the order of the rows; all
rows previous to the current day are included in the SUM() . Here’s a partial result:
The first two columns day and daily_amount are values taken directly from the
table sales . The column running_total is calculated by the expression:
Code
You can clearly see how the running_total is the accumulated sum of the previous
daily_amounts .
If you wish to go deeper on this topic, I suggest the article What Is a SQL Running
Total and How Do You Compute It?, which includes many clarifying examples.
https://learnsql.com/blog/25-advanced-sql-query-examples/ 21/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
how these averages behave over time instead of examining the behavior of the
original or raw data points.
Let’s calculate the moving average for the last 7 days using the sales table from
the previous example:
Code
SELECT
day,
daily_amount,
AVG (daily_amount) OVER (ORDER BY day ROWS 6 PRECEDING)
AS moving_average
FROM sales
In the above query, we use the AVG() window function to calculate the average
using the current row (today) and the previous 6 rows. As the rows are ordered by
day, the current row and the 6 previous rows defines a period of 1 week.
The article What a Moving Average Is and How to Compute it in SQL goes into
detail about this subject; check it out if you want to learn more.
Let’s suppose we want to obtain a report with the total amount sold on each day,
but we also want to obtain the difference (or delta) related to the previous day. We
can use a query like this one:
Code
SELECT
day,
daily_amount,
daily_amount - LAG(daily_amount) OVER (ORDER BY day)
https://learnsql.com/blog/25-advanced-sql-query-examples/ 22/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
AS delta_yesterday_today
FROM sales
Code
Both elements of the arithmetic difference come from different rows. The first
element comes from the current row and LAG(daily_amount) comes from the
previous day row. LAG() returns the value of any column from the previous row
(based on the ORDER BY specified in the OVER clause).
If you want to read more about LAG() and LEAD() , I suggest the article How to
Calculate the Difference Between Two Rows in SQL.
In this example, we will use the sales table, which has data in a daily granularity.
We first need to aggregate the data to the year or month, which we will do by
creating a CTE with amounts aggregated by year. Here’s the query:
Code
WITH year_metrics AS (
SELECT
extract(year from day) as year,
SUM(daily_amount) as year_amount
FROM sales
GROUP BY year)
SELECT
year,
year_amount,
LAG(year_amount) OVER (ORDER BY year) AS revenue_previous_year,
https://learnsql.com/blog/25-advanced-sql-query-examples/ 23/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
Code
It is used to calculate the difference (as a value) between the amount of the current
year and the previous year using the LAG() window function and ordering the
data by year.
Code
In the article How to Compute Year-Over-Year Differences in SQL, you can find
several examples of calculating year-to-year and month-to-month differences.
https://learnsql.com/blog/25-advanced-sql-query-examples/ 24/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
When we have this sort of organization, we can have a hierarchy of various levels.
In each row, the column manager_id refers to the row on the immediate upper
level in the hierarchy. In these cases, a frequent request is to obtain a list of all
employees reporting (directly or indirectly) to the CEO of the company (who, in this
case, has the employee_id of 110 ). The query to use is:
Code
UNION ALL
SELECT
e.employee_id,
e.first_name,
e.last_name,
e.manager_id
FROM employee e
JOIN subordinate s
ON e.manager_id = s.employee_id
)
SELECT
employee_id,
first_name,
last_name,
manager_id
FROM subordinate ;
In this query, we created a recursive CTE called subordinate . It’s the key part of
this query because it traverses the data hierarchy going from one row to the rows
in the hierarchy immediately below it.
There are two subqueries connected by a UNION ALL ; the first subquery returns the
top row of the hierarchy and the second query returns the next level, adding those
rows to the intermediate result of the query. Then the second subquery is executed
again to return the next level, which again will be added to the intermediate result
set. This process is repeated until no new rows are added to the intermediate
result.
https://learnsql.com/blog/25-advanced-sql-query-examples/ 25/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
Finally, the main query consumes the data in the subordinate CTE and returns
data in the way we expect. If you want to learn more about recursive queries in
SQL, I suggest the article How to Find All Employees Under Each Manager in SQL.
1 Jan 25 2023 51
2 Jan 26 2023 46
3 Jan 27 2023 41
4 Jan 30 2023 59
5 Jan 31 2023 73
6 Feb 1 2023 34
7 Feb 2 2023 56
8 Feb 4 2023 34
There are 3 different data series shown in different colors. We are looking for a
query to obtain the length of each data series. The first data series starts on Jan 25
and has a length of 3 elements, the second one starts on Jan 30 and its length is 4,
and so on.
Code
WITH data_series AS (
SELECT
RANK() OVER (ORDER BY day) AS row_number,
https://learnsql.com/blog/25-advanced-sql-query-examples/ 26/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
day,
day - RANK() OVER (ORDER BY day) AS series_id
FROM user_registration )
SELECT
MIN(day) AS series_start_day,
MAX(day) AS series_end_day,
MAX(day) - MIN (day) + 1 AS series_length
FROM data_series
GROUP BY series_id
ORDER BY series_start_date
In the previous query, the CTE has the column series_id , which is a value
intended to be used as an ID for the rows in the same data series. In the main
query, the GROUP BY series_id clause is used to aggregate rows of the same data
series. Then we can obtain the start of the series with MIN(day) and its end with
MAX(day) . The length of the series is calculated with the expression:
Code
If you want to go deeper with this topic, the article How to Calculate the Length of
a Series with SQL provides a detailed explanation of this technique.
If you like learning SQL using hands-on exercises, then you’ve got to
try All Forever SQL Package.
If you want to continue learning SQL, I suggest our advanced SQL courses:
Window Functions course, Recursive Queries, and GROUP BY Extensions in SQL.
All of them cover complex areas of the SQL language in simple words and with
https://learnsql.com/blog/25-advanced-sql-query-examples/ 27/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
plenty of examples. Discover how you practice advanced SQL with our platform.
Increase your skill and invest in yourself with SQL!
https://learnsql.com/blog/25-advanced-sql-query-examples/ 28/29
8/8/24, 11:45 AM 25 Advanced SQL Query Examples | LearnSQL.com
Email address
Subscribe
Quick links
Courses Blog
Pricing Cookbook
Assistance
Write to us
Follow us
Terms of service
Privacy policy
Imprint
https://learnsql.com/blog/25-advanced-sql-query-examples/ 29/29