-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path07-joins.sql
More file actions
334 lines (284 loc) · 10.1 KB
/
Copy path07-joins.sql
File metadata and controls
334 lines (284 loc) · 10.1 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
324
325
326
327
328
329
330
331
332
333
334
-- ============================================================
-- SQL Masterclass — Chapter 07: JOINs
-- ============================================================
-- 🟡 INTERMEDIATE
--
-- In this chapter you will learn:
-- • INNER JOIN — matching rows from both tables
-- • LEFT JOIN — all rows from left + matches from right
-- • RIGHT JOIN — all rows from right + matches from left
-- • FULL OUTER JOIN — all rows from both tables
-- • CROSS JOIN — cartesian product
-- • Self-joins — joining a table to itself
-- • Multi-table joins — combining 3+ tables
-- • Table aliases for readability
-- ============================================================
-- ============================================================
-- 7.1 INNER JOIN — Only matching rows
-- ============================================================
-- Returns rows only where the join condition matches in BOTH tables.
-- Get orders with customer details
SELECT
o.order_id,
o.order_status,
o.order_purchase_timestamp,
c.customer_city,
c.customer_state
FROM orders o
INNER JOIN customers c ON o.customer_id = c.customer_id
LIMIT 10;
-- Get order items with product details
SELECT
oi.order_id,
oi.price,
p.product_category_name,
p.product_weight_g
FROM order_items oi
INNER JOIN products p ON oi.product_id = p.product_id
LIMIT 10;
-- ============================================================
-- 7.2 LEFT JOIN — All from left, matches from right
-- ============================================================
-- Returns ALL rows from the left table.
-- If no match in right table, NULL is returned.
-- All products, even those never sold
SELECT
p.product_id,
p.product_category_name,
oi.order_id,
oi.price
FROM products p
LEFT JOIN order_items oi ON p.product_id = oi.product_id
LIMIT 20;
-- Find products that were NEVER sold
SELECT
p.product_id,
p.product_category_name
FROM products p
LEFT JOIN order_items oi ON p.product_id = oi.product_id
WHERE oi.order_id IS NULL
LIMIT 10;
-- Count unsold products
SELECT COUNT(*) AS unsold_products
FROM products p
LEFT JOIN order_items oi ON p.product_id = oi.product_id
WHERE oi.order_id IS NULL;
-- ============================================================
-- 7.3 RIGHT JOIN — All from right, matches from left
-- ============================================================
-- Returns ALL rows from the right table.
-- Less commonly used — you can always rewrite as a LEFT JOIN.
-- All order items with their reviews (if any)
SELECT
oi.order_id,
oi.product_id,
oi.price,
r.review_score
FROM order_reviews r
RIGHT JOIN order_items oi ON r.order_id = oi.order_id
LIMIT 20;
-- Equivalent LEFT JOIN version (preferred):
SELECT
oi.order_id,
oi.product_id,
oi.price,
r.review_score
FROM order_items oi
LEFT JOIN order_reviews r ON oi.order_id = r.order_id
LIMIT 20;
-- ============================================================
-- 7.4 FULL OUTER JOIN — All from both
-- ============================================================
-- Returns ALL rows from BOTH tables.
-- NULLs where there's no match on either side.
-- Note: SQLite doesn't support FULL OUTER JOIN natively.
-- Find mismatches between orders and payments
-- (orders without payment OR payments without orders)
SELECT
o.order_id AS order_order_id,
p.order_id AS payment_order_id,
o.order_status,
p.payment_value
FROM orders o
FULL OUTER JOIN order_payments p ON o.order_id = p.order_id
WHERE o.order_id IS NULL OR p.order_id IS NULL
LIMIT 20;
-- ============================================================
-- 7.5 CROSS JOIN — Cartesian product
-- ============================================================
-- Every row from left × every row from right.
-- Rarely used but powerful for generating combinations.
-- All possible state pairs (e.g., for a distance matrix)
SELECT DISTINCT
c.customer_state AS buyer_state,
s.seller_state AS seller_state
FROM (SELECT DISTINCT customer_state FROM customers) c
CROSS JOIN (SELECT DISTINCT seller_state FROM sellers) s
ORDER BY buyer_state, seller_state
LIMIT 20;
-- ============================================================
-- 7.6 SELF JOIN — Joining a table to itself
-- ============================================================
-- Useful for comparing rows within the same table.
-- Find pairs of sellers in the same city
SELECT
s1.seller_id AS seller_1,
s2.seller_id AS seller_2,
s1.seller_city AS city,
s1.seller_state AS state
FROM sellers s1
INNER JOIN sellers s2
ON s1.seller_city = s2.seller_city
AND s1.seller_state = s2.seller_state
AND s1.seller_id < s2.seller_id -- avoid duplicates and self-pairs
LIMIT 15;
-- ============================================================
-- 7.7 MULTI-TABLE JOINS — Combining 3+ tables
-- ============================================================
-- Full order details: customer + order + items + product
SELECT
c.customer_state,
c.customer_city,
o.order_id,
o.order_status,
oi.price,
oi.freight_value,
p.product_category_name
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
LIMIT 10;
-- Complete order view: customer → order → item → seller → product → review
SELECT
c.customer_state AS buyer_state,
s.seller_state AS seller_state,
p.product_category_name,
oi.price,
r.review_score,
o.order_purchase_timestamp
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
JOIN sellers s ON oi.seller_id = s.seller_id
LEFT JOIN order_reviews r ON o.order_id = r.order_id
WHERE o.order_status = 'delivered'
LIMIT 10;
-- Product categories with English names
SELECT
p.product_category_name,
t.product_category_name_english AS category_english,
COUNT(*) AS items_sold,
SUM(oi.price) AS total_revenue
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
LEFT JOIN product_category_name_translation t
ON p.product_category_name = t.product_category_name
WHERE p.product_category_name IS NOT NULL
GROUP BY p.product_category_name, t.product_category_name_english
ORDER BY total_revenue DESC
LIMIT 15;
-- ============================================================
-- 7.8 JOIN FOR ANALYTICS — Real-world patterns
-- ============================================================
-- Cross-state commerce: Where do sellers ship to?
SELECT
s.seller_state AS from_state,
c.customer_state AS to_state,
COUNT(*) AS num_orders,
SUM(oi.price) AS total_revenue
FROM order_items oi
JOIN orders o ON oi.order_id = o.order_id
JOIN customers c ON o.customer_id = c.customer_id
JOIN sellers s ON oi.seller_id = s.seller_id
GROUP BY s.seller_state, c.customer_state
ORDER BY num_orders DESC
LIMIT 15;
-- Average review by product category (English names)
SELECT
COALESCE(t.product_category_name_english, p.product_category_name) AS category,
COUNT(r.review_id) AS num_reviews,
AVG(r.review_score) AS avg_score
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
JOIN orders o ON oi.order_id = o.order_id
LEFT JOIN order_reviews r ON o.order_id = r.order_id
LEFT JOIN product_category_name_translation t
ON p.product_category_name = t.product_category_name
WHERE p.product_category_name IS NOT NULL
GROUP BY COALESCE(t.product_category_name_english, p.product_category_name)
HAVING COUNT(r.review_id) > 100
ORDER BY avg_score DESC
LIMIT 10;
-- ============================================================
-- EXERCISES
-- ============================================================
-- Exercise 1: Join orders with order_payments. Show order_id,
-- order_status, payment_type, and payment_value.
-- Only show delivered orders. Limit to 15 rows.
-- Exercise 2: Find all sellers who have never sold anything.
-- (Hint: LEFT JOIN + IS NULL)
-- Exercise 3: For each customer state, show the total number of
-- orders and total payment value. Order by value DESC.
-- Exercise 4: Join order_items with products and translations.
-- Show the English category name, count of items sold,
-- and average price. Show top 10 by items sold.
-- Exercise 5: Find the top 5 seller-customer state pairs with
-- the highest total revenue.
-- ============================================================
-- SOLUTIONS
-- ============================================================
-- Exercise 1
SELECT
o.order_id,
o.order_status,
p.payment_type,
p.payment_value
FROM orders o
JOIN order_payments p ON o.order_id = p.order_id
WHERE o.order_status = 'delivered'
LIMIT 15;
-- Exercise 2
SELECT
s.seller_id,
s.seller_city,
s.seller_state
FROM sellers s
LEFT JOIN order_items oi ON s.seller_id = oi.seller_id
WHERE oi.order_id IS NULL;
-- Exercise 3
SELECT
c.customer_state,
COUNT(DISTINCT o.order_id) AS num_orders,
SUM(p.payment_value) AS total_payment
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN order_payments p ON o.order_id = p.order_id
GROUP BY c.customer_state
ORDER BY total_payment DESC;
-- Exercise 4
SELECT
COALESCE(t.product_category_name_english, p.product_category_name) AS category,
COUNT(*) AS items_sold,
AVG(oi.price) AS avg_price
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
LEFT JOIN product_category_name_translation t
ON p.product_category_name = t.product_category_name
WHERE p.product_category_name IS NOT NULL
GROUP BY COALESCE(t.product_category_name_english, p.product_category_name)
ORDER BY items_sold DESC
LIMIT 10;
-- Exercise 5
SELECT
s.seller_state AS seller_state,
c.customer_state AS customer_state,
SUM(oi.price) AS total_revenue
FROM order_items oi
JOIN orders o ON oi.order_id = o.order_id
JOIN customers c ON o.customer_id = c.customer_id
JOIN sellers s ON oi.seller_id = s.seller_id
GROUP BY s.seller_state, c.customer_state
ORDER BY total_revenue DESC
LIMIT 5;