|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CREATE TABLE raw.customers ( |
|
|
customer_id INTEGER PRIMARY KEY, |
|
|
email VARCHAR(255) NOT NULL, |
|
|
first_name VARCHAR(100), |
|
|
last_name VARCHAR(100), |
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
|
|
country VARCHAR(50), |
|
|
segment VARCHAR(50) |
|
|
); |
|
|
|
|
|
CREATE TABLE raw.orders ( |
|
|
order_id INTEGER PRIMARY KEY, |
|
|
customer_id INTEGER REFERENCES raw.customers(customer_id), |
|
|
order_date DATE NOT NULL, |
|
|
total_amount DECIMAL(10,2), |
|
|
currency VARCHAR(3) DEFAULT 'USD', |
|
|
status VARCHAR(20), |
|
|
shipping_address_id INTEGER |
|
|
); |
|
|
|
|
|
CREATE TABLE raw.products ( |
|
|
product_id INTEGER PRIMARY KEY, |
|
|
product_name VARCHAR(255) NOT NULL, |
|
|
category VARCHAR(100), |
|
|
subcategory VARCHAR(100), |
|
|
brand VARCHAR(100), |
|
|
price DECIMAL(10,2), |
|
|
cost DECIMAL(10,2) |
|
|
); |
|
|
|
|
|
CREATE TABLE raw.order_items ( |
|
|
order_item_id INTEGER PRIMARY KEY, |
|
|
order_id INTEGER REFERENCES raw.orders(order_id), |
|
|
product_id INTEGER REFERENCES raw.products(product_id), |
|
|
quantity INTEGER NOT NULL, |
|
|
unit_price DECIMAL(10,2), |
|
|
discount_percent DECIMAL(5,2) DEFAULT 0 |
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CREATE VIEW staging.stg_customers AS |
|
|
SELECT |
|
|
customer_id, |
|
|
LOWER(TRIM(email)) as email, |
|
|
INITCAP(first_name) as first_name, |
|
|
INITCAP(last_name) as last_name, |
|
|
DATE(created_at) as signup_date, |
|
|
UPPER(country) as country, |
|
|
COALESCE(segment, 'Unknown') as segment |
|
|
FROM raw.customers |
|
|
WHERE email IS NOT NULL; |
|
|
|
|
|
|
|
|
CREATE VIEW staging.stg_orders AS |
|
|
SELECT |
|
|
order_id, |
|
|
customer_id, |
|
|
order_date, |
|
|
total_amount, |
|
|
currency, |
|
|
CASE |
|
|
WHEN status IN ('completed', 'shipped', 'delivered') THEN 'Fulfilled' |
|
|
WHEN status IN ('pending', 'processing') THEN 'In Progress' |
|
|
ELSE 'Other' |
|
|
END as order_status |
|
|
FROM raw.orders |
|
|
WHERE order_date >= '2024-01-01'; |
|
|
|
|
|
|
|
|
CREATE VIEW staging.stg_products AS |
|
|
SELECT |
|
|
product_id, |
|
|
product_name, |
|
|
category, |
|
|
subcategory, |
|
|
brand, |
|
|
price, |
|
|
cost, |
|
|
(price - cost) / NULLIF(price, 0) * 100 as margin_percent |
|
|
FROM raw.products |
|
|
WHERE price > 0; |
|
|
|
|
|
|
|
|
CREATE VIEW staging.stg_order_items AS |
|
|
SELECT |
|
|
order_item_id, |
|
|
order_id, |
|
|
product_id, |
|
|
quantity, |
|
|
unit_price, |
|
|
discount_percent, |
|
|
quantity * unit_price * (1 - discount_percent/100) as line_total |
|
|
FROM raw.order_items; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CREATE TABLE intermediate.int_customer_orders AS |
|
|
SELECT |
|
|
c.customer_id, |
|
|
c.email, |
|
|
c.first_name, |
|
|
c.last_name, |
|
|
c.signup_date, |
|
|
c.country, |
|
|
c.segment, |
|
|
COUNT(DISTINCT o.order_id) as total_orders, |
|
|
SUM(o.total_amount) as total_spent, |
|
|
MIN(o.order_date) as first_order_date, |
|
|
MAX(o.order_date) as last_order_date, |
|
|
AVG(o.total_amount) as avg_order_value |
|
|
FROM staging.stg_customers c |
|
|
LEFT JOIN staging.stg_orders o ON c.customer_id = o.customer_id |
|
|
GROUP BY c.customer_id, c.email, c.first_name, c.last_name, |
|
|
c.signup_date, c.country, c.segment; |
|
|
|
|
|
|
|
|
CREATE TABLE intermediate.int_order_details AS |
|
|
SELECT |
|
|
o.order_id, |
|
|
o.customer_id, |
|
|
o.order_date, |
|
|
o.order_status, |
|
|
oi.product_id, |
|
|
p.product_name, |
|
|
p.category, |
|
|
p.brand, |
|
|
oi.quantity, |
|
|
oi.unit_price, |
|
|
oi.line_total, |
|
|
p.margin_percent |
|
|
FROM staging.stg_orders o |
|
|
JOIN staging.stg_order_items oi ON o.order_id = oi.order_id |
|
|
JOIN staging.stg_products p ON oi.product_id = p.product_id; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CREATE TABLE marts.dim_customers AS |
|
|
SELECT |
|
|
customer_id, |
|
|
email, |
|
|
first_name || ' ' || last_name as full_name, |
|
|
signup_date, |
|
|
country, |
|
|
segment, |
|
|
total_orders, |
|
|
total_spent, |
|
|
first_order_date, |
|
|
last_order_date, |
|
|
avg_order_value, |
|
|
CASE |
|
|
WHEN total_spent > 10000 THEN 'Platinum' |
|
|
WHEN total_spent > 5000 THEN 'Gold' |
|
|
WHEN total_spent > 1000 THEN 'Silver' |
|
|
ELSE 'Bronze' |
|
|
END as customer_tier, |
|
|
DATEDIFF(day, signup_date, first_order_date) as days_to_first_order |
|
|
FROM intermediate.int_customer_orders; |
|
|
|
|
|
|
|
|
CREATE TABLE marts.dim_products AS |
|
|
SELECT |
|
|
product_id, |
|
|
product_name, |
|
|
category, |
|
|
subcategory, |
|
|
brand, |
|
|
price, |
|
|
cost, |
|
|
margin_percent, |
|
|
CASE |
|
|
WHEN margin_percent > 50 THEN 'High Margin' |
|
|
WHEN margin_percent > 25 THEN 'Medium Margin' |
|
|
ELSE 'Low Margin' |
|
|
END as margin_tier |
|
|
FROM staging.stg_products; |
|
|
|
|
|
|
|
|
CREATE TABLE marts.fct_orders AS |
|
|
SELECT |
|
|
od.order_id, |
|
|
od.customer_id, |
|
|
od.product_id, |
|
|
od.order_date, |
|
|
od.order_status, |
|
|
od.quantity, |
|
|
od.unit_price, |
|
|
od.line_total, |
|
|
od.margin_percent, |
|
|
dc.customer_tier, |
|
|
dp.margin_tier, |
|
|
dp.category as product_category |
|
|
FROM intermediate.int_order_details od |
|
|
JOIN marts.dim_customers dc ON od.customer_id = dc.customer_id |
|
|
JOIN marts.dim_products dp ON od.product_id = dp.product_id; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CREATE VIEW reporting.rpt_daily_sales AS |
|
|
SELECT |
|
|
order_date, |
|
|
product_category, |
|
|
COUNT(DISTINCT order_id) as num_orders, |
|
|
SUM(quantity) as units_sold, |
|
|
SUM(line_total) as gross_revenue, |
|
|
AVG(line_total) as avg_order_value |
|
|
FROM marts.fct_orders |
|
|
GROUP BY order_date, product_category; |
|
|
|
|
|
|
|
|
CREATE VIEW reporting.rpt_customer_ltv AS |
|
|
SELECT |
|
|
customer_id, |
|
|
full_name, |
|
|
customer_tier, |
|
|
country, |
|
|
total_orders, |
|
|
total_spent as lifetime_value, |
|
|
avg_order_value, |
|
|
days_to_first_order, |
|
|
DATEDIFF(day, first_order_date, last_order_date) as customer_lifespan_days, |
|
|
total_spent / NULLIF(DATEDIFF(month, first_order_date, last_order_date), 0) as monthly_value |
|
|
FROM marts.dim_customers |
|
|
WHERE total_orders > 0; |
|
|
|
|
|
|
|
|
CREATE VIEW reporting.rpt_product_performance AS |
|
|
SELECT |
|
|
dp.product_id, |
|
|
dp.product_name, |
|
|
dp.category, |
|
|
dp.brand, |
|
|
dp.margin_tier, |
|
|
COUNT(DISTINCT fo.order_id) as times_ordered, |
|
|
SUM(fo.quantity) as total_units_sold, |
|
|
SUM(fo.line_total) as total_revenue, |
|
|
AVG(fo.margin_percent) as avg_margin |
|
|
FROM marts.dim_products dp |
|
|
LEFT JOIN marts.fct_orders fo ON dp.product_id = fo.product_id |
|
|
GROUP BY dp.product_id, dp.product_name, dp.category, dp.brand, dp.margin_tier; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|