199 lines
7.5 KiB
SQL
199 lines
7.5 KiB
SQL
-- ============================================================================
|
||
-- 💡 SQL 16: MUSTERLÖSUNG – MASTER DATA WAREHOUSE & ADVANCED ANALYTICS 💡
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS dim_customers (
|
||
customer_key INTEGER PRIMARY KEY,
|
||
customer_id TEXT UNIQUE NOT NULL,
|
||
name TEXT NOT NULL,
|
||
city TEXT NOT NULL,
|
||
country TEXT NOT NULL,
|
||
signup_date TEXT NOT NULL
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS dim_products (
|
||
product_key INTEGER PRIMARY KEY,
|
||
product_id TEXT UNIQUE NOT NULL,
|
||
product_name TEXT NOT NULL,
|
||
category TEXT NOT NULL,
|
||
unit_cost REAL NOT NULL,
|
||
unit_price REAL NOT NULL
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS dim_channels (
|
||
channel_key INTEGER PRIMARY KEY,
|
||
channel_name TEXT NOT NULL
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS fact_orders (
|
||
order_key INTEGER PRIMARY KEY,
|
||
order_id TEXT UNIQUE NOT NULL,
|
||
customer_key INTEGER NOT NULL,
|
||
channel_key INTEGER NOT NULL,
|
||
order_date TEXT NOT NULL,
|
||
total_amount REAL NOT NULL,
|
||
status TEXT NOT NULL,
|
||
FOREIGN KEY (customer_key) REFERENCES dim_customers(customer_key),
|
||
FOREIGN KEY (channel_key) REFERENCES dim_channels(channel_key)
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS fact_order_items (
|
||
item_key INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
order_key INTEGER NOT NULL,
|
||
product_key INTEGER NOT NULL,
|
||
quantity INTEGER NOT NULL,
|
||
unit_price REAL NOT NULL,
|
||
line_total REAL NOT NULL,
|
||
FOREIGN KEY (order_key) REFERENCES fact_orders(order_key),
|
||
FOREIGN KEY (product_key) REFERENCES dim_products(product_key)
|
||
);
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_fact_orders_customer ON fact_orders(customer_key);
|
||
CREATE INDEX IF NOT EXISTS idx_fact_orders_date ON fact_orders(order_date);
|
||
CREATE INDEX IF NOT EXISTS idx_fact_items_order ON fact_order_items(order_key);
|
||
|
||
INSERT OR REPLACE INTO dim_customers (customer_key, customer_id, name, city, country, signup_date) VALUES
|
||
(1, 'CUST-001', 'Alice Becker', 'Berlin', 'Germany', '2024-01-05'),
|
||
(2, 'CUST-002', 'Bob Meier', 'Hamburg', 'Germany', '2024-01-12'),
|
||
(3, 'CUST-003', 'Carla Wagner', 'München', 'Germany', '2024-01-20'),
|
||
(4, 'CUST-004', 'David Fischer', 'Wien', 'Austria', '2024-02-02'),
|
||
(5, 'CUST-005', 'Eva Klein', 'Zürich', 'Switzerland', '2024-02-15'),
|
||
(6, 'CUST-006', 'Felix Wolf', 'Köln', 'Germany', '2024-03-01'),
|
||
(7, 'CUST-007', 'Grace Hopper', 'Berlin', 'Germany', '2024-03-10');
|
||
|
||
INSERT OR REPLACE INTO dim_products (product_key, product_id, product_name, category, unit_cost, unit_price) VALUES
|
||
(1, 'PROD-01', 'Cloud Server S', 'Hosting', 20.00, 49.00),
|
||
(2, 'PROD-02', 'Cloud Server L', 'Hosting', 60.00, 149.00),
|
||
(3, 'PROD-03', 'Enterprise Database', 'Software', 150.00, 499.00),
|
||
(4, 'PROD-04', 'Security Suite Pro', 'Software', 50.00, 199.00),
|
||
(5, 'PROD-05', 'Consulting Tagessatz', 'Services', 400.00, 950.00);
|
||
|
||
INSERT OR REPLACE INTO dim_channels (channel_key, channel_name) VALUES
|
||
(1, 'Web Direct'),
|
||
(2, 'Partner Referral'),
|
||
(3, 'Sales Outbound');
|
||
|
||
INSERT OR REPLACE INTO fact_orders (order_key, order_id, customer_key, channel_key, order_date, total_amount, status) VALUES
|
||
(1001, 'ORD-1001', 1, 1, '2024-01-10', 49.00, 'COMPLETED'),
|
||
(1002, 'ORD-1002', 2, 2, '2024-01-15', 149.00, 'COMPLETED'),
|
||
(1003, 'ORD-1003', 3, 1, '2024-01-22', 499.00, 'COMPLETED'),
|
||
(1004, 'ORD-1004', 1, 1, '2024-02-05', 199.00, 'COMPLETED'),
|
||
(1005, 'ORD-1005', 2, 2, '2024-02-18', 149.00, 'COMPLETED'),
|
||
(1006, 'ORD-1006', 4, 3, '2024-02-20', 950.00, 'COMPLETED'),
|
||
(1007, 'ORD-1007', 5, 1, '2024-02-25', 49.00, 'COMPLETED'),
|
||
(1008, 'ORD-1008', 1, 1, '2024-03-02', 499.00, 'COMPLETED'),
|
||
(1009, 'ORD-1009', 3, 1, '2024-03-12', 49.00, 'COMPLETED'),
|
||
(1010, 'ORD-1010', 6, 2, '2024-03-15', 950.00, 'COMPLETED'),
|
||
(1011, 'ORD-1011', 7, 1, '2024-03-20', 149.00, 'COMPLETED'),
|
||
(1012, 'ORD-1012', 1, 1, '2024-04-05', 950.00, 'COMPLETED'),
|
||
(1013, 'ORD-1013', 2, 2, '2024-04-10', 149.00, 'COMPLETED'),
|
||
(1014, 'ORD-1014', 4, 3, '2024-04-15', 499.00, 'COMPLETED');
|
||
|
||
-- 🎯 TODO 1: Top-Produkte & Analytics Check
|
||
SELECT
|
||
p.product_name,
|
||
p.category,
|
||
COUNT(fo.order_key) AS anzahl_verkaeufe,
|
||
SUM(fo.total_amount) AS gesamt_umsatz
|
||
FROM dim_products p
|
||
JOIN fact_orders fo ON p.product_key = fo.channel_key
|
||
GROUP BY p.product_key, p.product_name, p.category
|
||
ORDER BY gesamt_umsatz DESC;
|
||
|
||
-- 🎯 TODO 2: Kohorten-Retention-Matrix (Cohort Analysis)
|
||
WITH kunden_kohorte AS (
|
||
SELECT
|
||
customer_key,
|
||
strftime('%Y-%m', MIN(order_date)) AS kohorte
|
||
FROM fact_orders
|
||
GROUP BY customer_key
|
||
),
|
||
kunden_aktivitaet AS (
|
||
SELECT DISTINCT
|
||
fo.customer_key,
|
||
kk.kohorte,
|
||
strftime('%Y-%m', fo.order_date) AS aktivitaets_monat
|
||
FROM fact_orders fo
|
||
JOIN kunden_kohorte kk ON fo.customer_key = kk.customer_key
|
||
),
|
||
kohorten_basis AS (
|
||
SELECT
|
||
kohorte,
|
||
COUNT(DISTINCT customer_key) AS kohorten_groesse
|
||
FROM kunden_kohorte
|
||
GROUP BY kohorte
|
||
)
|
||
SELECT
|
||
ka.kohorte,
|
||
ka.aktivitaets_monat,
|
||
COUNT(DISTINCT ka.customer_key) AS aktive_kunden,
|
||
kb.kohorten_groesse,
|
||
ROUND(COUNT(DISTINCT ka.customer_key) * 100.0 / kb.kohorten_groesse, 1) AS retention_rate_pct
|
||
FROM kunden_aktivitaet ka
|
||
JOIN kohorten_basis kb ON ka.kohorte = kb.kohorte
|
||
GROUP BY ka.kohorte, ka.aktivitaets_monat, kb.kohorten_groesse
|
||
ORDER BY ka.kohorte ASC, ka.aktivitaets_monat ASC;
|
||
|
||
-- 🎯 TODO 3: RFM-Kundensegmentierung (Recency, Frequency, Monetary)
|
||
WITH rfm_raw AS (
|
||
SELECT
|
||
fo.customer_key,
|
||
c.name AS kunden_name,
|
||
CAST(julianday('2024-04-30') - julianday(MAX(fo.order_date)) AS INTEGER) AS recency_tage,
|
||
COUNT(fo.order_key) AS frequency,
|
||
SUM(fo.total_amount) AS monetary
|
||
FROM fact_orders fo
|
||
JOIN dim_customers c ON fo.customer_key = c.customer_key
|
||
GROUP BY fo.customer_key, c.name
|
||
),
|
||
rfm_scored AS (
|
||
SELECT
|
||
customer_key,
|
||
kunden_name,
|
||
recency_tage,
|
||
frequency,
|
||
monetary,
|
||
NTILE(4) OVER (ORDER BY recency_tage DESC) AS r_score,
|
||
NTILE(4) OVER (ORDER BY frequency ASC) AS f_score,
|
||
NTILE(4) OVER (ORDER BY monetary ASC) AS m_score
|
||
FROM rfm_raw
|
||
)
|
||
SELECT
|
||
customer_key,
|
||
kunden_name,
|
||
recency_tage,
|
||
frequency,
|
||
monetary,
|
||
r_score,
|
||
f_score,
|
||
m_score,
|
||
CASE
|
||
WHEN r_score >= 3 AND f_score >= 3 AND m_score >= 3 THEN 'Champions'
|
||
WHEN f_score >= 3 THEN 'Loyal Customers'
|
||
WHEN r_score <= 2 AND f_score >= 2 THEN 'At Risk'
|
||
ELSE 'Potential / Hibernating'
|
||
END AS kunden_segment
|
||
FROM rfm_scored
|
||
ORDER BY monetary DESC;
|
||
|
||
-- 🎯 TODO 4: Executive Business Intelligence View
|
||
DROP VIEW IF EXISTS v_executive_kpi_dashboard;
|
||
CREATE VIEW v_executive_kpi_dashboard AS
|
||
SELECT
|
||
strftime('%Y-%m', order_date) AS monat,
|
||
SUM(total_amount) AS monats_umsatz,
|
||
COUNT(order_key) AS anzahl_bestellungen,
|
||
COUNT(DISTINCT customer_key) AS aktive_kunden,
|
||
ROUND(AVG(total_amount), 2) AS aov,
|
||
LAG(SUM(total_amount), 1, 0) OVER (ORDER BY strftime('%Y-%m', order_date)) AS vormonat_umsatz,
|
||
ROUND(
|
||
(SUM(total_amount) - LAG(SUM(total_amount), 1, SUM(total_amount)) OVER (ORDER BY strftime('%Y-%m', order_date))) * 100.0 /
|
||
LAG(SUM(total_amount), 1, SUM(total_amount)) OVER (ORDER BY strftime('%Y-%m', order_date)),
|
||
2
|
||
) AS mom_wachstum_pct,
|
||
SUM(SUM(total_amount)) OVER (
|
||
ORDER BY strftime('%Y-%m', order_date)
|
||
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||
) AS kumulierter_allzeit_umsatz
|
||
FROM fact_orders
|
||
GROUP BY strftime('%Y-%m', order_date);
|