-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path02_sql_analysis.sql
More file actions
323 lines (293 loc) · 12.9 KB
/
Copy path02_sql_analysis.sql
File metadata and controls
323 lines (293 loc) · 12.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
-- ============================================================
-- CooperVision Pricing & Promotion Analytics — SQL Analysis
-- ============================================================
-- Author: Rahul Muddhapuram
-- Date: April 2026
--
-- This script demonstrates the SQL analysis layer for a
-- contact lens pricing analytics system. Written for
-- SQL Server / T-SQL syntax, adaptable to PostgreSQL.
--
-- Tables: dim_products, dim_distributors, dim_promotions,
-- dim_contracts, dim_competitors, fact_transactions
-- ============================================================
-- ============================================================
-- 1. PRICING KPI SUMMARY BY QUARTER
-- ============================================================
-- Core metrics the Strategic Pricing team tracks
SELECT
quarter,
COUNT(DISTINCT transaction_id) AS total_transactions,
SUM(total_lenses) AS total_lenses_sold,
ROUND(SUM(net_revenue), 0) AS total_net_revenue,
ROUND(SUM(gross_revenue), 0) AS total_gross_revenue,
ROUND(SUM(net_revenue) / NULLIF(SUM(total_lenses), 0), 4)
AS avg_net_price_per_lens,
ROUND(AVG(discount_pct) * 100, 1) AS avg_discount_pct,
ROUND(SUM(gross_margin), 0) AS total_gross_margin,
ROUND(AVG(margin_pct) * 100, 1) AS avg_margin_pct,
ROUND(SUM(rebate_amount), 0) AS total_rebate_payout,
ROUND(SUM(CASE WHEN rebate_amount > 0 THEN 1 ELSE 0 END) * 100.0
/ COUNT(*), 1) AS rebate_redemption_pct
FROM fact_transactions
GROUP BY quarter
ORDER BY quarter;
-- ============================================================
-- 2. PRODUCT FAMILY PRICING ANALYSIS
-- ============================================================
-- Shows list-to-net price gap and margin by product family
-- This is the analysis an intern would build for price reviews
SELECT
p.family,
p.lens_type,
p.replacement,
COUNT(t.transaction_id) AS txn_count,
SUM(t.total_lenses) AS total_lenses,
ROUND(SUM(t.net_revenue), 0) AS net_revenue,
ROUND(AVG(t.list_price_per_lens), 4) AS avg_list_price,
ROUND(AVG(t.net_price_per_lens), 4) AS avg_net_price,
ROUND(AVG(t.discount_pct) * 100, 1) AS avg_discount_pct,
ROUND(AVG(t.margin_pct) * 100, 1) AS avg_margin_pct,
-- Price position vs competitors
c.avg_competitor_price,
CASE
WHEN AVG(t.net_price_per_lens) < c.avg_competitor_price * 0.97
THEN 'Below Market'
WHEN AVG(t.net_price_per_lens) > c.avg_competitor_price * 1.03
THEN 'Above Market'
ELSE 'At Market'
END AS price_position
FROM fact_transactions t
JOIN dim_products p ON t.product_id = p.product_id
LEFT JOIN (
SELECT lens_type, replacement,
ROUND(AVG(est_wholesale_price), 4) AS avg_competitor_price
FROM dim_competitors
GROUP BY lens_type, replacement
) c ON p.lens_type = c.lens_type AND p.replacement = c.replacement
GROUP BY p.family, p.lens_type, p.replacement,
c.avg_competitor_price
ORDER BY net_revenue DESC;
-- ============================================================
-- 3. MONTHLY NET SELLING PRICE TREND
-- ============================================================
-- Tracks price realization over time — critical for measuring
-- whether price increases are holding or eroding
SELECT
month,
month_name,
ROUND(SUM(net_revenue) / NULLIF(SUM(total_lenses), 0), 4)
AS avg_net_price,
ROUND(SUM(gross_revenue) / NULLIF(SUM(total_lenses), 0), 4)
AS avg_list_price,
ROUND((1 - SUM(net_revenue) / NULLIF(SUM(gross_revenue), 0)) * 100, 1)
AS realized_discount_pct,
SUM(total_lenses) AS volume,
ROUND(SUM(net_revenue), 0) AS net_revenue
FROM fact_transactions
GROUP BY month, month_name
ORDER BY month;
-- ============================================================
-- 4. DISTRIBUTOR PRICING SCORECARD
-- ============================================================
-- Used by the team to manage price consistency across channels
-- and prepare for contract renewal negotiations
SELECT
d.distributor_name,
d.tier,
d.region,
COUNT(t.transaction_id) AS txn_count,
ROUND(SUM(t.net_revenue), 0) AS net_revenue,
ROUND(SUM(t.net_revenue) * 100.0
/ SUM(SUM(t.net_revenue)) OVER(), 1) AS revenue_share_pct,
ROUND(AVG(t.discount_pct) * 100, 1) AS avg_discount_pct,
ROUND(AVG(t.net_price_per_lens), 4) AS avg_net_price,
-- Price index: 100 = portfolio average
ROUND(AVG(t.net_price_per_lens)
/ (SUM(SUM(t.net_revenue)) OVER()
/ NULLIF(SUM(SUM(t.total_lenses)) OVER(), 0)) * 100, 0)
AS net_price_index,
ROUND(AVG(t.margin_pct) * 100, 1) AS avg_margin_pct,
-- Contract info
ct.status AS contract_status,
ct.end_date AS contract_end_date,
ROUND(ct.contracted_discount * 100, 1) AS contracted_discount_pct
FROM fact_transactions t
JOIN dim_distributors d ON t.distributor_id = d.distributor_id
LEFT JOIN dim_contracts ct ON d.distributor_id = ct.distributor_id
GROUP BY d.distributor_name, d.tier, d.region,
ct.status, ct.end_date, ct.contracted_discount
ORDER BY net_revenue DESC;
-- ============================================================
-- 5. PROMOTION ROI ANALYSIS
-- ============================================================
-- Measures the return on rebate investment — key for deciding
-- which campaigns to renew, modify, or discontinue
SELECT
pr.promo_name,
pr.promo_type,
pr.reward_amount AS reward_per_redemption,
-- Transaction metrics during promo period
COUNT(t.transaction_id) AS total_txns,
SUM(CASE WHEN t.rebate_amount > 0 THEN 1 ELSE 0 END)
AS redeemed_txns,
ROUND(SUM(CASE WHEN t.rebate_amount > 0 THEN 1 ELSE 0 END) * 100.0
/ NULLIF(COUNT(*), 0), 1) AS redemption_rate_pct,
-- Financial impact
ROUND(SUM(t.net_revenue), 0) AS total_net_revenue,
ROUND(SUM(t.rebate_amount), 0) AS total_rebate_cost,
ROUND(SUM(t.net_revenue)
/ NULLIF(SUM(t.rebate_amount), 0), 1) AS revenue_to_rebate_ratio,
ROUND(SUM(t.gross_margin), 0) AS gross_margin_after_cogs,
ROUND((SUM(t.gross_margin) - SUM(t.rebate_amount))
/ NULLIF(SUM(t.rebate_amount), 0), 1) AS net_promo_roi
FROM fact_transactions t
JOIN dim_promotions pr
ON t.rebate_promo_id = pr.promo_id
GROUP BY pr.promo_name, pr.promo_type, pr.reward_amount
ORDER BY net_promo_roi DESC;
-- ============================================================
-- 6. DISCOUNT DEPTH DISTRIBUTION
-- ============================================================
-- Identifies if discount discipline is being maintained
-- Flags transactions with excessive discounting
SELECT
CASE
WHEN discount_pct < 0.10 THEN '< 10%'
WHEN discount_pct < 0.13 THEN '10-13%'
WHEN discount_pct < 0.15 THEN '13-15%'
WHEN discount_pct < 0.18 THEN '15-18%'
ELSE '18%+'
END AS discount_band,
COUNT(*) AS txn_count,
ROUND(COUNT(*) * 100.0
/ SUM(COUNT(*)) OVER(), 1) AS pct_of_total,
ROUND(SUM(net_revenue), 0) AS net_revenue,
ROUND(AVG(margin_pct) * 100, 1) AS avg_margin_pct
FROM fact_transactions
GROUP BY
CASE
WHEN discount_pct < 0.10 THEN '< 10%'
WHEN discount_pct < 0.13 THEN '10-13%'
WHEN discount_pct < 0.15 THEN '13-15%'
WHEN discount_pct < 0.18 THEN '15-18%'
ELSE '18%+'
END
ORDER BY discount_band;
-- ============================================================
-- 7. Q-OVER-Q GROWTH BY PRODUCT FAMILY
-- ============================================================
-- Quarter-over-quarter comparison for pricing reviews
WITH quarterly AS (
SELECT
family,
quarter,
ROUND(SUM(net_revenue), 0) AS net_revenue,
SUM(total_lenses) AS volume,
ROUND(SUM(net_revenue)
/ NULLIF(SUM(total_lenses), 0), 4) AS asp
FROM fact_transactions
GROUP BY family, quarter
)
SELECT
curr.family,
curr.quarter AS current_quarter,
curr.net_revenue AS curr_revenue,
prev.net_revenue AS prev_revenue,
ROUND((curr.net_revenue - prev.net_revenue) * 100.0
/ NULLIF(prev.net_revenue, 0), 1) AS revenue_growth_pct,
curr.asp AS curr_asp,
prev.asp AS prev_asp,
ROUND((curr.asp - prev.asp) * 100.0
/ NULLIF(prev.asp, 0), 1) AS asp_change_pct,
curr.volume AS curr_volume,
ROUND((curr.volume - prev.volume) * 100.0
/ NULLIF(prev.volume, 0), 1) AS volume_growth_pct
FROM quarterly curr
LEFT JOIN quarterly prev
ON curr.family = prev.family
AND curr.quarter = prev.quarter + 1
WHERE curr.quarter > 1
ORDER BY curr.family, curr.quarter;
-- ============================================================
-- 8. DISTRIBUTOR CONCENTRATION RISK
-- ============================================================
-- Herfindahl-Hirschman Index (HHI) for distributor concentration
WITH dist_shares AS (
SELECT
distributor_name,
ROUND(SUM(net_revenue) * 100.0
/ SUM(SUM(net_revenue)) OVER(), 2) AS share_pct
FROM fact_transactions
GROUP BY distributor_name
)
SELECT
ROUND(SUM(share_pct * share_pct), 0) AS hhi_index,
-- HHI > 2500 = highly concentrated
-- HHI 1500-2500 = moderately concentrated
-- HHI < 1500 = competitive
CASE
WHEN SUM(share_pct * share_pct) > 2500 THEN 'HIGHLY CONCENTRATED'
WHEN SUM(share_pct * share_pct) > 1500 THEN 'MODERATELY CONCENTRATED'
ELSE 'COMPETITIVE'
END AS concentration_level,
MAX(share_pct) AS largest_share_pct
FROM dist_shares;
-- ============================================================
-- 9. MiSight PRICING DEEP-DIVE
-- ============================================================
-- MiSight is CooperVision's flagship myopia management lens
-- No direct competitor — strategic pricing is critical
SELECT
month_name,
month,
COUNT(transaction_id) AS txn_count,
SUM(total_lenses) AS lenses_sold,
ROUND(AVG(net_price_per_lens), 4) AS avg_net_price,
ROUND(AVG(discount_pct) * 100, 1) AS avg_discount_pct,
ROUND(AVG(margin_pct) * 100, 1) AS avg_margin_pct,
SUM(CASE WHEN rebate_amount > 0 THEN 1 ELSE 0 END)
AS rebate_redemptions,
ROUND(SUM(rebate_amount), 0) AS total_rebate_cost,
ROUND(SUM(net_revenue), 0) AS net_revenue
FROM fact_transactions
WHERE family = 'MiSight'
GROUP BY month_name, month
ORDER BY month;
-- ============================================================
-- 10. ACTIONABLE PRICING RECOMMENDATIONS
-- ============================================================
-- Identifies products where price increases may be possible
-- (high margin + below market) vs. products under margin pressure
SELECT
p.product_name,
p.family,
ROUND(AVG(t.net_price_per_lens), 4) AS current_avg_net,
c.avg_competitor_price,
ROUND((c.avg_competitor_price - AVG(t.net_price_per_lens))
/ NULLIF(c.avg_competitor_price, 0) * 100, 1)
AS gap_to_competitor_pct,
ROUND(AVG(t.margin_pct) * 100, 1) AS current_margin_pct,
SUM(t.total_lenses) AS annual_volume,
-- Estimated revenue impact of 2% price increase
ROUND(SUM(t.net_revenue) * 0.02, 0) AS est_revenue_from_2pct_increase,
CASE
WHEN AVG(t.net_price_per_lens) < c.avg_competitor_price * 0.95
AND AVG(t.margin_pct) > 0.55
THEN '✅ PRICE INCREASE OPPORTUNITY'
WHEN AVG(t.net_price_per_lens) > c.avg_competitor_price * 1.05
THEN '⚠️ ABOVE MARKET — MONITOR VOLUME'
WHEN AVG(t.margin_pct) < 0.45
THEN '🔴 MARGIN PRESSURE — REVIEW DISCOUNTS'
ELSE '— HOLD CURRENT PRICING'
END AS recommendation
FROM fact_transactions t
JOIN dim_products p ON t.product_id = p.product_id
LEFT JOIN (
SELECT lens_type, replacement,
ROUND(AVG(est_wholesale_price), 4) AS avg_competitor_price
FROM dim_competitors
GROUP BY lens_type, replacement
) c ON p.lens_type = c.lens_type AND p.replacement = c.replacement
GROUP BY p.product_name, p.family, c.avg_competitor_price
ORDER BY est_revenue_from_2pct_increase DESC;