SQL exercises with solutions: text and date functions
Updated on
Text and dates: UPPER, LOWER, LENGTH, SUBSTR, ||, LIKE, strftime, julianday, date. These 15 exercises range from level 2 to level 8; they use the syntax of SQLite, SpeedQL’s SQL engine.
Tip: In LIKE, % stands for any run of characters and _ for a single character.
Read the “Text and dates” card in the cheat sheet
Exercise 1
Show the name of the customers whose name starts with the letter C.
| id | name | city |
|---|---|---|
| 1 | Alice | Paris |
| 2 | Bruno | Lyon |
| 3 | Chloe | Paris |
| 4 | Dylan | Nantes |
Show the hint
Topics to use: WHERE (filters), Text and dates.
Query structure:
SELECT …
FROM …
WHERE … LIKE …Show the solution
SELECT name
FROM customers
WHERE name LIKE 'C%';Expected result (1 row):
| name |
|---|
| Chloe |
Exercise 2
Show the id of the invoices issued in March 2025.
| id | client | issued | due | amount | paid_on |
|---|---|---|---|---|---|
| 1 | Acme | 2025-01-15 | 2025-02-14 | 1200 | 2025-02-10 |
| 2 | Acme | 2025-03-01 | 2025-03-16 | 800 | NULL |
| 3 | Bolt | 2025-01-20 | 2025-03-06 | 450 | 2025-03-01 |
| 4 | Bolt | 2025-02-25 | 2025-03-27 | 950 | 2025-04-02 |
| 5 | Cyan | 2025-03-05 | 2025-05-04 | 300 | NULL |
| 6 | Cyan | 2024-12-10 | 2024-12-30 | 600 | 2025-01-05 |
Show the hint
Topics to use: WHERE (filters), Text and dates.
Query structure:
SELECT …
FROM …
WHERE STRFTIME(…, …) = …Show the solution
SELECT id
FROM invoices
WHERE strftime('%Y-%m', issued) = '2025-03';Expected result (2 rows):
| id |
|---|
| 2 |
| 5 |
Exercise 3
Show each invoice's id and the number of days between its issue date and its due date.
| id | client | issued | due | amount | paid_on |
|---|---|---|---|---|---|
| 1 | Acme | 2025-01-15 | 2025-02-14 | 1200 | 2025-02-10 |
| 2 | Acme | 2025-03-01 | 2025-03-16 | 800 | NULL |
| 3 | Bolt | 2025-01-20 | 2025-03-06 | 450 | 2025-03-01 |
| 4 | Bolt | 2025-02-25 | 2025-03-27 | 950 | 2025-04-02 |
| 5 | Cyan | 2025-03-05 | 2025-05-04 | 300 | NULL |
| 6 | Cyan | 2024-12-10 | 2024-12-30 | 600 | 2025-01-05 |
Show the hint
Topics to use: Text and dates.
Query structure:
SELECT …, CAST(JULIANDAY(…) - JULIANDAY(…) AS INTEGER)
FROM …Show the solution
SELECT id, CAST(julianday(due) - julianday(issued) AS INTEGER)
FROM invoices;Expected result (6 rows):
| id | CAST(julianday(due) - julianday(issued) AS INTEGER) |
|---|---|
| 1 | 30 |
| 2 | 15 |
| 3 | 45 |
| 4 | 30 |
| 5 | 60 |
| 6 | 20 |
Exercise 4
Show each contact's name in uppercase, then the length of that name.
| id | name | phone | city | |
|---|---|---|---|---|
| 1 | Alice Martin | alice@mail.com | 0612345678 | Paris |
| 2 | bob durand | NULL | 0698765432 | Lyon |
| 3 | CLAIRE ROUX | claire@work.org | NULL | NULL |
| 4 | David Lefevre | NULL | NULL | Nantes |
| 5 | Emma Petit | emma@mail.com | 0611223344 | NULL |
Show the hint
Topics to use: Text and dates.
Query structure:
SELECT UPPER(…), LENGTH(…)
FROM …Show the solution
SELECT UPPER(name), LENGTH(name)
FROM contacts;Expected result (5 rows):
| UPPER(name) | LENGTH(name) |
|---|---|
| ALICE MARTIN | 12 |
| BOB DURAND | 10 |
| CLAIRE ROUX | 11 |
| DAVID LEFEVRE | 13 |
| EMMA PETIT | 10 |
Exercise 5
Show the date of the matches played on a Sunday (strftime('%w', …) is '0' on Sundays).
| id | played_on | home_id | away_id | home_goals | away_goals |
|---|---|---|---|---|---|
| 1 | 2025-08-02 | 1 | 2 | 2 | 1 |
| 2 | 2025-08-03 | 3 | 4 | 0 | 0 |
| 3 | 2025-08-09 | 5 | 1 | 1 | 3 |
| 4 | 2025-08-10 | 2 | 3 | 2 | 2 |
| 5 | 2025-08-16 | 4 | 5 | 3 | 1 |
| 6 | 2025-08-17 | 1 | 3 | 1 | 0 |
| 7 | 2025-08-23 | 2 | 4 | 0 | 1 |
| 8 | 2025-08-24 | 3 | 5 | 2 | 3 |
| 9 | 2025-08-30 | 4 | 1 | 1 | 1 |
| 10 | 2025-08-31 | 5 | 2 | 0 | 2 |
Show the hint
Topics to use: WHERE (filters), Text and dates.
Query structure:
SELECT …
FROM …
WHERE STRFTIME(…, …) = …Show the solution
SELECT played_on
FROM matches
WHERE strftime('%w', played_on) = '0';Expected result (5 rows):
| played_on |
|---|
| 2025-08-03 |
| 2025-08-10 |
| 2025-08-17 |
| 2025-08-24 |
| 2025-08-31 |
Exercise 6
Show each student's name and their age in completed years on 2025-09-01 (a student has only gained a year once their birthday has passed).
| id | name | class | birth_date |
|---|---|---|---|
| 1 | Adele | A | 2009-03-14 |
| 2 | Bruno | A | 2008-11-02 |
| 3 | Cyril | B | 2009-07-21 |
| 4 | Dina | B | 2009-01-30 |
| 5 | Elsa | A | 2008-09-12 |
| 6 | Farah | C | 2009-05-05 |
| 7 | Gael | C | 2008-12-24 |
| 8 | Hana | B | 2009-10-10 |
Show the hint
Topics to use: Text and dates.
Query structure:
SELECT …, CAST(STRFTIME(…, …) AS INTEGER) - CAST(STRFTIME(…, …) AS INTEGER) - (STRFTIME(…, …) < STRFTIME(…, …))
FROM …Show the solution
SELECT name, CAST(strftime('%Y', '2025-09-01') AS INTEGER) - CAST(strftime('%Y', birth_date) AS INTEGER) - (strftime('%m-%d', '2025-09-01') < strftime('%m-%d', birth_date))
FROM students;Expected result (8 rows):
| name | CAST(strftime('%Y', '2025-09-01') AS INTEGER) - CAST(strftime('%Y', birth_date) AS INTEGER) - (strftime('%m-%d', '2025-09-01') < strftime('%m-%d', birth_date)) |
|---|---|
| Adele | 16 |
| Bruno | 16 |
| Cyril | 16 |
| Dina | 16 |
| Elsa | 16 |
| Farah | 16 |
| Gael | 16 |
| Hana | 15 |
Exercise 7
Show the id of each flight and its arrival time in the format YYYY-MM-DD HH:MM (departure time + duration).
| id | airline | origin | dest | departs | duration_min | price | seats_sold | capacity |
|---|---|---|---|---|---|---|---|---|
| 1 | SkyJet | CDG | MAD | 2025-06-01 08:10 | 125 | 89 | 150 | 180 |
| 2 | AirNova | CDG | LIS | 2025-06-01 11:40 | 155 | 120 | 160 | 170 |
| 3 | BlueWing | LYS | FCO | 2025-06-02 07:30 | 95 | 75 | 110 | 150 |
| 4 | SkyJet | MAD | CDG | 2025-06-02 18:20 | 120 | 95 | 170 | 180 |
| 5 | AirNova | BER | CDG | 2025-06-03 09:05 | 110 | 105 | 140 | 160 |
| 6 | BlueWing | NCE | BER | 2025-06-03 13:50 | 130 | 140 | 90 | 150 |
| 7 | SkyJet | CDG | FCO | 2025-06-04 06:45 | 135 | 99 | 175 | 180 |
| 8 | AirNova | LIS | MAD | 2025-06-04 16:15 | 75 | 65 | 60 | 120 |
| 9 | BlueWing | FCO | LYS | 2025-06-05 20:30 | 100 | 82 | 130 | 150 |
| 10 | SkyJet | CDG | BER | 2025-06-05 10:00 | 105 | 110 | 120 | 180 |
| 11 | AirNova | MAD | LIS | 2025-06-06 12:25 | 80 | 70 | 95 | 120 |
| 12 | BlueWing | LYS | MAD | 2025-06-06 15:40 | 115 | 79 | 100 | 150 |
Show the hint
Topics to use: Text and dates.
Query structure:
SELECT …, STRFTIME(…, …, … || … || …)
FROM …Show the solution
SELECT id, strftime('%Y-%m-%d %H:%M', departs, '+' || duration_min || ' minutes')
FROM flights;Expected result (12 rows):
| id | strftime('%Y-%m-%d %H:%M', departs, '+' || duration_min || ' minutes') |
|---|---|
| 1 | 2025-06-01 10:15 |
| 2 | 2025-06-01 14:15 |
| 3 | 2025-06-02 09:05 |
| 4 | 2025-06-02 20:20 |
| 5 | 2025-06-03 10:55 |
| 6 | 2025-06-03 16:00 |
| 7 | 2025-06-04 09:00 |
| 8 | 2025-06-04 17:30 |
| 9 | 2025-06-05 22:10 |
| 10 | 2025-06-05 11:45 |
| 11 | 2025-06-06 13:45 |
| 12 | 2025-06-06 17:35 |
Exercise 8
Show the id and a column called route in the format ORIGIN-DESTINATION (for example CDG-MAD) of the flights of the airlines whose name starts with 'Air' or 'Blue' (columns: id, route).
| id | airline | origin | dest | departs | duration_min | price | seats_sold | capacity |
|---|---|---|---|---|---|---|---|---|
| 1 | SkyJet | CDG | MAD | 2025-06-01 08:10 | 125 | 89 | 150 | 180 |
| 2 | AirNova | CDG | LIS | 2025-06-01 11:40 | 155 | 120 | 160 | 170 |
| 3 | BlueWing | LYS | FCO | 2025-06-02 07:30 | 95 | 75 | 110 | 150 |
| 4 | SkyJet | MAD | CDG | 2025-06-02 18:20 | 120 | 95 | 170 | 180 |
| 5 | AirNova | BER | CDG | 2025-06-03 09:05 | 110 | 105 | 140 | 160 |
| 6 | BlueWing | NCE | BER | 2025-06-03 13:50 | 130 | 140 | 90 | 150 |
| 7 | SkyJet | CDG | FCO | 2025-06-04 06:45 | 135 | 99 | 175 | 180 |
| 8 | AirNova | LIS | MAD | 2025-06-04 16:15 | 75 | 65 | 60 | 120 |
| 9 | BlueWing | FCO | LYS | 2025-06-05 20:30 | 100 | 82 | 130 | 150 |
| 10 | SkyJet | CDG | BER | 2025-06-05 10:00 | 105 | 110 | 120 | 180 |
| 11 | AirNova | MAD | LIS | 2025-06-06 12:25 | 80 | 70 | 95 | 120 |
| 12 | BlueWing | LYS | MAD | 2025-06-06 15:40 | 115 | 79 | 100 | 150 |
Show the hint
Topics to use: WHERE (filters), Text and dates.
Query structure:
SELECT …, … || … || … AS …
FROM …
WHERE … LIKE … OR … LIKE …Show the solution
SELECT id, origin || '-' || dest AS route
FROM flights
WHERE airline LIKE 'Air%' OR airline LIKE 'Blue%';Expected result (8 rows):
| id | route |
|---|---|
| 2 | CDG-LIS |
| 3 | LYS-FCO |
| 5 | BER-CDG |
| 6 | NCE-BER |
| 8 | LIS-MAD |
| 9 | FCO-LYS |
| 11 | MAD-LIS |
| 12 | LYS-MAD |
Exercise 9
Show the title of each song and its duration in minutes:seconds format, always with 2 digits for the seconds (for example 3:34 or 4:01), in a column called mmss (columns: title, mmss).
| id | title | artist_id | genre | duration_s | released |
|---|---|---|---|---|---|
| 1 | Glass Heart | 1 | Pop | 214 | 2019-04-12 |
| 2 | Low Tide | 1 | Pop | 198 | 2021-06-01 |
| 3 | Rust | 2 | Rock | 256 | 2008-09-30 |
| 4 | Wires | 2 | Rock | 301 | 2015-02-14 |
| 5 | Sunday Market | 3 | Afrobeat | 233 | 2020-11-20 |
| 6 | Palm Wine | 3 | Afrobeat | 245 | 2022-03-03 |
| 7 | Alma | 4 | Latin | 189 | 2013-07-07 |
| 8 | Brisa | 4 | Pop | 205 | 2018-05-25 |
| 9 | Night Drive | 5 | Electro | 276 | 2021-10-10 |
| 10 | Pulse | 5 | Electro | 230 | 2023-01-15 |
Show the hint
Topics to use: Text and dates.
Query structure:
SELECT …, (… / …) || … || PRINTF(…, … % …) AS …
FROM …Show the solution
SELECT title, (duration_s / 60) || ':' || printf('%02d', duration_s % 60) AS mmss
FROM songs;Expected result (10 rows):
| title | mmss |
|---|---|
| Glass Heart | 3:34 |
| Low Tide | 3:18 |
| Rust | 4:16 |
| Wires | 5:01 |
| Sunday Market | 3:53 |
| Palm Wine | 4:05 |
| Alma | 3:09 |
| Brisa | 3:25 |
| Night Drive | 4:36 |
| Pulse | 3:50 |
Exercise 10
Show the city, the day and, in a column called weekday, the day of the week as a 3-letter English abbreviation (Sun, Mon, Tue, Wed, Thu, Fri, Sat), for the readings whose maximum temperature is above 25 degrees (columns: city, day, weekday).
| id | city | day | temp_max | temp_min | rain_mm |
|---|---|---|---|---|---|
| 1 | Paris | 2025-07-01 | 24 | 15 | 0 |
| 2 | Paris | 2025-07-02 | 27 | 17 | 0 |
| 3 | Paris | 2025-07-03 | 22 | 16 | 4.5 |
| 4 | Paris | 2025-07-04 | 19 | 14 | 12 |
| 5 | Paris | 2025-07-05 | 23 | 13 | 0 |
| 6 | Lyon | 2025-07-01 | 26 | 16 | 0 |
| 7 | Lyon | 2025-07-02 | 29 | 18 | 0 |
| 8 | Lyon | 2025-07-03 | 31 | 19 | 0 |
| 9 | Lyon | 2025-07-04 | 24 | 17 | 8.5 |
| 10 | Lyon | 2025-07-05 | 22 | 15 | 3 |
| 11 | Marseille | 2025-07-01 | 30 | 21 | 0 |
| 12 | Marseille | 2025-07-02 | 32 | 22 | 0 |
| 13 | Marseille | 2025-07-03 | 33 | 23 | 0 |
| 14 | Marseille | 2025-07-04 | 29 | 21 | 0 |
| 15 | Marseille | 2025-07-05 | 28 | 20 | 0 |
Show the hint
Topics to use: WHERE (filters), Text and dates.
Query structure:
SELECT …, …, SUBSTR(…, … + … * CAST(STRFTIME(…, …) AS INTEGER), …) AS …
FROM …
WHERE … > …Show the solution
SELECT city, day, substr('SunMonTueWedThuFriSat', 1 + 3 * CAST(strftime('%w', day) AS INTEGER), 3) AS weekday
FROM readings
WHERE temp_max > 25;Expected result (9 rows):
| city | day | weekday |
|---|---|---|
| Paris | 2025-07-02 | Wed |
| Lyon | 2025-07-01 | Tue |
| Lyon | 2025-07-02 | Wed |
| Lyon | 2025-07-03 | Thu |
| Marseille | 2025-07-01 | Tue |
| Marseille | 2025-07-02 | Wed |
| Marseille | 2025-07-03 | Thu |
| Marseille | 2025-07-04 | Fri |
| Marseille | 2025-07-05 | Sat |
Exercise 11
Show the id of each booking and its number of nights (difference between check_out and check_in, in whole days).
| id | room_id | guest_id | check_in | check_out |
|---|---|---|---|---|
| 1 | 2 | 1 | 2025-07-01 | 2025-07-04 |
| 2 | 5 | 2 | 2025-07-02 | 2025-07-05 |
| 3 | 8 | 3 | 2025-07-03 | 2025-07-06 |
| 4 | 3 | 4 | 2025-07-05 | 2025-07-12 |
| 5 | 6 | 5 | 2025-07-06 | 2025-07-08 |
| 6 | 1 | 2 | 2025-07-08 | 2025-07-10 |
| 7 | 9 | 3 | 2025-07-10 | 2025-07-11 |
| 8 | 7 | 5 | 2025-07-11 | 2025-07-15 |
| 9 | 4 | 1 | 2025-07-14 | 2025-07-16 |
| 10 | 6 | 4 | 2025-07-15 | 2025-07-19 |
| 11 | 6 | 5 | 2025-07-16 | 2025-07-18 |
Show the hint
Topics to use: Text and dates.
Query structure:
SELECT …, CAST(JULIANDAY(…) - JULIANDAY(…) AS INTEGER)
FROM …Show the solution
SELECT id, CAST(julianday(check_out) - julianday(check_in) AS INTEGER)
FROM bookings;Expected result (11 rows):
| id | CAST(julianday(check_out) - julianday(check_in) AS INTEGER) |
|---|---|
| 1 | 3 |
| 2 | 3 |
| 3 | 3 |
| 4 | 7 |
| 5 | 2 |
| 6 | 2 |
| 7 | 1 |
| 8 | 4 |
| 9 | 2 |
| 10 | 4 |
| 11 | 2 |
Exercise 12
For each month (format YYYY-MM, using strftime), show the month, the number of transactions and the sum of the amounts.
| id | account_id | made_on | amount | label |
|---|---|---|---|---|
| 1 | 1 | 2025-01-02 | 2500 | salary |
| 2 | 1 | 2025-01-05 | -60 | groceries |
| 3 | 1 | 2025-01-12 | -800 | rent |
| 4 | 2 | 2025-01-15 | 500 | transfer |
| 5 | 3 | 2025-01-03 | 1900 | salary |
| 6 | 3 | 2025-01-20 | -120 | groceries |
| 7 | 3 | 2025-02-01 | -950 | rent |
| 8 | 4 | 2025-01-25 | 2100 | salary |
| 9 | 4 | 2025-02-03 | -45 | restaurant |
| 10 | 1 | 2025-02-02 | 2600 | salary |
| 11 | 1 | 2025-02-06 | -75 | groceries |
| 12 | 5 | 2025-02-10 | 30 | interest |
| 13 | 2 | 2025-02-15 | 500 | transfer |
| 14 | 4 | 2025-02-18 | -600 | rent |
Show the hint
Topics to use: COUNT, SUM, AVG, MIN, MAX, GROUP BY, Text and dates.
Query structure:
SELECT STRFTIME(…, …), COUNT(*), SUM(…)
FROM …
GROUP BY STRFTIME(…, …)Show the solution
SELECT strftime('%Y-%m', made_on), COUNT(*), SUM(amount)
FROM transactions
GROUP BY strftime('%Y-%m', made_on);Expected result (2 rows):
| strftime('%Y-%m', made_on) | COUNT(*) | SUM(amount) |
|---|---|---|
| 2025-01 | 7 | 6020 |
| 2025-02 | 7 | 1460 |
Exercise 13
Show the possible connections (columns: flight 1 id, flight 2 id): flight 2 departs from the arrival airport of flight 1, between 0 and 48 hours after flight 1 departs.
| id | airline | origin | dest | departs | duration_min | price | seats_sold | capacity |
|---|---|---|---|---|---|---|---|---|
| 1 | SkyJet | CDG | MAD | 2025-06-01 08:10 | 125 | 89 | 150 | 180 |
| 2 | AirNova | CDG | LIS | 2025-06-01 11:40 | 155 | 120 | 160 | 170 |
| 3 | BlueWing | LYS | FCO | 2025-06-02 07:30 | 95 | 75 | 110 | 150 |
| 4 | SkyJet | MAD | CDG | 2025-06-02 18:20 | 120 | 95 | 170 | 180 |
| 5 | AirNova | BER | CDG | 2025-06-03 09:05 | 110 | 105 | 140 | 160 |
| 6 | BlueWing | NCE | BER | 2025-06-03 13:50 | 130 | 140 | 90 | 150 |
| 7 | SkyJet | CDG | FCO | 2025-06-04 06:45 | 135 | 99 | 175 | 180 |
| 8 | AirNova | LIS | MAD | 2025-06-04 16:15 | 75 | 65 | 60 | 120 |
| 9 | BlueWing | FCO | LYS | 2025-06-05 20:30 | 100 | 82 | 130 | 150 |
| 10 | SkyJet | CDG | BER | 2025-06-05 10:00 | 105 | 110 | 120 | 180 |
| 11 | AirNova | MAD | LIS | 2025-06-06 12:25 | 80 | 70 | 95 | 120 |
| 12 | BlueWing | LYS | MAD | 2025-06-06 15:40 | 115 | 79 | 100 | 150 |
Show the hint
Topics to use: JOIN, Text and dates.
Query structure:
SELECT …, …
FROM … …
JOIN … … ON … = … AND JULIANDAY(…) - JULIANDAY(…) BETWEEN … AND …Show the solution
SELECT f.id, g.id
FROM flights f
JOIN flights g ON g.origin = f.dest AND julianday(g.departs) - julianday(f.departs) BETWEEN 0 AND 2;Expected result (6 rows):
| id | id |
|---|---|
| 1 | 4 |
| 4 | 7 |
| 5 | 7 |
| 7 | 9 |
| 8 | 11 |
| 9 | 12 |
Exercise 14
Show the name of the guests who stayed in at least 2 different cities, with the number of cities and their total number of nights.
| id | name | country |
|---|---|---|
| 1 | Ana Silva | Portugal |
| 2 | Ben Ford | USA |
| 3 | Chen Li | China |
| 4 | Dana Weiss | Germany |
| 5 | Emma Roy | France |
| 6 | Farid Nasser | Morocco |
| id | room_id | guest_id | check_in | check_out |
|---|---|---|---|---|
| 1 | 2 | 1 | 2025-07-01 | 2025-07-04 |
| 2 | 5 | 2 | 2025-07-02 | 2025-07-05 |
| 3 | 8 | 3 | 2025-07-03 | 2025-07-06 |
| 4 | 3 | 4 | 2025-07-05 | 2025-07-12 |
| 5 | 6 | 5 | 2025-07-06 | 2025-07-08 |
| 6 | 1 | 2 | 2025-07-08 | 2025-07-10 |
| 7 | 9 | 3 | 2025-07-10 | 2025-07-11 |
| 8 | 7 | 5 | 2025-07-11 | 2025-07-15 |
| 9 | 4 | 1 | 2025-07-14 | 2025-07-16 |
| 10 | 6 | 4 | 2025-07-15 | 2025-07-19 |
| 11 | 6 | 5 | 2025-07-16 | 2025-07-18 |
| id | hotel_id | type | price |
|---|---|---|---|
| 1 | 1 | single | 70 |
| 2 | 1 | double | 95 |
| 3 | 2 | double | 130 |
| 4 | 2 | suite | 210 |
| 5 | 3 | single | 110 |
| 6 | 3 | double | 150 |
| 7 | 4 | double | 65 |
| 8 | 5 | double | 260 |
| 9 | 5 | suite | 480 |
| 10 | 5 | single | 190 |
| id | name | city | stars |
|---|---|---|---|
| 1 | Seaside Inn | Nice | 3 |
| 2 | Alpine Lodge | Annecy | 4 |
| 3 | City Loft | Paris | 4 |
| 4 | Old Mill | Bordeaux | 2 |
| 5 | Grand Palace | Paris | 5 |
Show the hint
Topics to use: COUNT, SUM, AVG, MIN, MAX, GROUP BY, JOIN, HAVING, Text and dates.
Query structure:
SELECT …, COUNT(DISTINCT …), SUM(CAST(JULIANDAY(…) - JULIANDAY(…) AS INTEGER))
FROM … …
JOIN … … ON … = …
JOIN … … ON … = …
JOIN … … ON … = …
GROUP BY …
HAVING COUNT(DISTINCT …) >= …Show the solution
SELECT g.name, COUNT(DISTINCT h.city), SUM(CAST(julianday(b.check_out) - julianday(b.check_in) AS INTEGER))
FROM guests g
JOIN bookings b ON b.guest_id = g.id
JOIN rooms r ON r.id = b.room_id
JOIN hotels h ON h.id = r.hotel_id
GROUP BY g.id
HAVING COUNT(DISTINCT h.city) >= 2;Expected result (4 rows):
| name | COUNT(DISTINCT h.city) | SUM(CAST(julianday(b.check_out) - julianday(b.check_in) AS INTEGER)) |
|---|---|---|
| Ana Silva | 2 | 5 |
| Ben Ford | 2 | 5 |
| Dana Weiss | 2 | 11 |
| Emma Roy | 2 | 8 |
Exercise 15
Show the name, signup date, first order date and delay in days of the customers whose first order came less than 300 days after they signed up.
| id | name | city | signup |
|---|---|---|---|
| 1 | Alba | Paris | 2024-01-10 |
| 2 | Boris | Lyon | 2024-02-15 |
| 3 | Carla | Paris | 2024-03-01 |
| 4 | Denis | Nantes | 2024-05-20 |
| 5 | Eva | Lyon | 2024-06-30 |
| 6 | Fabio | Lille | 2024-08-08 |
| id | customer_id | order_date | status |
|---|---|---|---|
| 1 | 1 | 2025-01-05 | shipped |
| 2 | 2 | 2025-01-12 | shipped |
| 3 | 1 | 2025-02-03 | paid |
| 4 | 3 | 2025-02-10 | cancelled |
| 5 | 4 | 2025-02-20 | shipped |
| 6 | 5 | 2025-03-02 | shipped |
| 7 | 2 | 2025-03-15 | paid |
| 8 | 3 | 2025-03-28 | shipped |
Show the hint
Topics to use: COUNT, SUM, AVG, MIN, MAX, GROUP BY, JOIN, HAVING, Text and dates.
Query structure:
SELECT …, …, MIN(…), CAST(JULIANDAY(MIN(…)) - JULIANDAY(…) AS INTEGER)
FROM … …
JOIN … … ON … = …
GROUP BY …
HAVING JULIANDAY(MIN(…)) - JULIANDAY(…) < …Show the solution
SELECT c.name, c.signup, MIN(o.order_date), CAST(julianday(MIN(o.order_date)) - julianday(c.signup) AS INTEGER)
FROM customers c
JOIN orders o ON o.customer_id = c.id
GROUP BY c.id
HAVING julianday(MIN(o.order_date)) - julianday(c.signup) < 300;Expected result (2 rows):
| name | signup | MIN(o.order_date) | CAST(julianday(MIN(o.order_date)) - julianday(c.signup) AS INTEGER) |
|---|---|---|---|
| Denis | 2024-05-20 | 2025-02-20 | 276 |
| Eva | 2024-06-30 | 2025-03-02 | 245 |
Practice with automatic checking
In SpeedQL, you write your query and it is checked straight away, on these tables and then on a hidden dataset.