ปกติแล้วงานวิเคราะห์ข้อมูลด้วย SQL มักจะใช้การเขียน SQL ระดับพื้นฐาน แต่ถ้าเราอยากใช้ SQL ในระดับที่ซับซ้อนมากขึ้น เราจะเริ่มเข้าไปทำในส่วนของการวิเคราะห์หาสาเหตุและเปรียบเทียบเชิงลึก (Diagnostic analysis) เพื่อให้ได้คำตอบที่สามารถเชื่อมโยงกับเป้าหมายทางธุรกิจได้ชัดเจน สำคัญที่สุดคือวิธีคิดและรู้จักการใช้ฟังก์ชันสนับสนุนเป้าหมายในการวิเคราะห์ข้อมูล โดยตัวอย่างคำถามเชิง Diagnostic analysis เช่น
- ลูกค้าเลิกใช้บริการเพราะอะไร?
- สินค้ากลุ่มใดที่ทำให้ยอดขายตก?
- ผู้ใช้งานกลุ่มไหนที่มีแนวโน้มจะกลับมาใช้ซ้ำ?
ถ้าสามารถเขียน SQL เพื่อหาคำตอบข้างต้นได้ก็แปลว่าเราใช้ SQL ได้เก่งมากขึ้นแล้ว
ลองมาทดสอบกันหน่อยไหม ว่าเราใช้ SQL ได้เก่งแค่ไหน ถ้ายังไม่มั่นใจอ่านต่อในสรุปรูปแบบการใช้ SQL สำหรับ Diagnostic analysis ทั้ง 4 รูปแบบได้เลย
อยากฝึกโจทย์ Data แนวไหนอีก ? ทักมาบอกทีมงานเราได้ที่ Post นี้เลย – ทีม Datayolk
ในบทความนี้มีการใช้ https://sqliteonline.com/ เพื่อให้ผู้อ่านสามารถคลิกลิ้งค์, ลองเขียน SQL และลองรันผลลัพธ์ตามได้เลย หากรันผลลัพธ์ไม่ได้สามารถทักมาสอบถามเราได้อีกทีนะ
วิธีคิดในการใช้ SQL สำหรับ Diagnostic Analysis
บริษัทค้าปลีกเก็บข้อมูลไว้ 3 ตารางคือตารางลูกค้า (customers) ตารางรายการซื้อ (sales) และตารางสินค้า (products)
ตาราง customers
customer_id | name | region | join_date |
---|---|---|---|
1 | ชาย | North | 2023-01-15 |
2 | หญิง | South | 2023-02-10 |
3 | ศรี | East | 2023-01-20 |
4 | ชัย | West | 2023-03-05 |
ตาราง sales
sale_id | customer_id | sale_date | product_id | quantity | amount |
---|---|---|---|---|---|
101 | 1 | 2024-01-10 | 10 | 2 | 350.0 |
102 | 2 | 2024-01-15 | 20 | 1 | 800.0 |
103 | 3 | 2024-01-20 | 30 | 5 | 125.0 |
104 | 4 | 2024-01-25 | 40 | 3 | 1200.0 |
105 | 1 | 2024-03-10 | 10 | 2 | 400.0 |
106 | 2 | 2024-03-15 | 20 | 1 | 600.0 |
ตาราง products
product_id | product_name | category | price |
---|---|---|---|
10 | ทีวี | Electronics | 175.0 |
20 | โน๊ตบุ๊ค | Electronics | 800.0 |
30 | เสื้อ | Fashion | 25.0 |
40 | ตู้เย็น | Home | 400.0 |
ฝ่ายขายไม่ต้องการแค่ดึงข้อมูลพื้นฐานเช่น ยอดขายรวมเท่าไหร่ แต่ต้องการใช้วิธี Diagnostic analysis เพื่อวิเคราะห์เจาะลึกว่า
- เดือนไหนขายดีที่สุด? จัดประเภทตามสินค้าแต่ละรายการ แต่ละภูมิภาค
- พฤติกรรมลูกค้าแต่ละกลุ่มตามยอดซื้อของเป็นอย่างไร? โดยที่มีการแบ่งกลุ่มประเภทลูกค้า (Segment) ตามเงื่อนไขยอดซื้อรวมน้อยกว่า 500 จัดเป็น Normal และยอดซื้อรวมมากกว่าหรือเท่ากับ 500 จัดเป็น Premium
- เรียงลำดับลูกค้าที่มียอดซื้อของในแต่ละ Segment
ตัวอย่างตารางแสดงผลลัพธ์ที่ฝ่ายขายต้องการ
region | month | category | total_sales | customer_segment | rank_in_segment |
---|---|---|---|---|---|
East | 2024-01 | Fashion | 125 | ลูกค้าทั่วไป | 3 |
North | 2024-01 | Electronics | 350 | ลูกค้าทั่วไป | 2 |
South | 2024-01 | Electronics | 800 | ลูกค้าพรีเมียม | 2 |
West | 2024-01 | Home | 1200 | ลูกค้าพรีเมียม | 1 |
North | 2024-03 | Electronics | 400 | ลูกค้าทั่วไป | 1 |
South | 2024-03 | Electronics | 600 | ลูกค้าพรีเมีย | 3 |
ถึงตรงนี้ลองเขียนคำสั่ง SQL ให้ได้ผลลัพธ์ตามตารางนี้ได้เลย ถ้าแก้โจทย์ข้อนี้ได้คุณคือผู้ที่ใช้งาน SQL ในการทำงานจริง แต่ถ้ายังไม่รู้วิธีเขียนคำสั่ง SQL อย่างไรถึงจะได้ตารางสรุปข้อมูลแบบนี้
เราลองแตกปัญหาออกมาเป็นประเด็นย่อย ๆ ให้ง่ายต่อการทำงาน ซึ่งสามารถสรุปได้ 4 ประเด็นดังนี้
1. วิเคราะห์ลงรากลึกถึงสิ่งที่เกิดขึ้น (Root Cause Analysis)
คำถามส่วนใหญ่มักเป็น
- ช่วงไหนที่ยอดขายตก/ ขายดี?
- สินค้าไหนมียอดขายลดลง / เพิ่มขึ้น?
- ยอดขายส่วนใหญ่มาจากกลุ่มลูกค้ากลุ่มใด?
รูปแบบการเขียน SQL โดยใช้ GROUP BY
ใช้ GROUP BY
จัดหมวดหมู่เช่น เวลา ภูมิภาค ประเภทสินค้าเพื่อเปรียบเทียบจำนวน นอกจากนี้ในการทำงานจริงมักมีเงื่อนไข เช่น ใช้คู่กับ HAVING
เพื่อกรองข้อมูลหลัง GROUP BY
ซึ่งมีความต่างจาก WHERE
ที่ใช้กรองก่อนการจัดหมวดหมู่
ลองเขียน SQL ด้วยการใช้ GROUP BY
ลองเขียน SQL เองก่อนดูเฉลยข้อที่ 1 ที่ SQLite Editor
ตาราง Input : sales
sale_id | category | region | sale_date | sales_amount |
---|---|---|---|---|
1 | Electronics | North | 2024-01-05 | 300,000 |
2 | Electronics | North | 2024-01-15 | 500,000 |
3 | Electronics | North | 2024-01-25 | 400,000 |
4 | Electronics | North | 2024-02-10 | 850,000 |
5 | Electronics | North | 2024-03-20 | 600,000 |
6 | Fashion | South | 2024-01-12 | 450,000 |
7 | Fashion | South | 2024-01-25 | 450,000 |
8 | Fashion | South | 2024-02-20 | 870,000 |
9 | Fashion | South | 2024-03-05 | 500,000 |
10 | Fashion | South | 2024-03-18 | 550,000 |
ตาราง Output ที่ต้องการ
แสดงยอดขายรวมตาม category, region, และ month โดยที่ total_sales ≥ 900,000
category | region | month | total_sales |
---|---|---|---|
Electronics | North | 2024-01-01 | 1,200,000 |
Fashion | South | 2024-01-01 | 900,000 |
Fashion | South | 2024-03-01 | 1,050,000 |
เฉลยวิธีการเขียน SQL ข้อแรก
SELECT
category,
region,
strftime('%Y-%m', sale_date) AS month,
SUM(sales_amount) AS total_sales
FROM sales
GROUP BY category, region, strftime('%Y-%m', sale_date)
HAVING SUM(sales_amount) >= 900000;
ขั้นตอนการคิดอย่างละเอียด
- เลือกสิ่งที่อยากเห็นใน Select Clause
category ✅
region ✅
month -- ใช้ฟังก์ชัน DATE_TRUNC()
total_sales -- ใช้ฟังก์ชัน SUM()
- หา month ด้วยการลดทอน sale_date ให้เหลือระดับ “month”
- กรณี SQLite ใช้ strftime
strftime('%Y-%m', sale_date) AS month
- กรณี PostgreSQL หรือ DuckDB ใช้ DATE_TRUNC
DATE_TRUNC('month', sale_date) AS month
- กรณี SQLite ใช้ strftime
- หา total_sales รวมยอดขายด้วยฟังก์ชัน
SUM()
SUM(sales_amount) AS total_sales
- นี่คือข้อมูลที่แสดงจะใน Select Clause
SELECT
category,
region,
DATE_TRUNC('month', sale_date) AS month
SUM(sales_amount) AS total_sales
FROM sales;
- ต้องการดูแยกตาม category / region / month ใช้ฟังก์ชัน
GROUP BY
SELECT
category,
region,
DATE_TRUNC('month', sale_date) AS month
SUM(sales_amount) AS total_sales
FROM sales
GROUP BY category, region, DATE_TRUNC('month', sale_date);
- กรองเฉพาะกลุ่มที่มียอดขายรวมมากกว่าหรือเท่ากับ 900,000
SELECT
category,
region,
DATE_TRUNC('month', sale_date) AS month,
SUM(sales_amount) AS total_sales
FROM sales
GROUP BY category, region, DATE_TRUNC('month', sale_date)
HAVING SUM(sales_amount) >= 900000;
2. การจัดหมวดหมู่ (Segmentation)
คำถามส่วนใหญ่มักเป็น
- ผู้ใช้แบบไหนมีแนวโน้มจะ churn มากที่สุด?
- ผู้ใช้ฟรีมีพฤติกรรมต่างจากผู้เสียเงินหรือไม่?
- ผู้ใช้แต่ละกลุ่มใช้แอปบ่อยแค่ไหน?
รูปแบบการเขียน SQL ใช้ CASE WHEN
ใช้ CASE WHEN
เพื่อสร้างกลุ่มตามเงื่อนไข
- ลูกค้าที่มียอดค่าใช้จ่ายมากกว่า 0 คือ Paying User
- ถ้ายอดค่าใช้จ่ายเท่ากับ 0 คือ Free User
ลองเขียน SQL ด้วยการใช้ CASE WHEN
ลองเขียน SQL เองก่อนดูเฉลยข้อที่ 2 ที่ SQLite Editor
ตาราง Input: user_sessions
user_id | session_id | spend | session_date |
---|---|---|---|
101 | s001 | 0 | 2024-01-10 |
101 | s002 | 0 | 2024-01-15 |
102 | s003 | 120 | 2024-01-11 |
103 | s004 | 0 | 2024-01-12 |
104 | s005 | 500 | 2024-01-13 |
104 | s006 | 120 | 2024-01-20 |
ตาราง Output ที่ต้องการ
จัดกลุ่ม user_segment โดย
- ลูกค้าที่มียอดค่าใช้จ่ายมากกว่า 0 คือ Paying User
- ถ้ายอดค่าใช้จ่ายเท่ากับ 0 คือ Free User
user_id | user_segment | sessions |
---|---|---|
101 | Free User | 2 |
102 | Paying User | 1 |
103 | Free User | 1 |
104 | Paying User | 2 |
เฉลยวิธีการเขียน SQL ข้อสอง
SELECT
user_id,
CASE
WHEN spend = 0 THEN 'Free User'
WHEN spend > 0 THEN 'Paying User'
END AS user_segment,
COUNT(DISTINCT session_id) AS sessions
FROM user_sessions
GROUP BY user_id, user_segment;
ขั้นตอนการคิดอย่างละเอียด
- เลือกสิ่งที่อยากเห็นใน Select Clause
user_id ✅
user_segment -- ใช้ Control Flow Statement
sessions -- ใช้ฟังก์ชัน COUNT(DISTINCT )
- สร้าง user_segment ด้วยการใช้
CASE WHEN
เพื่อสร้างกลุ่มตามเงื่อนไข ลูกค้าที่มียอดค่าใช้จ่ายมากกว่า 0 คือ Paying User ถ้ายอดค่าใช้จ่ายเท่ากับ 0 คือ Free User
CASE
WHEN spend = 0 THEN 'Free User'
WHEN spend > 0 THEN 'Paying User'
END AS user_segment
- คำนวณ sessions ด้วยการนับ session_id ที่ไม่ซ้ำกันด้วยฟังก์ชัน COUNT(DISTINCT )
COUNT(DISTINCT session_id) AS sessions
- นี่คือข้อมูลที่แสดงจะใน Select Clause
SELECT
user_id,
CASE
WHEN spend = 0 THEN 'Free User'
WHEN spend > 0 THEN 'Paying User'
END AS user_segment,
COUNT(DISTINCT session_id) AS sessions
FROM user_sessions;
- ต้องการดูแยกตาม user_id และ user_segment ใช้ฟังก์ชัน
GROUP BY
SELECT
user_id,
CASE
WHEN spend = 0 THEN 'Free User'
WHEN spend > 0 THEN 'Paying User'
END AS user_segment,
COUNT(DISTINCT session_id) AS sessions
FROM user_sessions
GROUP BY user_id, user_segment;
3. เปรียบเทียบสิ่งที่เกิดขึ้นตามช่วงเวลา
คำถามส่วนใหญ่มักเป็น
- ลูกค้ากลับมาซื้อสินค้าอีกครั้งภายในกี่วัน นับตั้งแต่วันที่ซื้อสินค้าครั้งล่าสุด?
- ลูกค้าคนไหนมาซื้อซ้ำเร็วที่สุด?
- ใครคือ Top 3 ผู้ใช้ที่มีจำนวน session สูงสุดในแต่ละเดือน?
- ยอดขายสินค้าตัวใดตกจากอันดับ 1 เป็นอันดับ 2?
รูปแบบการเขียน SQL ใช้ Window Function
ใช้ LAG()
, RANK()
, DENSE_RANK()
,ROW_NUMBER()
ช่วยให้เราสามารถเปรียบเทียบข้อมูลของแถวก่อนหน้า หรือจัดอันดับเพื่อดูการเปลี่ยนแปลงพฤติกรรมแบบลำดับเวลาได้
LAG()
ใช้เปรียบเทียบกับค่าจากแถวก่อนหน้า เช่น คำนวณว่าเดือนนี้ยอดขายเพิ่มหรือลดลงจากเดือนที่แล้ว (sales_diff)
month | sales_amount | prev_month_sales | sales_diff |
---|---|---|---|
2024-01 | 10000 | (null) | (null) |
2024-02 | 12000 | 10000 | +2000 |
2024-03 | 9000 | 12000 | -3000 |
2024-04 | 9500 | 9000 | +500 |
2024-05 | 11000 | 9500 | +1500 |
RANK()
ใช้เพื่อจัดอันดับแบบข้ามลำดับเมื่อมีค่าซ้ำ เช่น ลำดับยอดขายสูงสุดในแต่ละภูมิภาค มียอดขายสูงสุดคือ 1000 ได้เท่ากัน 2 แถว ให้จัดอยู่ใน Rank 1 เท่ากัน และให้นับข้ามอันดับ 2 ไป
region | salesperson | sales | rank_in_region |
---|---|---|---|
East | John | 1000 | 1 |
East | Jane | 1000 | 1 |
East | Tom | 800 | 3 |
DENSE_RANK()
ใช้เพื่อจัดอันดับแบบไม่ข้ามลำดับเมื่อมีค่าซ้ำ เช่น ลำดับยอดขายสูงสุดในแต่ละภูมิภาค มียอดขายสูงสุดคือ 1000 ได้เท่ากัน 2 แถว ให้จัดอยู่ใน Rank 1 เท่ากัน และให้อันดับต่อไปจัดอยู่ใน Rank 2
region | salesperson | sales | dense_rank_in_region |
---|---|---|---|
East | John | 1000 | 1 |
East | Jane | 1000 | 1 |
East | Tom | 800 | 2 |
แล้วถ้าต้องการหา Top 1 อย่างเช่น salesperson ที่ทำยอดขาย Top 1 คือใครควรใช้ Window Function อะไรดี? ก่อนอื่นเลยขึ้นอยู่กับความต้องการของธุรกิจ ถ้า
- ใช้
ROW_NUMBER()
เป็นฟังก์ที่ไม่ลำดับซ้ำ ถ้าต้องการ Top 1 คนเดียว เช่น Delivery App หา Top 1 ไรเดอร์หนึ่งคนที่ส่งของได้มากที่สุดในหนึ่งวัน ถ้าได้คะแนนเท่ากันให้วัดที่เวลา ใครส่งออเดอร์สุดท้ายก่อนให้แสดง Top 1 เป็นคนนั้น - จะใช้
RANK()
หรือDENSE_RANK()
ก็ได้ ถ้าต้องการ Top 1 ทุกคนที่ได้คะแนนสูงสุดเท่ากัน
ลองเขียน SQL ด้วยการใช้ Window Function
ลองเขียน SQL เองก่อนดูเฉลยข้อที่ 3 ที่ SQLite Editor
ตาราง Input: events
user_id | event_date |
---|---|
101 | 2024-01-01 |
101 | 2024-01-10 |
101 | 2024-01-20 |
102 | 2024-01-05 |
102 | 2024-01-15 |
103 | 2024-01-03 |
103 | 2024-01-30 |
ตาราง Output ที่ต้องการ
user_id | event_date | event_order |
---|---|---|
101 | 2024-01-01 | 1 |
101 | 2024-01-10 | 2 |
101 | 2024-01-20 | 3 |
102 | 2024-01-05 | 1 |
102 | 2024-01-15 | 2 |
103 | 2024-01-03 | 1 |
103 | 2024-01-30 | 2 |
เฉลยวิธีการเขียน SQL ข้อสาม
SELECT
user_id,
event_date,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_date) AS event_order
FROM events;
ขั้นการการคิดอย่างละเอียด
- เลือกสิ่งที่อยากเห็นใน Select Clause
user_id ✅
event_date ✅
event_order -- ใช้ Window Function
- หา event_order ด้วยการดึงค่าแสดงลำดับของการเกิด event ของแต่ละผู้ใช้ (user_id) ด้วย
ROW_NUMBER()
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_date) AS event_order
/* ใช้ ROW_NUMBER() เพื่อดึงลำดับการเกิด event
PARTITION BY user_id → คำนวณแยกกันในแต่ละ user
ORDER BY event_date → เรียงเวลาเพื่อหาค่าก่อนหน้าอย่างถูกต้อง */
- นี่คือข้อมูลที่แสดงจะใน Select Clause
SELECT
user_id,
event_date,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_date) AS event_order
FROM events;
4. การทดสอบสมมติฐานแบบเร็ว ๆ
คำถามส่วนใหญ่มักเป็น
- ผู้ใช้ที่รับโค้ดส่วนลด (promo code) มีโอกาสซื้อสินค้ามากกว่าผู้ที่ไม่ได้รับส่วนลดจริงหรือไม่?
- ส่วนใหญ่มักเป็นคนที่เจอ bug ในหน้า checkout มีโอกาสซื้อได้น้อยลงจริงไหม?
รูปแบบการเขียน SQL ใช้การ Joins
ใช้ JOIN
เพื่อเชื่อมโยงข้อมูลจากตาราง
ลองเขียน SQL ด้วยการใช้ Joins
ลองเขียน SQL เองก่อนดูเฉลยข้อที่ 4 ที่ SQLite Editor
ตาราง Input: user_events
user_id | event | event_date |
---|---|---|
1 | promo_code | 2024-03-01 10:00:00 |
1 | purchase | 2024-03-01 10:10:00 |
2 | promo_code | 2024-03-02 11:00:00 |
3 | browse | 2024-03-03 09:00:00 |
3 | purchase | 2024-03-03 09:15:00 |
4 | promo_code | 2024-03-04 13:00:00 |
5 | browse | 2024-03-05 15:00:00 |
ตาราง Output ที่ต้องการ
received_promo | total_users | purchases | conversion_rate |
---|---|---|---|
1 | 3 | 1 | 33.33 |
0 | 2 | 1 | 50.00 |
เฉลยวิธีการเขียน SQL ข้อสี่
SELECT
CASE WHEN promo.user_id IS NOT NULL THEN 1 ELSE 0 END AS received_promo,
COUNT(DISTINCT users.user_id) AS total_users,
COUNT(DISTINCT pur.user_id) AS purchases,
ROUND(COUNT(DISTINCT pur.user_id) * 100.0 / COUNT(DISTINCT users.user_id), 2) AS conversion_rate
FROM
(SELECT DISTINCT user_id FROM user_events) AS users
LEFT JOIN
(SELECT DISTINCT user_id FROM user_events WHERE event = 'promo_code') AS promo
ON users.user_id = promo.user_id
LEFT JOIN
(SELECT DISTINCT user_id FROM user_events WHERE event = 'purchase') AS pur
ON users.user_id = pur.user_id
GROUP BY
CASE WHEN promo.user_id IS NOT NULL THEN 1 ELSE 0 END;
ขั้นตอนการคิดอย่างละเอียด
- เลือกสิ่งที่อยากเห็นใน Select Clause
- ผู้ใช้กลุ่มไหนเคยได้รับโค้ดโปรโมชัน (promo_code) บ้าง?
- ในแต่ละกลุ่ม (ได้โปร / ไม่ได้โปร) มีผู้ใช้งานทั้งหมดกี่คน?
- มีผู้ใช้งานที่จ่ายเงินกี่คน?
- อัตราการซื้อ (conversion rate) เท่าไหร่?
- ดึงข้อมูล user_id ที่ไม่มีการนับซ้ำ
(SELECT DISTINCT user_id FROM user_events) AS users
- เชื่อมตารางเพื่อเช็คว่ามี user_id ที่เคยได้รับ promo_code ด้วย Left Join
LEFT JOIN
(SELECT DISTINCT user_id FROM user_events WHERE event = 'promo_code') AS promo
ON users.user_id = promo.user_id
- เชื่อมตารางเพื่อเช็คว่ามี user_id ที่เคยจ่ายเงิน
LEFT JOIN
(SELECT DISTINCT user_id FROM user_events WHERE event = 'purchase') AS pur
ON users.user_id = pur.user_id
- แบ่งกลุ่มผู้ใช้งาน: ถ้าลูกค้าที่ได้รับ promo_code คือเจอ promo.user_id จะจัดเป็น 1 ถ้าไม่เจอ (NULL) คือลูกค้าที่ไม่ได้รับ promo_code ให้จัดเป็นเป็น 0
CASE
WHEN promo.user_id IS NOT NULL THEN 1
ELSE 0
END AS received_promo
- คำนวณจำนวนผู้ใช้และการซื้อในแต่ละกลุ่ม
COUNT(DISTINCT users.user_id) AS total_users, -- ผู้ใช้งานทั้งหมด
COUNT(DISTINCT pur.user_id) AS purchases --ผู้ใช้งานที่จ่ายเงิน
- คำนวณอัตราการซื้อสินค้าด้วย Conversion Rate = (purchases / total_users) × 100
ROUND(COUNT(DISTINCT pur.user_id) * 100.0 / COUNT(DISTINCT users.user_id), 2) AS conversion_rate
- นี่คือข้อมูลที่แสดงจะใน Select Clause
SELECT
CASE WHEN promo.user_id IS NOT NULL THEN 1 ELSE 0 END AS received_promo,
COUNT(DISTINCT users.user_id) AS total_users,
COUNT(DISTINCT pur.user_id) AS purchases,
ROUND(COUNT(DISTINCT pur.user_id) * 100.0 / COUNT(DISTINCT users.user_id), 2) AS conversion_rate
FROM
(SELECT DISTINCT user_id FROM user_events) AS users
LEFT JOIN
(SELECT DISTINCT user_id FROM user_events WHERE event = 'promo_code') AS promo
ON users.user_id = promo.user_id
LEFT JOIN
(SELECT DISTINCT user_id FROM user_events WHERE event = 'purchase') AS pur
ON users.user_id = pur.user_id;
- ต้องจัดกลุ่มผู้ใช้งานที่ได้ promo_code กับกลุ่มที่ไม่ได้ promo_code
SELECT
CASE WHEN promo.user_id IS NOT NULL THEN 1 ELSE 0 END AS received_promo,
COUNT(DISTINCT users.user_id) AS total_users,
COUNT(DISTINCT pur.user_id) AS purchases,
ROUND(COUNT(DISTINCT pur.user_id) * 100.0 / COUNT(DISTINCT users.user_id), 2) AS conversion_rate
FROM
(SELECT DISTINCT user_id FROM user_events) AS users
LEFT JOIN
(SELECT DISTINCT user_id FROM user_events WHERE event = 'promo_code') AS promo
ON users.user_id = promo.user_id
LEFT JOIN
(SELECT DISTINCT user_id FROM user_events WHERE event = 'purchase') AS pur
ON users.user_id = pur.user_id
GROUP BY
CASE WHEN promo.user_id IS NOT NULL THEN 1 ELSE 0 END;
ลองทำโจทย์ธุรกิจด้วยตัวเอง (1)
กลับมาโจทย์ของบริษัทค้าปลีกกันอีกครั้ง เพื่อที่จะได้ผลลัพธ์ตามในตารางแสดงผลลัพธ์ที่ฝ่ายขายต้องการ จะต้องเขียน SQL อย่างไร ?
ลองเขียน SQL เองก่อนดูเฉลยที่ SQLite Editor
ตาราง Output
region | month | category | total_sales | customer_segment | rank_in_segment |
---|---|---|---|---|---|
East | 2024-01 | Fashion | 125 | ลูกค้าทั่วไป | 3 |
North | 2024-01 | Electronics | 350 | ลูกค้าทั่วไป | 2 |
South | 2024-01 | Electronics | 800 | ลูกค้าพรีเมียม | 2 |
West | 2024-01 | Home | 1200 | ลูกค้าพรีเมียม | 1 |
North | 2024-03 | Electronics | 400 | ลูกค้าทั่วไป | 1 |
South | 2024-03 | Electronics | 600 | ลูกค้าพรีเมีย | 3 |
เฉลยวิธีการเขียน SQL ลงมือโจทย์ธุรกิจด้วยตัวเอง (1)
SELECT
t.region,
t.month,
t.category,
t.total_sales,
t.customer_segment,
RANK() OVER (
PARTITION BY t.customer_segment
ORDER BY t.total_sales DESC
) AS rank_in_segment
FROM (
SELECT
c.customer_id,
c.region,
strftime('%Y-%m', s.sale_date) AS month,
p.category,
SUM(s.amount) AS total_sales,
CASE
WHEN SUM(s.amount) >= 500 THEN 'ลูกค้าพรีเมียม'
ELSE 'ลูกค้าทั่วไป'
END AS customer_segment
FROM sales s
JOIN customers c ON s.customer_id = c.customer_id
JOIN products p ON s.product_id = p.product_id
GROUP BY c.customer_id, c.region, strftime('%Y-%m', s.sale_date), p.category
) AS t
ORDER BY t.month, t.region;
ลองทำโจทย์ธุรกิจด้วยตัวเอง (2)
จากข้อก่อนหน้า ฝ่ายขายเห็นข้อมูลที่เจาะลึกจึงเกิดไอเดียส่งมอบบัตรกำนันดูหนังฟรีให้กับ Top spender 2 ท่านที่มียอดซื้อของสะสมสูงที่สุด
ลองเขียน SQL เองก่อนดูเฉลยที่ SQLite Editor
ตาราง Output
customer_id |
---|
2 |
4 |
เฉลยวิธีการเขียน SQL ลงมือโจทย์ธุรกิจด้วยตัวเอง (2)
SELECT customer_id
FROM (
SELECT
customer_id,
SUM(amount) AS total_spent,
RANK() OVER (ORDER BY SUM(amount) DESC) AS spending_rank
FROM sales
GROUP BY customer_id
) AS ranked
WHERE spending_rank <= 2;
จบกันไปแล้วสำหรับการวินิจฉัยข้อมูล (Diagnostic analysis) ด้วย SQL ซึ่งเป็นการลองทำโจทย์เพื่อให้คุ้นเคยกับวิธีคิดและการใช้งานฟังก์ชัน SQL ในการทำงานสายเดต้าในชีวิตจริง เพื่อน ๆ สามารถลองเอา 4 เทคนิคเหล่านี้ไปใช้ในงานวิเคราะห์ข้อมูล เชื่อว่าจะเพิ่มมุมมองให้กับธุรกิจได้มากกว่าเดิม เดี๋ยวครั้งหน้าจะชวนไปทำโจทย์อื่น ๆ ที่น่าสนใจและใช้บ่อย ๆ ในคนทำงานสายข้อมูลกัน
ภาพประกอบจาก Freepik
อยากมาทำงานสาย Data ใช่มั้ย?
เรามี Workshop Data Interview ที่จะช่วยคุณเตรียมพร้อมสำหรับการสัมภาษณ์งาน ผ่านการทำ Case Interview