The request

From Jessica Turner, Merchandising Manager
Your goal
Step 1 of 3. Create lighting_feed (sku INTEGER, product_name VARCHAR, category VARCHAR, unit_cost DECIMAL(10, 2), list_price DECIMAL(10, 2)). Load every Lighting product from products with INSERT ... SELECT (product_id, name, category, unit_cost, unit_price), keep the starter's three supplier rows, then profile the feed: return category, feed_rows (row count), with_cost (rows with a unit_cost) and with_price (rows with a list_price) per category value exactly as stored, sorted by category.
- Read from the
productstablenot done yet - Show exactly
categoryfeed_rowswith_costwith_pricenot done yet - 3 rows, sorted as askednot done yet
Step 2 of 3. Starting from the loaded feed (in the starter), clean it: set category to 'Lighting' on every row whose trimmed, lowercased category is 'lighting', and delete the rows without a list_price. Return every remaining row with sku, product_name, category, unit_cost and list_price, sorted by sku.
- Read from the
productstablenot done yet - Show exactly
skuproduct_namecategoryunit_costlist_pricenot done yet - 22 rows, sorted as askednot done yet
Step 3 of 3. Starting from the cleaned feed (in the starter), create a table lighting_summary with one row per tier ('premium' when list_price >= 300, otherwise 'core'): tier, products (rows), with_cost (rows with a unit_cost), avg_price (average list_price, rounded to 2) and avg_margin_pct (average of (list_price - unit_cost) / list_price times 100 over rows with a cost, rounded to 1). Then return every column of lighting_summary plus rows_match (true when the summary's products add up to the number of rows in lighting_feed), sorted by tier.
- Read from the
productstablenot done yet - Show exactly
tierproductswith_costavg_priceavg_margin_pctrows_matchnot done yet - 2 rows, sorted as askednot done yet
Step 1 · your query
Loading the editor…
Reference solution
| product_id | name | category | subcategory | unit_price |
|---|---|---|---|---|
| 101 | Vale Velvet Loveseat | Furniture | Sofas | 1659.00 |
| 102 | Alder Oak Coffee Table | Furniture | Tables | 919.00 |
| 103 | Orchard Rattan Dining Chair | Furniture | Chairs | 789.00 |
| 104 | Upland Oak Bookcase | Furniture | Storage | 429.00 |
Mote checks your result
Press Run. Each row that matches the goal becomes a firefly in your lantern.
Step 2 · your query
Loading the editor…
Reference solution
| product_id | name | category | subcategory | unit_price |
|---|---|---|---|---|
| 101 | Vale Velvet Loveseat | Furniture | Sofas | 1659.00 |
| 102 | Alder Oak Coffee Table | Furniture | Tables | 919.00 |
| 103 | Orchard Rattan Dining Chair | Furniture | Chairs | 789.00 |
| 104 | Upland Oak Bookcase | Furniture | Storage | 429.00 |
Mote checks your result
Press Run. Each row that matches the goal becomes a firefly in your lantern.
Step 3 · your query
Loading the editor…
Reference solution
| product_id | name | category | subcategory | unit_price |
|---|---|---|---|---|
| 101 | Vale Velvet Loveseat | Furniture | Sofas | 1659.00 |
| 102 | Alder Oak Coffee Table | Furniture | Tables | 919.00 |
| 103 | Orchard Rattan Dining Chair | Furniture | Chairs | 789.00 |
| 104 | Upland Oak Bookcase | Furniture | Storage | 429.00 |
Mote checks your result
Press Run. Each row that matches the goal becomes a firefly in your lantern.
What Jessica gets, and why
Tables
Harbor & Pine store data, ready to query.
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 |
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 |
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 |
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
This boss runs a small import end to end: the kind of job where a supplier’s list joins your catalog and someone downstream relies on the result. Because every run starts fresh, each step repeats the earlier steps’ statements (the starters include them) and adds its own.
Step 1: load and profile
An INSERT can take its rows from a query instead of a VALUES list:
INSERT INTO lighting_feed
SELECT product_id, name, category, unit_cost, unit_price
FROM products
WHERE category = 'Lighting';
The columns are matched by position, so the SELECT must list them in the table’s order. The
supplier’s three rows follow as VALUES.
Then profile before you clean. Grouping by the raw category shows what actually arrived: 21 rows say
Lighting, one says LIGHTING and one says ' lighting ' with spaces. Counting unit_cost and
list_price per group shows one row without a cost (the Calder Arc Floor Lamp) and one without a price
(the Wren Ceramic Table Lamp). A profile like this is your list of cleaning jobs.
Step 2: clean with UPDATE and DELETE
UPDATE lighting_feed SET category = 'Lighting' WHERE LOWER(TRIM(category)) = 'lighting';
DELETE FROM lighting_feed WHERE list_price IS NULL;
The UPDATE’s WHERE normalizes before comparing, so one condition catches every spelling. The DELETE removes the product that cannot be sold yet. The product without a cost stays: it has a price, so it belongs in the catalog, and its margin is simply unknown. Deleting it would also understate the premium tier. The cleaned feed has 22 rows.
Step 3: publish a summary with a check
CREATE TABLE lighting_summary AS
SELECT CASE WHEN list_price >= 300 THEN 'premium' ELSE 'core' END AS tier,
COUNT(*) AS products,
COUNT(unit_cost) AS with_cost,
ROUND(AVG(list_price), 2) AS avg_price,
ROUND(100 * AVG((list_price - unit_cost) / list_price), 1) AS avg_margin_pct
FROM lighting_feed
GROUP BY 1;
AVG skips NULLs, so the margin average covers the 5 premium products that have a cost while
products still counts all 6. Showing with_cost next to the margin tells the reader how many rows
the margin rests on. Core products average $135.81 with a 56.4% margin; premium ones average $407.33
with 53.9%.
The final query adds rows_match: the summary’s product counts must add up to the feed. If a tier
CASE had a gap (say > 300 and < 300, losing a product at exactly $300), the check would turn false.
Common mistakes
- Listing INSERT … SELECT columns in a different order from the table, which silently puts names in the category column or costs in the price column.
- Cleaning before profiling, and fixing only the spellings you happened to notice.
- Deleting rows with a NULL cost: they are real products.
- Publishing a summary without a check that it covers every row.
In SQL Server
INSERT INTO ... SELECT and UPDATE/DELETE are the same in T-SQL. The summary would be built with
SELECT ... INTO dbo.LightingSummary FROM ..., and the check written as a CASE WHEN ... THEN 1 ELSE 0 END column, since T-SQL has no boolean column type. A real supplier file would come in through the SSMS
Import Flat File wizard; in DuckDB it is INSERT INTO lighting_feed SELECT * FROM read_csv('feed.csv').
Lantern lit!
Practised: create tableinsert selectupdatedeletecreate table asvalidationanalyst workflow
- Solved
- No hints
- First run
