lesson 88 Warehouse & reporting projects · advanced
Capstone: integrated customers to trusted sales
Combine integration and quality checks into trusted sales reporting.
the idea
The capstone chains the warehouse steps: integrate CRM and ERP customers under the contract (lesson 77), build quality-gated sales from Bronze (lesson 71), then report trusted sales at the integrated customer grain. Each step is materialized so it can be inspected and reconciled.
Dataset: Retail
The tables the challenges use, with their first 6 rows. Under each column name: the kind of value it holds, and which table it links to.
country_codes2 columns · 26 rows
| codetext · unique id | countrytext |
|---|---|
| AE | United Arab Emirates |
| AT | Austria |
| AU | Australia |
| BE | Belgium |
| BH | Bahrain |
| BR | Brazil |
- codetext · unique id
- AE
- countrytext
- United Arab Emirates
- codetext · unique id
- AT
- countrytext
- Austria
- codetext · unique id
- AU
- countrytext
- Australia
- codetext · unique id
- BE
- countrytext
- Belgium
- codetext · unique id
- BH
- countrytext
- Bahrain
- codetext · unique id
- BR
- countrytext
- Brazil
first 6 of 26 rows · your queries use all of them
dim_customer5 columns · 848 rows
| customer_keynumber · unique id | customer_idtext | contact_nametext | countrytext | regiontext |
|---|---|---|---|---|
| 1 | 12347 | Arthur Turner | Iceland | Europe |
| 2 | 12353 | Harry Turner | Bahrain | Rest of World |
| 3 | 12359 | Theo Turner | Cyprus | Europe |
| 4 | 12373 | Harry Hill | Austria | Europe |
| 5 | 12374 | Emily Hill | Austria | Europe |
| 6 | 12384 | Ava Moore | Switzerland | Europe |
- customer_keynumber · unique id
- 1
- customer_idtext
- 12347
- contact_nametext
- Arthur Turner
- countrytext
- Iceland
- regiontext
- Europe
- customer_keynumber · unique id
- 2
- customer_idtext
- 12353
- contact_nametext
- Harry Turner
- countrytext
- Bahrain
- regiontext
- Rest of World
- customer_keynumber · unique id
- 3
- customer_idtext
- 12359
- contact_nametext
- Theo Turner
- countrytext
- Cyprus
- regiontext
- Europe
- customer_keynumber · unique id
- 4
- customer_idtext
- 12373
- contact_nametext
- Harry Hill
- countrytext
- Austria
- regiontext
- Europe
- customer_keynumber · unique id
- 5
- customer_idtext
- 12374
- contact_nametext
- Emily Hill
- countrytext
- Austria
- regiontext
- Europe
- customer_keynumber · unique id
- 6
- customer_idtext
- 12384
- contact_nametext
- Ava Moore
- countrytext
- Switzerland
- regiontext
- Europe
first 6 of 848 rows · your queries use all of them
dim_product5 columns · 4,902 rows
| product_keynumber · unique id | product_idtext | descriptiontext | categorytext | pricedecimal |
|---|---|---|---|---|
| 1 | 10002 | INFLATABLE POLITICAL GLOBE | Other Gifts | 0.85 |
| 2 | 10002R | ROBOT PENCIL SHARPNER | Stationery & Gift Wrap | 5.17 |
| 3 | 10080 | GROOVY CACTUS INFLATABLE | Other Gifts | 0.39 |
| 4 | 10109 | BENDY COLOUR PENCILS | Stationery & Gift Wrap | 0.42 |
| 5 | 10120 | DOGGY RUBBER | Other Gifts | 0.21 |
| 6 | 10123C | HEARTS WRAPPING TAPE | Stationery & Gift Wrap | 0.65 |
- product_keynumber · unique id
- 1
- product_idtext
- 10002
- descriptiontext
- INFLATABLE POLITICAL GLOBE
- categorytext
- Other Gifts
- pricedecimal
- 0.85
- product_keynumber · unique id
- 2
- product_idtext
- 10002R
- descriptiontext
- ROBOT PENCIL SHARPNER
- categorytext
- Stationery & Gift Wrap
- pricedecimal
- 5.17
- product_keynumber · unique id
- 3
- product_idtext
- 10080
- descriptiontext
- GROOVY CACTUS INFLATABLE
- categorytext
- Other Gifts
- pricedecimal
- 0.39
- product_keynumber · unique id
- 4
- product_idtext
- 10109
- descriptiontext
- BENDY COLOUR PENCILS
- categorytext
- Stationery & Gift Wrap
- pricedecimal
- 0.42
- product_keynumber · unique id
- 5
- product_idtext
- 10120
- descriptiontext
- DOGGY RUBBER
- categorytext
- Other Gifts
- pricedecimal
- 0.21
- product_keynumber · unique id
- 6
- product_idtext
- 10123C
- descriptiontext
- HEARTS WRAPPING TAPE
- categorytext
- Stationery & Gift Wrap
- pricedecimal
- 0.65
first 6 of 4,902 rows · your queries use all of them
bronze_sales6 columns · 2,526 rows
| row_idnumber · unique id | customer_idtext | product_idtext | quantitytext | salestext | order_datedate |
|---|---|---|---|---|---|
| 123027 | 15687 | 23301 | 24 | 39.60 | 2011-12-01 |
| 123028 | 15687 | 22623 | 3 | 17.85 | 2011-12-01 |
| 123029 | 15687 | 20970 | 4 | 15.00 | 2011-12-01 |
| 123030 | 15687 | 23562 | 6 | 17.34 | 2011-12-01 |
| 123031 | 15687 | 71477 | 4 | 15.80 | 2011-12-01 |
| 123032 | 15687 | 23407 | 2 | 19.90 | 2011-12-01 |
- row_idnumber · unique id
- 123027
- customer_idtext
- 15687
- product_idtext
- 23301
- quantitytext
- 24
- salestext
- 39.60
- order_datedate
- 2011-12-01
- row_idnumber · unique id
- 123028
- customer_idtext
- 15687
- product_idtext
- 22623
- quantitytext
- 3
- salestext
- 17.85
- order_datedate
- 2011-12-01
- row_idnumber · unique id
- 123029
- customer_idtext
- 15687
- product_idtext
- 20970
- quantitytext
- 4
- salestext
- 15.00
- order_datedate
- 2011-12-01
- row_idnumber · unique id
- 123030
- customer_idtext
- 15687
- product_idtext
- 23562
- quantitytext
- 6
- salestext
- 17.34
- order_datedate
- 2011-12-01
- row_idnumber · unique id
- 123031
- customer_idtext
- 15687
- product_idtext
- 71477
- quantitytext
- 4
- salestext
- 15.80
- order_datedate
- 2011-12-01
- row_idnumber · unique id
- 123032
- customer_idtext
- 15687
- product_idtext
- 23407
- quantitytext
- 2
- salestext
- 19.90
- order_datedate
- 2011-12-01
first 6 of 2,526 rows · your queries use all of them
crm_people5 columns · 1,707 rows
| source_rownumber · unique id | customer_idtext | contact_nametext | gendertext | updated_atdate & time |
|---|---|---|---|---|
| 1 | 12347 | Arthur Turner | n/a | 2010-10-31 10:00:00 |
| 2 | 12347 | Arthur Turner | n/a | 2010-11-01 10:00:00 |
| 3 | 12353 | Harry Turner | M | 2010-10-27 10:00:00 |
| 4 | 12359 | Theo Turner | M | 2009-12-05 10:00:00 |
| 5 | 12359 | Theo Turner | M | 2010-01-01 10:00:00 |
| 6 | 12359 | Theo Turner | M | 2011-11-01 10:00:00 |
- source_rownumber · unique id
- 1
- customer_idtext
- 12347
- contact_nametext
- Arthur Turner
- gendertext
- n/a
- updated_atdate & time
- 2010-10-31 10:00:00
- source_rownumber · unique id
- 2
- customer_idtext
- 12347
- contact_nametext
- Arthur Turner
- gendertext
- n/a
- updated_atdate & time
- 2010-11-01 10:00:00
- source_rownumber · unique id
- 3
- customer_idtext
- 12353
- contact_nametext
- Harry Turner
- gendertext
- M
- updated_atdate & time
- 2010-10-27 10:00:00
- source_rownumber · unique id
- 4
- customer_idtext
- 12359
- contact_nametext
- Theo Turner
- gendertext
- M
- updated_atdate & time
- 2009-12-05 10:00:00
- source_rownumber · unique id
- 5
- customer_idtext
- 12359
- contact_nametext
- Theo Turner
- gendertext
- M
- updated_atdate & time
- 2010-01-01 10:00:00
- source_rownumber · unique id
- 6
- customer_idtext
- 12359
- contact_nametext
- Theo Turner
- gendertext
- M
- updated_atdate & time
- 2011-11-01 10:00:00
first 6 of 1,707 rows · your queries use all of them
erp_people3 columns · 848 rows
| erp_idtext · unique id | gendertext | birth_datedate |
|---|---|---|
| 12389 | female | 1978-06-14 |
| 12392 | Female | 1981-09-17 |
| 12416 | Male | 2031-05-01 |
| 12373 | Male | 1962-02-26 |
| 12374 | Male | 1963-03-27 |
| 12817 | female | 1983-02-22 |
- erp_idtext · unique id
- 12389
- gendertext
- female
- birth_datedate
- 1978-06-14
- erp_idtext · unique id
- 12392
- gendertext
- Female
- birth_datedate
- 1981-09-17
- erp_idtext · unique id
- 12416
- gendertext
- Male
- birth_datedate
- 2031-05-01
- erp_idtext · unique id
- 12373
- gendertext
- Male
- birth_datedate
- 1962-02-26
- erp_idtext · unique id
- 12374
- gendertext
- Male
- birth_datedate
- 1963-03-27
- erp_idtext · unique id
- 12817
- gendertext
- female
- birth_datedate
- 1983-02-22
first 6 of 848 rows · your queries use all of them
erp_locations2 columns · 848 rows
| erp_idtext · unique id | country_codetext |
|---|---|
| 12-389 | AU |
| 12-392 | AU |
| 12-416 | AU |
| 12-373 | AT |
| 12-374 | AT |
| 12-817 | AT |
- erp_idtext · unique id
- 12-389
- country_codetext
- AU
- erp_idtext · unique id
- 12-392
- country_codetext
- AU
- erp_idtext · unique id
- 12-416
- country_codetext
- AU
- erp_idtext · unique id
- 12-373
- country_codetext
- AT
- erp_idtext · unique id
- 12-374
- country_codetext
- AT
- erp_idtext · unique id
- 12-817
- country_codetext
- AT
first 6 of 848 rows · your queries use all of them
worked example
Run these steps in order to see the idea before you write your own query. The step marked “the pattern” is what the challenges ask for. Running the example does not count toward progress.
Size the raw Bronze batch. bronze_lines and distinct customers describe December sales before quality gates or integration—your capstone checkpoints start here.the pattern
example.sqlSELECT COUNT(*) AS bronze_lines, COUNT(DISTINCT TRIM(customer_id)) AS customers FROM bronze_sales;
Count CRM customers missing ERP. The integration contract keeps every keyed CRM row; without_erp shows how many have no matching ERP person.
step-2.sqlWITH crm AS ( SELECT customer_id, contact_name, crm_gender FROM ( SELECT TRIM(customer_id) AS customer_id, TRIM(contact_name) AS contact_name, CASE UPPER(TRIM(gender)) WHEN 'F' THEN 'Female' WHEN 'M' THEN 'Male' END AS crm_gender, ROW_NUMBER() OVER (PARTITION BY TRIM(customer_id) ORDER BY updated_at DESC, source_row DESC) AS rn FROM crm_people WHERE customer_id IS NOT NULL AND TRIM(customer_id) <> '' ) WHERE rn = 1 ), erp AS ( SELECT CASE WHEN substr(TRIM(erp_id), 1, 3) = 'NAS' THEN substr(TRIM(erp_id), 4) ELSE TRIM(erp_id) END AS customer_id, CASE UPPER(TRIM(gender)) WHEN 'FEMALE' THEN 'Female' WHEN 'MALE' THEN 'Male' END AS erp_gender, CASE WHEN date(birth_date, '+0 days') = birth_date AND birth_date <= '2011-12-31' THEN birth_date END AS birth_date FROM erp_people ), loc AS ( SELECT REPLACE(TRIM(erp_id), '-', '') AS customer_id, k.country FROM erp_locations e LEFT JOIN country_codes k ON k.code = UPPER(TRIM(e.country_code)) ) SELECT COUNT(*) AS crm_customers, SUM(erp.customer_id IS NULL) AS without_erp FROM crm LEFT JOIN erp ON erp.customer_id = crm.customer_id;
Map location codes to countries. erp_locations joins country_codes so integrated customers get a resolved country, not raw ISO text alone.
step-3.sqlSELECT k.country, COUNT(*) AS locations FROM erp_locations e LEFT JOIN country_codes k ON k.code = UPPER(TRIM(e.country_code)) GROUP BY k.country ORDER BY locations DESC;
common mistake
Joining Bronze sales to integrated customers before applying the quality gates lets invalid lines into the report.