The request

From Kevin Brooks, Operations Manager
Your goal
Keep the delivery_feed CTE from the starter. Parse delivered_text, which uses the formats '%Y-%m-%d', '%m/%d/%Y' and '%b %d, %Y', into delivered_on (a DATE, NULL when no format matches). Join to orders and return: order_id, ship_ref (the text 'ORD-', the order_id, '-', and the order's month and day as '%m%d', e.g. 'ORD-11500-1031'), order_day (order_date as a DATE), delivered_on, days_to_deliver (delivered_on minus order_day, in days) and needs_review (true when delivered_on is NULL). Sort by order_id.
- Read from the
orderstablenot done yet - Show exactly
order_idship_reforder_daydelivered_ondays_to_deliverneeds_reviewnot done yet - 9 rows, sorted as askednot done yet
Your query
Loading the editor…
Reference solution
| order_id | customer_id | order_date | status | channel |
|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace |
Mote checks your result
Press Run. Each row that matches the goal becomes a firefly in your lantern.
What Kevin gets, and why
Tables
Harbor & Pine store data, ready to query.
orders12,000 rows · 7 columns
order_idintegercustomer_idintegerorder_datetimestampstatusvarcharchannelvarcharshipping_feedecimalcoupon_codevarchar
| order_id | customer_id | order_date | status | channel | shipping_fee | coupon_code |
|---|---|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web | 0.00 | NULL |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web | 0.00 | NULL |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace | 4.99 | WELCOME10 |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace | 4.99 | WELCOME10 |
| 1005 | 82 | 2023-01-02 11:43:36 | completed | web | 0.00 | WELCOME10 |
customers2,018 rows · 9 columns
customer_idintegerfirst_namevarcharlast_namevarcharemailvarcharcityvarcharstatevarcharsignup_datedatesegmentvarcharreferral_sourcevarchar
| customer_id | first_name | last_name | city | state | signup_date | segment | referral_source | |
|---|---|---|---|---|---|---|---|---|
| 1 | Owen | Hudson | owen.hudson@icloud.com | San Francisco | CA | 2022-06-02 | consumer | search |
| 2 | Douglas | Mendoza | dmendoza69@gmail.com | Atlanta | GA | 2022-06-02 | consumer | NULL |
| 3 | Henry | Brooks | hbrooks@lakesidecafe.com | Los Angeles | CA | 2022-06-04 | business | NULL |
| 4 | Brian | Graham | bgraham@bluecedarhomes.com | Seattle | WA | 2022-06-06 | business | |
| 5 | Kyle | Jimenez | kjimenez27@yahoo.com | Indianapolis | IN | 2022-06-07 | consumer | NULL |
products120 rows · 8 columns
product_idintegernamevarcharcategoryvarcharsubcategoryvarcharunit_pricedecimalunit_costdecimallaunched_ondateis_activeboolean
| product_id | name | category | subcategory | unit_price | unit_cost | launched_on | is_active |
|---|---|---|---|---|---|---|---|
| 101 | Vale Velvet Loveseat | Furniture | Sofas | 1659.00 | NULL | 2023-09-11 | true |
| 102 | Alder Oak Coffee Table | Furniture | Tables | 919.00 | 503.50 | 2024-05-16 | true |
| 103 | Orchard Rattan Dining Chair | Furniture | Chairs | 789.00 | 429.87 | 2023-05-27 | true |
| 104 | Upland Oak Bookcase | Furniture | Storage | 429.00 | 214.70 | 2022-12-20 | true |
| 105 | Avery Leather Bed Frame | Furniture | Beds | 449.00 | 232.70 | 2021-08-31 | true |
order_items30,338 rows · 6 columns
order_item_idintegerorder_idintegerproduct_idintegerquantityintegerunit_pricedecimaldiscount_pctdecimal
| order_item_id | order_id | product_id | quantity | unit_price | discount_pct |
|---|---|---|---|---|---|
| 1 | 1001 | 121 | 9 | 31.99 | 0.00 |
| 2 | 1001 | 154 | 3 | 11.99 | 0.00 |
| 3 | 1002 | 154 | 4 | 11.99 | 0.00 |
| 4 | 1002 | 194 | 1 | 149.00 | 0.00 |
| 5 | 1002 | 193 | 2 | 19.99 | 0.00 |
returns1,673 rows · 5 columns
return_idintegerorder_item_idintegerreturned_ondatereasonvarcharrefund_amountdecimal
| return_id | order_item_id | returned_on | reason | refund_amount |
|---|---|---|---|---|
| 1 | 14 | 2023-01-10 | damaged | 83.69 |
| 2 | 42 | 2023-01-10 | wrong_item | 19.99 |
| 3 | 105 | 2023-01-14 | wrong_item | 10.99 |
| 4 | 24 | 2023-01-16 | changed_mind | 26.99 |
| 5 | 126 | 2023-01-16 | late | 20.99 |
employees60 rows · 8 columns
employee_idintegerfirst_namevarcharlast_namevarchardepartmentvarchartitlevarcharmanager_idintegerhire_datedatesalaryinteger
| employee_id | first_name | last_name | department | title | manager_id | hire_date | salary |
|---|---|---|---|---|---|---|---|
| 1 | Catherine | Hale | Executive | Chief Executive Officer | NULL | 2016-03-01 | 285000 |
| 2 | Robert | Kim | Executive | Chief Operating Officer | 1 | 2017-01-09 | 235000 |
| 3 | Megan | Collins | Sales | Head of Sales | 1 | 2018-04-16 | 172000 |
| 4 | Chloe | Gutierrez | Sales | Account Executive | 3 | 2020-01-21 | 72000 |
| 5 | Frank | Bell | Sales | Account Executive | 3 | 2023-05-21 | 73000 |
web_sessions40,000 rows · 7 columns
session_idintegercustomer_idintegerstarted_attimestampdevicevarcharsourcevarcharpages_viewedintegerconvertedboolean
| session_id | customer_id | started_at | device | source | pages_viewed | converted |
|---|---|---|---|---|---|---|
| 1 | NULL | 2023-01-01 23:02:24 | mobile | paid | 1 | false |
| 2 | 18 | 2023-01-01 15:07:49 | desktop | paid | 1 | false |
| 3 | NULL | 2023-01-01 16:55:46 | desktop | direct | 7 | false |
| 4 | NULL | 2023-01-01 12:43:28 | mobile | organic | 3 | false |
| 5 | NULL | 2023-01-01 13:06:21 | mobile | social | 2 | false |
marketing_spend3,276 rows · 3 columns
spend_datedatechannelvarcharamountdecimal
| spend_date | channel | amount |
|---|---|---|
| 2023-01-01 | paid | 492.99 |
| 2023-01-01 | 37.38 | |
| 2023-01-01 | social | 200.53 |
| 2023-01-02 | paid | 709.13 |
| 2023-01-02 | 64.13 |
The lesson
A date stored as text is just characters. Before you can subtract, group by month or compare, you have to parse it, and that means telling the database which format the text uses.
strptime and format codes
strptime(text, format) reads text into a TIMESTAMP using format codes:
| code | means | example |
|---|---|---|
%Y |
4-digit year | 2025 |
%m |
month number | 11 or 3 |
%d |
day of month | 05 or 6 |
%b |
short month name | Nov |
SELECT strptime('11/05/2025', '%m/%d/%Y'); -- 2025-11-05 00:00:00
DuckDB’s parser accepts single-digit months and days for %m and %d, so '11/7/2025' also works.
Several formats in one column
strptime raises an error when the text does not match. try_strptime returns NULL instead, which
lets you try formats one after another:
CAST(COALESCE(
try_strptime(delivered_text, '%Y-%m-%d'),
try_strptime(delivered_text, '%m/%d/%Y'),
try_strptime(delivered_text, '%b %d, %Y')
) AS DATE) AS delivered_on
DuckDB also takes a list: try_strptime(delivered_text, ['%Y-%m-%d', '%m/%d/%Y']). Whatever is still
NULL after the last format is your review list. In the sample, order 11500 was placed on Halloween,
2025-10-31, and delivered on 2025-11-04: 4 days.
Formatting dates back into text
strftime goes the other way, from a date to text in the format you choose. Reference codes often
embed a date: CONCAT('ORD-', order_id, '-', strftime(order_date, '%m%d')) gives ORD-11500-1031.
CONCAT turns the number into text for you.
Common mistakes
- Ambiguous day and month.
'05/11/2025'is May 11 in the US and November 5 in Europe. No query can tell which was meant. Ask the sender for their format, then parse each source with its own rule.'13/11/2025'fails as month/day, a hint that this depot writes the day first. - Comparing text dates.
'11/05/2025' < '2025-11-04'compares characters, not dates. - Dropping the failures. Filtering out NULL dates hides the deliveries that most need a follow-up.
Dialects and SQL Server
Postgres and Snowflake use TO_DATE(text, 'MM/DD/YYYY') (Snowflake also has TRY_TO_DATE), BigQuery
SAFE.PARSE_DATE('%m/%d/%Y', text), MySQL STR_TO_DATE. SQL Server uses style numbers: 101 is
mm/dd/yyyy, 103 is dd/mm/yyyy, 23 is yyyy-mm-dd. ISDATE and TRY_PARSE help with checks.
SELECT CONCAT('ORD-', o.order_id, '-', FORMAT(o.order_date, 'MMdd')) AS ship_ref,
COALESCE(TRY_CONVERT(date, f.delivered_text, 23),
TRY_CONVERT(date, f.delivered_text, 101)) AS delivered_on
FROM delivery_feed AS f
JOIN orders AS o ON o.order_id = f.order_id; Lantern lit!
Practised: date parsingstrptimestrftimecoalescedata cleaning
- Solved
- No hints
- First run
