-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path05_operations.sql
More file actions
179 lines (159 loc) · 6.41 KB
/
Copy path05_operations.sql
File metadata and controls
179 lines (159 loc) · 6.41 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
/* ============================================================
Fulfilment and payment operations (Q9, Q10, Q14, Q15, Q18)
Operational questions spanning shipping and payments. Shipping
holds 21,141 rows against 21,629 orders, because the 488
cancelled orders were never shipped. That is correct rather
than missing data.
There is no delivery date anywhere in this schema, so delivery
time cannot be derived and is not attempted. Three of these
questions return zero rows or prove unanswerable. Each says so
and shows the evidence, because a null result with proof is a
finding rather than a gap.
============================================================ */
-- ============================================================
-- Q9 | Orders despatched more than seven days late
-- Business question: which orders took more than seven days to
-- dispatch after being placed?
-- Approach: subtract order date from shipping date, then report
-- the full lag distribution as supporting evidence.
-- Note: none exist. The longest dispatch lag in the data is five
-- days, and the spread across one to five days is near uniform.
-- ============================================================
-- Part A: the question as asked
SELECT
o.order_id,
o.order_date,
s.shipping_date,
s.shipping_provider,
s.shipping_date - o.order_date AS dispatch_lag_days
FROM orders AS o
INNER JOIN shipping AS s
ON s.order_id = o.order_id
WHERE s.shipping_date - o.order_date > 7
ORDER BY dispatch_lag_days DESC;
-- Part B: the evidence behind the zero
SELECT
s.shipping_date - o.order_date AS dispatch_lag_days,
-- COUNT counts the rows that landed in this one bucket
COUNT(*) AS orders,
-- SUM adds every bucket's count down into one total, then writes
-- that total onto each row so it can show its share
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 2) AS pct_of_shipped
FROM orders AS o
INNER JOIN shipping AS s
ON s.order_id = o.order_id
GROUP BY 1
ORDER BY 1;
-- ============================================================
-- Q10 | Payment success rate
-- Business question: what share of payments succeeded, failed,
-- or were refunded?
-- Approach: count payments by status and express each as a share
-- of all transactions.
-- Note: refunds are reversals, not successes. The success rate
-- is 84.61%, not the 97.74% that adding refunds back gives.
-- ============================================================
SELECT
payment_status,
COUNT(*) AS transactions,
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 2) AS pct_of_payments
FROM payments
GROUP BY 1
ORDER BY transactions DESC;
-- ============================================================
-- Q14 | Paid orders awaiting shipment
-- Business question: which successfully paid orders have no
-- shipping record?
-- Approach: left join shipping to orders and keep rows where no
-- shipping record exists.
-- Note 1: none exist. All 488 orders without a shipping record
-- are cancelled with a failed payment.
-- Note 2: the left join is essential. With an inner join the null
-- check could never be true, so the zero would be guaranteed by
-- the query rather than found in the data.
-- ============================================================
SELECT
o.order_id,
o.order_date,
o.order_status,
DATE '2024-07-30' - o.order_date AS days_awaiting_shipment
FROM orders AS o
INNER JOIN payments AS p
ON p.order_id = o.order_id
LEFT JOIN shipping AS s
ON s.order_id = o.order_id
WHERE p.payment_status = 'Payment Successed'
AND s.order_id IS NULL
ORDER BY o.order_date;
-- ============================================================
-- Q15 | Cross-sell opportunities
-- Business question: which products are frequently bought
-- together in the same order?
-- Approach: count orders holding more than one distinct product,
-- and measure the largest basket in the data.
-- Note: not answerable here. No order contains more than one
-- product, so no pair co-occurs and affinity is undefined.
-- ============================================================
WITH baskets AS (
SELECT
order_id,
COUNT(*) AS line_items,
COUNT(DISTINCT product_id) AS distinct_products
FROM order_items
GROUP BY order_id
)
SELECT
COUNT(*) AS orders_with_line_items,
MAX(line_items) AS max_line_items,
COUNT(*) FILTER (WHERE distinct_products > 1) AS orders_with_multiple_products
FROM baskets;
-- SELECT
-- oi_a.product_id AS product_a_id,
-- pa.product_name AS product_a,
-- oi_b.product_id AS product_b_id,
-- pb.product_name AS product_b,
-- COUNT(DISTINCT oi_a.order_id) AS orders_containing_both
-- FROM order_items AS oi_a
-- INNER JOIN order_items AS oi_b
-- ON oi_b.order_id = oi_a.order_id
-- AND oi_b.product_id > oi_a.product_id
-- INNER JOIN products AS pa
-- ON pa.product_id = oi_a.product_id
-- INNER JOIN products AS pb
-- ON pb.product_id = oi_b.product_id
-- GROUP BY 1, 2, 3, 4
-- ORDER BY orders_containing_both DESC
-- LIMIT 10;
-- ============================================================
-- Q18 | Shipping provider performance
-- Business question: how much order value does each shipping
-- provider handle, and how quickly do they dispatch?
-- Approach: group shipped orders by provider, reporting value,
-- average dispatch lag and returns handled.
-- Note: delivery time is not derivable, as the schema holds no
-- delivery date. Dispatch lag is the closest available measure.
-- ============================================================
SELECT
s.shipping_provider,
COUNT(DISTINCT o.order_id) AS orders_handled,
ROUND(SUM(oi.total_sale), 2) AS shipped_order_value,
ROUND(AVG(s.shipping_date - o.order_date), 2) AS avg_dispatch_lag_days,
COUNT(DISTINCT o.order_id) FILTER (WHERE s.return_date IS NOT NULL)
AS returned_orders
FROM orders AS o
INNER JOIN order_items AS oi
ON oi.order_id = o.order_id
INNER JOIN shipping AS s
ON s.order_id = o.order_id
GROUP BY 1
ORDER BY shipped_order_value DESC;
-- If we wanted to add a return_rate_pct, we would simply add the above query in a cte, and write this query
-- SELECT
-- shipping_provider,
-- orders_handled,
-- shipped_order_value,
-- avg_dispatch_lag_days,
-- returned_orders,
-- ROUND(returned_orders * 100.0 / orders_handled, 2) AS return_rate_pct
-- FROM provider_totals (cte from above query)
-- ORDER BY shipped_order_value DESC;