จาก Analysis สู่ Diagnostic: ฝึกทำโจทย์วิเคราะห์ข้อมูลแบบเจาะลึกด้วย SQL

ปกติแล้วงานวิเคราะห์ข้อมูลด้วย 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_idnameregionjoin_date
1ชายNorth2023-01-15
2หญิงSouth2023-02-10
3ศรีEast2023-01-20
4ชัยWest2023-03-05

ตาราง sales

sale_idcustomer_idsale_dateproduct_idquantityamount
10112024-01-10102350.0
10222024-01-15201800.0
10332024-01-20305125.0
10442024-01-254031200.0
10512024-03-10102400.0
10622024-03-15201600.0

ตาราง products

product_idproduct_namecategoryprice
10ทีวีElectronics175.0
20โน๊ตบุ๊คElectronics800.0
30เสื้อFashion25.0
40ตู้เย็นHome400.0

ฝ่ายขายไม่ต้องการแค่ดึงข้อมูลพื้นฐานเช่น ยอดขายรวมเท่าไหร่ แต่ต้องการใช้วิธี Diagnostic analysis เพื่อวิเคราะห์เจาะลึกว่า

  • เดือนไหนขายดีที่สุด? จัดประเภทตามสินค้าแต่ละรายการ แต่ละภูมิภาค
  • พฤติกรรมลูกค้าแต่ละกลุ่มตามยอดซื้อของเป็นอย่างไร? โดยที่มีการแบ่งกลุ่มประเภทลูกค้า (Segment) ตามเงื่อนไขยอดซื้อรวมน้อยกว่า 500 จัดเป็น Normal และยอดซื้อรวมมากกว่าหรือเท่ากับ 500 จัดเป็น Premium
  • เรียงลำดับลูกค้าที่มียอดซื้อของในแต่ละ Segment

ตัวอย่างตารางแสดงผลลัพธ์ที่ฝ่ายขายต้องการ

regionmonthcategorytotal_salescustomer_segmentrank_in_segment
East2024-01Fashion125ลูกค้าทั่วไป3
North2024-01Electronics350ลูกค้าทั่วไป2
South2024-01Electronics800ลูกค้าพรีเมียม2
West2024-01Home1200ลูกค้าพรีเมียม1
North2024-03Electronics400ลูกค้าทั่วไป1
South2024-03Electronics600ลูกค้าพรีเมีย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_idcategoryregionsale_datesales_amount
1ElectronicsNorth2024-01-05300,000
2ElectronicsNorth2024-01-15500,000
3ElectronicsNorth2024-01-25400,000
4ElectronicsNorth2024-02-10850,000
5ElectronicsNorth2024-03-20600,000
6FashionSouth2024-01-12450,000
7FashionSouth2024-01-25450,000
8FashionSouth2024-02-20870,000
9FashionSouth2024-03-05500,000
10FashionSouth2024-03-18550,000

ตาราง Output ที่ต้องการ

แสดงยอดขายรวมตาม category, region, และ month โดยที่ total_sales ≥ 900,000

categoryregionmonthtotal_sales
ElectronicsNorth2024-01-011,200,000
FashionSouth2024-01-01900,000
FashionSouth2024-03-011,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
  • หา 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_idsession_idspendsession_date
101s00102024-01-10
101s00202024-01-15
102s0031202024-01-11
103s00402024-01-12
104s0055002024-01-13
104s0061202024-01-20

ตาราง Output ที่ต้องการ

จัดกลุ่ม user_segment โดย

  • ลูกค้าที่มียอดค่าใช้จ่ายมากกว่า 0 คือ Paying User
  • ถ้ายอดค่าใช้จ่ายเท่ากับ 0 คือ Free User
user_iduser_segmentsessions
101Free User2
102Paying User1
103Free User1
104Paying User2

เฉลยวิธีการเขียน 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)

monthsales_amountprev_month_salessales_diff
2024-0110000(null)(null)
2024-021200010000+2000
2024-03900012000-3000
2024-0495009000+500
2024-05110009500+1500

RANK() ใช้เพื่อจัดอันดับแบบข้ามลำดับเมื่อมีค่าซ้ำ เช่น ลำดับยอดขายสูงสุดในแต่ละภูมิภาค มียอดขายสูงสุดคือ 1000 ได้เท่ากัน 2 แถว ให้จัดอยู่ใน Rank 1 เท่ากัน และให้นับข้ามอันดับ 2 ไป

regionsalespersonsalesrank_in_region
EastJohn10001
EastJane10001
EastTom8003

DENSE_RANK() ใช้เพื่อจัดอันดับแบบไม่ข้ามลำดับเมื่อมีค่าซ้ำ เช่น ลำดับยอดขายสูงสุดในแต่ละภูมิภาค มียอดขายสูงสุดคือ 1000 ได้เท่ากัน 2 แถว ให้จัดอยู่ใน Rank 1 เท่ากัน และให้อันดับต่อไปจัดอยู่ใน Rank 2

regionsalespersonsalesdense_rank_in_region
EastJohn10001
EastJane10001
EastTom8002

แล้วถ้าต้องการหา 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_idevent_date
1012024-01-01
1012024-01-10
1012024-01-20
1022024-01-05
1022024-01-15
1032024-01-03
1032024-01-30

ตาราง Output ที่ต้องการ

user_idevent_dateevent_order
1012024-01-011
1012024-01-102
1012024-01-203
1022024-01-051
1022024-01-152
1032024-01-031
1032024-01-302

เฉลยวิธีการเขียน 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_ideventevent_date
1promo_code2024-03-01 10:00:00
1purchase2024-03-01 10:10:00
2promo_code2024-03-02 11:00:00
3browse2024-03-03 09:00:00
3purchase2024-03-03 09:15:00
4promo_code2024-03-04 13:00:00
5browse2024-03-05 15:00:00

ตาราง Output ที่ต้องการ

received_promototal_userspurchasesconversion_rate
13133.33
02150.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

regionmonthcategorytotal_salescustomer_segmentrank_in_segment
East2024-01Fashion125ลูกค้าทั่วไป3
North2024-01Electronics350ลูกค้าทั่วไป2
South2024-01Electronics800ลูกค้าพรีเมียม2
West2024-01Home1200ลูกค้าพรีเมียม1
North2024-03Electronics400ลูกค้าทั่วไป1
South2024-03Electronics600ลูกค้าพรีเมีย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