2. SELECT, FROM, WHERE

วัตถุประสงค์การเรียนรู้

เมื่อจบบทนี้ ผู้เรียนจะสามารถ

  1. เขียนคำสั่ง SELECT เพื่อเลือกคอลัมน์ที่ต้องการจากตาราง
  2. กรองแถวข้อมูลด้วย WHERE พร้อมตัวดำเนินการเปรียบเทียบ (=, >, <, >=, <=, <>)
  3. รวมเงื่อนไขหลายเงื่อนไขด้วย AND, OR และ NOT
  4. ใช้ LIKE และ ILIKE เพื่อค้นหารูปแบบข้อความ
  5. เรียงลำดับผลลัพธ์ด้วย ORDER BY ทั้งแบบ ASC และ DESC
  6. ใช้ IN เป็นทางลัดแทน OR หลายเงื่อนไข
  7. ใช้ BETWEEN เพื่อกรองข้อมูลในช่วงที่กำหนด
  8. ใช้ DISTINCT เพื่อหาค่าที่ไม่ซ้ำกันในคอลัมน์
  9. จัดการค่า NULL อย่างถูกต้องด้วย IS NULL และ IS NOT NULL
  10. ใช้ AS สำหรับกำหนด “ชื่อแทน” (alias)

บทนำ

บทนำ

ในบทที่ 1 ผู้เรียนได้ทำความรู้จักโครงสร้างฐานข้อมูลของ PiCha และตารางข้อมูลหลักทั้ง 6 ตารางไปแล้ว บทนี้จะพาผู้เรียนก้าวจากการทำความเข้าใจโครงสร้างข้อมูล ไปสู่การลงมือเขียนคำสั่ง SQL เพื่อตอบคำถามทางธุรกิจที่เกิดขึ้นจริง

ตัวอย่างเช่น ผู้บริหารอาจต้องการทราบว่า “ลูกค้าระดับ Gold ที่รู้จักแบรนด์ผ่าน TikTok มีใครบ้าง” เพื่อใช้วางแผนแคมเปญการตลาดเฉพาะกลุ่ม การแปลงคำถามทางธุรกิจลักษณะนี้ให้เป็นคำสั่ง SQL ที่ชัดเจน เป็นระบบ และทำซ้ำได้ ถือเป็นทักษะสำคัญที่จะช่วยให้การวิเคราะห์ข้อมูลมีประสิทธิภาพและนำไปใช้ประโยชน์ได้จริง

SELECT คำสั่งพื้นฐานสำหรับการดึงข้อมูล

คำสั่ง SELECT เป็นคำสั่งพื้นฐานและใช้บ่อยที่สุดใน SQL ซึ่งทำหน้าที่ดึงข้อมูลจากตารางในฐานข้อมูลมาแสดงผล โดยเป็นการดำเนินการแบบ read-only ที่ไม่ทำการเปลี่ยนแปลงข้อมูลต้นฉบับแต่อย่างใด ทำให้เหมาะสำหรับการวิเคราะห์ข้อมูลทางธุรกิจโดยไม่ต้องกังวลเรื่องความปลอดภัยของข้อมูล

คำสั่ง SELECT มีหลายรูปแบบการใช้งาน เริ่มจากรูปแบบพื้นฐานที่สุดคือ

  1. SELECT * สำหรับดึงข้อมูลทุกคอลัมน์

รูปแบบนี้ใช้เครื่องหมาย asterisk (*) เป็นตัวแทนของ “ทุกคอลัมน์” ในตาราง เหมาะสำหรับการสำรวจข้อมูลเบื้องต้น (exploratory data analysis) เมื่อต้องการทำความเข้าใจโครงสร้างและเนื้อหาของตารางอย่างรวดเร็ว

SELECT * FROM menus;

คำสั่งนี้จะดึงข้อมูลทุกคอลัมน์จากตาราง menus โดยไม่ต้องระบุชื่อคอลัมน์ทีละตัว ผลลัพธ์ที่ได้จะแสดงคอลัมน์ทั้งหมด 17 คอลัมน์ และทุกแถวของข้อมูลในตาราง menus ตามลำดับที่ถูกกำหนดในโครงสร้างตาราง โดยได้ผลลัพธ์ดังนี้

sku_id sku_name family signature_flag launch_branch_flag
1 Glutinous Green Milk Tea R milk_tea 1 0
2 Glutinous Green Milk Tea L milk_tea 1 0
3 Tie Guan Yin Milk Tea R milk_tea 1 0
4 Tie Guan Yin Milk Tea L milk_tea 1 0
5 Da Hong Pao Milk Tea R milk_tea 1 0
6 Da Hong Pao Milk Tea L milk_tea 1 0
7 Pu’er Milk Tea R milk_tea 0 0
8 Pu’er Milk Tea L milk_tea 0 0
9 Osmanthus Milk Tea R milk_tea 0 0
10 Osmanthus Milk Tea L milk_tea 0 0
11 Jasmine Milk Tea R milk_tea 1 0
12 Jasmine Milk Tea L milk_tea 1 0
13 White Peach Milk Tea R milk_tea 1 0
14 White Peach Milk Tea L milk_tea 1 0
15 Jasmine Grapefruit Tea L fruit_tea 1 0
16 Lemon Glutinous Green Tea L fruit_tea 1 0
17 Peach Jasmine Tea L fruit_tea 1 0
18 Fresh Orange Jasmine Tea L fruit_tea 1 0
19 Tie Guan Yin Pure Tea R pure_tea 0 0
20 Glutinous Green Pure Tea R pure_tea 0 0
21 Mango Sticky Rice Tea L fruit_tea 1 1
22 Thai Tea Frappe L blended 1 0
คลิกเพื่อดูตารางฉบับเต็ม
sku_id sku_name family tea_base milk_flag fruit_flag cold_brew_flag blended_flag size_code size_ml list_price_thb sweetness_min_pct sweetness_max_pct caffeine_level seasonal_flag signature_flag launch_branch_flag
1 Glutinous Green Milk Tea R milk_tea glutinous_green 1 0 0 0 R 500 85 0 100 medium 0 1 0
2 Glutinous Green Milk Tea L milk_tea glutinous_green 1 0 0 0 L 700 100 0 100 medium 0 1 0
3 Tie Guan Yin Milk Tea R milk_tea tie_guan_yin 1 0 0 0 R 500 110 0 100 high 0 1 0
4 Tie Guan Yin Milk Tea L milk_tea tie_guan_yin 1 0 0 0 L 700 130 0 100 high 0 1 0
5 Da Hong Pao Milk Tea R milk_tea da_hong_pao 1 0 0 0 R 500 110 0 100 high 0 1 0
6 Da Hong Pao Milk Tea L milk_tea da_hong_pao 1 0 0 0 L 700 130 0 100 high 0 1 0
7 Pu’er Milk Tea R milk_tea puer 1 0 0 0 R 500 95 0 100 medium 0 0 0
8 Pu’er Milk Tea L milk_tea puer 1 0 0 0 L 700 115 0 100 medium 0 0 0
9 Osmanthus Milk Tea R milk_tea osmanthus 1 0 0 0 R 500 95 0 100 low 0 0 0
10 Osmanthus Milk Tea L milk_tea osmanthus 1 0 0 0 L 700 115 0 100 low 0 0 0
11 Jasmine Milk Tea R milk_tea jasmine 1 0 0 0 R 500 95 0 100 medium 0 1 0
12 Jasmine Milk Tea L milk_tea jasmine 1 0 0 0 L 700 115 0 100 medium 0 1 0
13 White Peach Milk Tea R milk_tea white_peach 1 0 0 0 R 500 95 0 100 low 0 1 0
14 White Peach Milk Tea L milk_tea white_peach 1 0 0 0 L 700 115 0 100 low 0 1 0
15 Jasmine Grapefruit Tea L fruit_tea jasmine 0 1 0 0 L 700 105 0 100 low 0 1 0
16 Lemon Glutinous Green Tea L fruit_tea glutinous_green 0 1 0 0 L 700 105 0 100 medium 0 1 0
17 Peach Jasmine Tea L fruit_tea jasmine 0 1 0 0 L 700 95 0 100 low 0 1 0
18 Fresh Orange Jasmine Tea L fruit_tea jasmine 0 1 0 0 L 700 150 0 100 low 1 1 0
19 Tie Guan Yin Pure Tea R pure_tea tie_guan_yin 0 0 0 0 R 500 110 0 100 high 0 0 0
20 Glutinous Green Pure Tea R pure_tea glutinous_green 0 0 0 0 R 500 85 0 100 medium 0 0 0
21 Mango Sticky Rice Tea L fruit_tea mango 0 1 0 0 L 700 135 0 100 low 1 1 1
22 Thai Tea Frappe L blended thai_tea 0 0 0 1 L 700 125 0 100 medium 1 1 0

SELECT * เหมาะสำหรับการสำรวจข้อมูลเบื้องต้นเมื่อต้องการดูโครงสร้างทั้งหมดอย่างรวดเร็ว แต่ไม่แนะนำในงานวิเคราะห์จริงเพราะดึงคอลัมน์ที่ไม่จำเป็น ทำให้ช้าและอ่านยากขึ้น นอกจากนี้หากโครงสร้างตารางเปลี่ยน ผลลัพธ์อาจเปลี่ยนตามโดยไม่ได้ตั้งใจ และในงานจริงควรระบุชื่อคอลัมน์ที่ต้องการเสมอ

  1. SELECT สำหรับเลือกเฉพาะบางคอลัมน์
SELECT sku_id, sku_name, family, list_price_thb, size_code
FROM menus;
sku_id sku_name family list_price_thb size_code
1 Glutinous Green Milk Tea R milk_tea 85 R
2 Glutinous Green Milk Tea L milk_tea 100 L
3 Tie Guan Yin Milk Tea R milk_tea 110 R
4 Tie Guan Yin Milk Tea L milk_tea 130 L
5 Da Hong Pao Milk Tea R milk_tea 110 R
6 Da Hong Pao Milk Tea L milk_tea 130 L
7 Pu’er Milk Tea R milk_tea 95 R
8 Pu’er Milk Tea L milk_tea 115 L
9 Osmanthus Milk Tea R milk_tea 95 R
10 Osmanthus Milk Tea L milk_tea 115 L
11 Jasmine Milk Tea R milk_tea 95 R
12 Jasmine Milk Tea L milk_tea 115 L
13 White Peach Milk Tea R milk_tea 95 R
14 White Peach Milk Tea L milk_tea 115 L
15 Jasmine Grapefruit Tea L fruit_tea 105 L
16 Lemon Glutinous Green Tea L fruit_tea 105 L
17 Peach Jasmine Tea L fruit_tea 95 L
18 Fresh Orange Jasmine Tea L fruit_tea 150 L
19 Tie Guan Yin Pure Tea R pure_tea 110 R
20 Glutinous Green Pure Tea R pure_tea 85 R
21 Mango Sticky Rice Tea L fruit_tea 135 L
22 Thai Tea Frappe L blended 125 L

คำสั่งนี้ประกอบด้วยสองส่วนหลัก

1. SELECT clause (ส่วนระบุคอลัมน์) - ระบุว่าต้องการดึงข้อมูลจากคอลัมน์ใดบ้าง - ในตัวอย่างนี้ เลือก 5 คอลัมน์: sku_id, sku_name, family, list_price_thb และ size_code - แต่ละคอลัมน์คั่นด้วยเครื่องหมายจุลภาค (,) - ลำดับของคอลัมน์ที่ระบุจะเป็นลำดับที่แสดงในผลลัพธ์

2. FROM clause (ส่วนระบุตาราง) - ระบุว่าต้องการดึงข้อมูลจากตารางใด - ในตัวอย่างนี้ ดึงข้อมูลจากตาราง menus - FROM เป็นคีย์เวิร์ดบังคับที่ต้องมีเสมอเมื่อใช้ SELECT

เมื่อเข้าใจวิธีเลือกคอลัมน์ด้วย SELECT แล้ว ขั้นถัดไปคือการกำหนดว่าแถวใดควรถูกนำมาแสดงในผลลัพธ์ ซึ่งเป็นบทบาทของคำสั่ง WHERE

การใช้ WHERE กำหนดเงื่อนไข

ใช้สำหรับการกำหนดเงื่อนไขว่าแถวใดในตารางจะถูกนำมาแสดงในผลลัพธ์ โดยการระบุเงื่อนไขนั้นใช้ตัวดำเนินการ (Operator) แบบต่าง ๆ เป็นตัวกำหนด ตัวดำเนินการที่ใช้แบ่งออกได้เป็น 2 หมวดหลัก ๆ คือ ตัวดำเนินการเปรียบเทียบ (Comparison Operators) และตัวดำเนินการตรรกะ (Logical Operators)

เพื่อให้เห็นภาพการใช้งานอย่างเป็นระบบ จะเริ่มจากกลุ่มแรก คือ ตัวดำเนินการเปรียบเทียบ ซึ่งใช้เปรียบเทียบค่าในแต่ละแถวกับเกณฑ์ที่กำหนด

ตัวดำเนินการเปรียบเทียบ (Comparison Operators)

ตัวดำเนินการ ความหมาย ตัวอย่าง PiCha ผลลัพธ์
= เท่ากับ WHERE family = 'milk_tea' ได้สินค้า milk_tea ทั้งหมด
<> หรือ != ไม่เท่ากับ WHERE size_code <> 'L' ได้สินค้าที่ไม่ใช่ขนาด L ทั้งหมด
> มากกว่า WHERE list_price_thb > 100 ได้สินค้าที่ราคาเกิน 100 บาท
< น้อยกว่า WHERE list_price_thb < 100 ได้สินค้าที่ราคาต่ำกว่า 100 บาท
>= มากกว่าหรือเท่ากับ WHERE list_price_thb >= 100 ได้สินค้าที่ราคาตั้งแต่ 100 บาทขึ้นไป
<= น้อยกว่าหรือเท่ากับ WHERE list_price_thb <= 130 ได้สินค้าที่ราคาไม่เกิน 130 บาท

ตัวดำเนินการแบบตรรกะ (Logical Operators)

นิพจน์ A นิพจน์ B A AND B A OR B NOT A
จริง จริง จริง จริง เท็จ
จริง เท็จ เท็จ จริง เท็จ
เท็จ จริง เท็จ จริง จริง
เท็จ เท็จ เท็จ เท็จ จริง

ตัวดำเนินการอื่น ๆ

ตัวดำเนินการ ใช้ทำอะไร รูปแบบทั่วไป ตัวอย่างโค้ด ความหมายของตัวอย่าง
LIKE ค้นหาข้อความตามรูปแบบ (pattern) ด้วย wildcard คอลัมน์ LIKE 'pattern' WHERE sku_name LIKE '%Tea%' เลือกเมนูที่มีคำว่า Tea อยู่ในชื่อ เช่น Thai Milk Tea, Matcha Tea
NOT LIKE เลือกข้อความที่ ไม่ ตรง pattern คอลัมน์ NOT LIKE 'pattern' WHERE sku_name NOT LIKE '%Milk%' ตัดเมนูที่มีคำว่า Milk ออก เหลือเฉพาะกลุ่ม fruit tea / pure tea
IN เลือกค่าที่อยู่ในชุดที่กำหนด คอลัมน์ IN (ค่า1, ค่า2, ...) WHERE loyalty_tier IN ('Gold', 'Platinum') เลือกเฉพาะลูกค้า tier สูง Gold และ Platinum
NOT IN เลือกค่าที่ ไม่ อยู่ในชุด คอลัมน์ NOT IN (ค่า1, ค่า2, ...) WHERE channel NOT IN ('delivery', 'to_go') ตัด channel delivery และ to_go ออก เหลือเฉพาะ dine-in
BETWEEN เลือกค่าที่อยู่ในช่วง (รวมขอบบน–ล่าง) คอลัมน์ BETWEEN ค่าเริ่ม AND ค่าจบ WHERE price_thb BETWEEN 80 AND 120 เลือกเมนูราคา 80 ถึง 120 บาท รวมทั้ง 80 และ 120
NOT BETWEEN เลือกค่าที่อยู่นอกช่วง คอลัมน์ NOT BETWEEN ค่าเริ่ม AND ค่าจบ WHERE net_sales_thb NOT BETWEEN 100 AND 300 เลือกออร์เดอร์ที่มูลค่าต่ำกว่า 100 หรือสูงกว่า 300 บาท
NOT กลับเงื่อนไข (ใช้คู่กับ LIKE, IN, BETWEEN หรือเงื่อนไขอื่น) NOT เงื่อนไข WHERE NOT (branch_type = 'mall') เทียบเท่ากับ branch_type <> 'mall' คือสาขาที่ไม่ใช่ mall

คำสั่ง WHERE ใช้สำหรับ กรองข้อมูล ที่ต้องการจากตาราง โดยมีไวยากรณ์พื้นฐานดังนี้

SELECT ชื่อคอลัมน์1, ชื่อคอลัมน์2
FROM ชื่อตาราง
WHERE เงื่อนไข;

ตัวอย่างแรกจะแสดงการประยุกต์ใช้ WHERE กับโจทย์ทางธุรกิจอย่างง่าย โดยตรวจสอบว่าเมนูใดในตาราง menus มีราคาสูงกว่า 120 บาท ผ่านเงื่อนไข WHERE list_price_thb > 120

SELECT sku_id, sku_name, family, list_price_thb, size_code
FROM menus
WHERE list_price_thb > 120; -- กรองเฉพาะเมนูที่ราคาสูงกว่า 120 บาท
sku_id sku_name family list_price_thb size_code
4 Tie Guan Yin Milk Tea L milk_tea 130 L
6 Da Hong Pao Milk Tea L milk_tea 130 L
18 Fresh Orange Jasmine Tea L fruit_tea 150 L
21 Mango Sticky Rice Tea L fruit_tea 135 L
22 Thai Tea Frappe L blended 125 L

จากเมนู 22 รายการ มี 5 รายการที่ราคาสูงกว่า 120 บาท สินค้าราคาสูงสุดคือ Fresh Orange Jasmine Tea L ที่ราคา 150 บาท

เมื่อสามารถกรองแถวที่ต้องการได้แล้ว ขั้นต่อไปคือการกำหนดลำดับการแสดงผล เพื่อให้ผลลัพธ์อ่านและตีความได้ง่ายขึ้น ซึ่งเป็นหน้าที่ของ ORDER BY

การใช้ ORDER BY เรียงลำดับผลลัพธ์

หากต้องการเรียงราคาจากต่ำสุดไปสูงสุด ทำได้โดยใช้ ORDER BY

SELECT sku_id, sku_name, family, list_price_thb, size_code
FROM menus
WHERE list_price_thb > 120    
ORDER BY list_price_thb; -- เรียงจากราคาต่ำสุดไปสูงสุด
sku_id sku_name family list_price_thb size_code
22 Thai Tea Frappe L blended 125 L
4 Tie Guan Yin Milk Tea L milk_tea 130 L
6 Da Hong Pao Milk Tea L milk_tea 130 L
21 Mango Sticky Rice Tea L fruit_tea 135 L
18 Fresh Orange Jasmine Tea L fruit_tea 150 L

ซึ่งการเรียงข้อมูลจากต่ำสุดไปสูงสุดอีกวิธีหนึ่ง คือ การระบุ ASC หลังคอลัมน์ที่ต้องการเรียงลำดับ ดังนี้

SELECT sku_id, sku_name, family, list_price_thb, size_code
FROM menus
WHERE list_price_thb > 120    
ORDER BY list_price_thb ASC; -- เรียงจากราคาต่ำสุดไปสูงสุด

หากต้องการเรียงราคาจากสูงสุดไปต่ำ ทำได้โดยใช้ ORDER BY โดยระบุ DESC ท้ายคอลัมน์ที่ต้องการเรียงลำดับ

SELECT sku_id, sku_name, family, list_price_thb, size_code
FROM menus
WHERE list_price_thb > 120    
ORDER BY list_price_thb DESC; -- เรียงจากราคาสูงสุดไปต่ำสุด
sku_id sku_name family list_price_thb size_code
18 Fresh Orange Jasmine Tea L fruit_tea 150 L
21 Mango Sticky Rice Tea L fruit_tea 135 L
4 Tie Guan Yin Milk Tea L milk_tea 130 L
6 Da Hong Pao Milk Tea L milk_tea 130 L
22 Thai Tea Frappe L blended 125 L

ORDER BY สามารถเรียงตามหลายคอลัมน์ได้ คอลัมน์แรกเป็นเกณฑ์หลัก คอลัมน์ที่สองใช้เรียงเมื่อคอลัมน์แรกมีค่าเท่ากัน

-- ดึงข้อมูลเมนูทั้งหมด เรียงตามหมวดหมู่จาก A-Z และราคาจากสูงไปต่ำในแต่ละหมวด
SELECT sku_id, sku_name, family, list_price_thb
FROM menus -- ตารางข้อมูลเมนู
ORDER BY family ASC, list_price_thb DESC; -- เรียงตามหมวดหมู่จาก A-Z จากนั้นเรียงตามราคาสูงสุดในแต่ละหมวด
sku_id sku_name family list_price_thb
22 Thai Tea Frappe L blended 125
18 Fresh Orange Jasmine Tea L fruit_tea 150
21 Mango Sticky Rice Tea L fruit_tea 135
15 Jasmine Grapefruit Tea L fruit_tea 105
16 Lemon Glutinous Green Tea L fruit_tea 105
17 Peach Jasmine Tea L fruit_tea 95
4 Tie Guan Yin Milk Tea L milk_tea 130
6 Da Hong Pao Milk Tea L milk_tea 130
8 Pu’er Milk Tea L milk_tea 115
10 Osmanthus Milk Tea L milk_tea 115
12 Jasmine Milk Tea L milk_tea 115
14 White Peach Milk Tea L milk_tea 115
3 Tie Guan Yin Milk Tea R milk_tea 110
5 Da Hong Pao Milk Tea R milk_tea 110
2 Glutinous Green Milk Tea L milk_tea 100
7 Pu’er Milk Tea R milk_tea 95
9 Osmanthus Milk Tea R milk_tea 95
11 Jasmine Milk Tea R milk_tea 95
13 White Peach Milk Tea R milk_tea 95
1 Glutinous Green Milk Tea R milk_tea 85
19 Tie Guan Yin Pure Tea R pure_tea 110
20 Glutinous Green Pure Tea R pure_tea 85

สังเกตได้ว่าจะเป็นการเรียงตามประเภท (family) จากนั้นจะเรียงตามราคาในแต่ละ family จากสูงไปต่ำ

ผลที่ได้ คำอธิบาย
blended มาก่อน เรียง A→Z
fruit_tea ตามมา เรียง A→Z
milk_tea, pure_tea เรียง A→Z
แต่ละ family ราคาสูงไปต่ำ (เช่น milk_tea: 130→85)

เมื่อเข้าใจการเรียงลำดับผลลัพธ์แล้ว ส่วนถัดไปจะพิจารณาวิธีจำกัดจำนวนแถวที่แสดง เพื่อให้การตรวจสอบผลลัพธ์ทำได้สะดวกและรวดเร็วยิ่งขึ้น

การใช้ DISTINCT แสดงค่าที่ไม่ซ้ำ

DISTINCT ใช้เพื่อดึงเฉพาะค่าที่ไม่ซ้ำกันในคอลัมน์ที่ระบุ โดยมีรูปแบบพื้นฐานดังนี้

SELECT DISTINCT ชื่อคอลัมน์
FROM ชื่อตาราง;

หากฝ่ายสมาชิกต้องการทราบว่า PiCha Tea House มีระดับสมาชิกอะไรบ้าง เพื่อออกแบบแคมเปญ onboard สำหรับแต่ละกลุ่ม คำถามนี้แสดงวิธีใช้ DISTINCT เพื่อต้องการรู้ว่า “มีค่าอะไรบ้าง” ในคอลัมน์นั้น (ในกรณีนี้คือคอลัมน์ loyalty_tier) โดยไม่สนใจว่าซ้ำกี่ครั้ง

SELECT DISTINCT loyalty_tier
FROM customers
ORDER BY loyalty_tier;
loyalty_tier
gold
member
none
silver

การใช้ BETWEEN กรองข้อมูลเป็นช่วง

list_price_thb BETWEEN 100 AND 130 ให้ผลลัพธ์เหมือน list_price_thb >= 100 AND list_price_thb <= 130 โดยค่า 100 และ 130 จะถูกนับรวมด้วย (inclusive ทั้งสองด้าน) BETWEEN ใช้ได้กับตัวเลข วันที่ และข้อความ

-- กรองตัวเลข
SELECT sku_id, sku_name, list_price_thb, size_code
FROM menus
WHERE list_price_thb BETWEEN 100 AND 130;
-- กรองชั่วโมง: ดึงออเดอร์ช่วงเช้า
SELECT * FROM transactions
WHERE hour BETWEEN 9 AND 11;
-- กรองวันที่: ดึงออเดอร์จากวันที่ 1-7 จากเดือนกุมภาพันธ์
SELECT * FROM transactions
WHERE order_date BETWEEN '2026-02-01' AND '2026-02-07'
-- กรองวันที่และเวลา: ดึงออเดอร์เดือนกุมภาพันธ์
SELECT * FROM transactions
WHERE order_datetime BETWEEN '2026-02-01' AND '2026-02-07';
Warning

สำหรับวันที่ ต้องระวังว่า '2026-02-07' จะรวมเฉพาะเที่ยงคืนของวันที่ 7 ถ้าข้อมูลเก็บเป็น timestamp (มีเวลาต่อท้าย) ข้อมูลระหว่างวันที่ 7 จะหลุดออกไป วิธีที่ปลอดภัยกว่าคือใช้ < '2026-02-08' แทน ซึ่งจับทุกช่วงเวลาของวันที่ 7 ได้ครบ

ตัวอย่างต่อไปนี้แสดงการใช้ BETWEEN เพื่อหาเมนูชานมที่มีราคาตั้งแต่ 100–130 บาท

-- ดึงเมนูชานมในช่วงราคา 100-130 บาท เรียงตามราคาและขนาด
SELECT sku_id, sku_name, list_price_thb, size_code
FROM menus -- ตารางข้อมูลเมนู
WHERE family = 'milk_tea' -- กรองเฉพาะหมวดหมู่ชานม
 AND list_price_thb BETWEEN 100 AND 130 -- กรองเฉพาะราคาตั้งแต่ 100-130 บาท
ORDER BY list_price_thb, size_code; -- เรียงตามราคา จากนั้นเรียงตามขนาด
sku_id sku_name list_price_thb size_code
2 Glutinous Green Milk Tea L 100 L
3 Tie Guan Yin Milk Tea R 110 R
5 Da Hong Pao Milk Tea R 110 R
8 Pu’er Milk Tea L 115 L
10 Osmanthus Milk Tea L 115 L
12 Jasmine Milk Tea L 115 L
14 White Peach Milk Tea L 115 L
4 Tie Guan Yin Milk Tea L 130 L
6 Da Hong Pao Milk Tea L 130 L

จากผลลัพธ์ จะเห็นได้ว่า milk_tea ในราคาตั้งแต่ 100–130 บาทมี 9 รายการ โดยราคาที่พบบ่อยที่สุดคือ 115 บาท (4 รายการ) ขนาด L มีจำนวนมากเป็นพิเศษ (7 จาก 9 รายการ) สะท้อนว่า PiCha วางตำแหน่ง milk_tea ไว้ที่ขนาดใหญ่เป็นหลัก ส่วนขนาด R มีเพียง 2 รายการที่ราคา 110 บาท ซึ่งอาจทำหน้าที่เป็นจุดเข้าถึง (entry point) สำหรับลูกค้าใหม่ที่ยังไม่คุ้นเคยกับแบรนด์

หลังจากพิจารณาการกรองค่าช่วงของตัวเลขและวันที่แล้ว ส่วนถัดไปจะขยับไปสู่ตัวอย่างที่ใช้หลายเงื่อนไขร่วมกัน

ตัวอย่างถัดมาจะเป็นการหาลูกค้าระดับ gold ที่รู้จัก PiCha Tea House ผ่าน TikTok เพื่อให้ทีมการตลาดออกแบบแคมเปญพิเศษสำหรับลูกค้าระดับ gold ที่มาจาก TikTok ซึ่งทีมมองว่าเป็นกลุ่มที่เติบโตเร็วที่สุด คำถามคือกลุ่มนี้มีโปรไฟล์อย่างไร?

-- ดึงข้อมูลลูกค้าระดับ Gold ที่ค้นพบแบรนด์ผ่าน TikTok
SELECT customer_id, age_group, gender, income_segment, loyalty_tier
FROM customers -- ตารางข้อมูลลูกค้า
WHERE loyalty_tier = 'gold' -- เงื่อนไขที่ 1: ลูกค้าระดับ Gold
 AND discovery_channel = 'tiktok' -- เงื่อนไขที่ 2: ค้นพบแบรนด์ผ่าน TikTok (ต้องผ่านทั้งสองเงื่อนไข)
ORDER BY customer_id -- เรียงตามรหัสลูกค้า
LIMIT 10; -- แสดงเพียง 10 แถวแรก
customer_id age_group gender income_segment loyalty_tier
1009 Gen_Z_18_24 female high gold
1042 Gen_Z_18_24 male high gold
1057 Gen_Z_18_24 female high gold
1061 Gen_Z_25_29 female low gold
1077 Gen_Z_25_29 male high gold
1083 Gen_Z_25_29 male high gold
1089 Gen_Z_18_24 male low gold
1166 Millennial_30_35 female high gold
1210 Millennial_30_35 male mid gold
1239 Gen_Z_18_24 male mid gold

จะเห็นได้ว่าใน 10 รายการแรก ลูกค้า gold จาก TikTok เป็น Gen_Z_18_24 มากที่สุด (5 คน) รองลงมาเป็น Gen_Z_25_29 (3 คน) และ Millennial_30_35 (2 คน) กลุ่มรายได้สูงมี 6 คน รายได้ต่ำ 2 คน รายได้ปานกลาง 2 คน

ลูกค้า gold จาก TikTok เป็นกลุ่ม Gen Z อย่างชัดเจน (8 จาก 10 คน = 80%) และส่วนใหญ่มีรายได้สูง แสดงว่า TikTok สามารถดึงลูกค้า Gen Z ที่มีกำลังซื้อเข้าสู่โปรแกรมสมาชิกระดับสูงได้

ผลลัพธ์นี้แสดงให้เห็นว่าหากเพิ่มงบโฆษณา TikTok สำหรับกลุ่ม Gen Z ที่มีรายได้สูงอาจคุ้มค่า แต่ผู้บริหารควรตรวจสอบเพิ่มเติมว่าลูกค้า gold ที่มีรายได้ต่ำ (เช่น customer 1061 และ 1089) ได้รับโปรโมชั่นมากเกินไป ในบทที่ 3 ผู้เรียนจะใช้ COUNT และ GROUP BY เพื่อเปรียบเทียบจำนวนลูกค้า gold ระหว่าง TikTok กับช่องทางอื่น

ตัวอย่างนี้ใช้เงื่อนไขสองข้อพร้อมกัน จึงเป็นโอกาสเหมาะในการอธิบายความแตกต่างระหว่าง AND และ OR รวมถึงลำดับการประเมินเงื่อนไขใน SQL

การใช้ AND, OR ตรวจสอบเงื่อนไข

ตัวอย่างนี้ใช้ AND เพราะต้องการลูกค้าที่ตรงทั้งสองเงื่อนไข ทั้ง gold และมาจาก TikTok

WHERE loyalty_tier = 'gold' AND discovery_channel = 'tiktok'

ถ้าเปลี่ยนเป็น OR ผลลัพธ์จะกว้างขึ้นมาก เพราะลูกค้าที่เป็น gold (ไม่ว่ามาจากช่องทางใด) หรือมาจาก TikTok (ไม่ว่าระดับใด) จะผ่านเกณฑ์ทั้งหมด

-- ดึงข้อมูลลูกค้าระดับ Gold หรือค้นพบแบรนด์ผ่าน TikTok
SELECT customer_id, age_group, gender, income_segment, loyalty_tier, discovery_channel
FROM customers -- ตารางข้อมูลลูกค้า
WHERE loyalty_tier = 'gold' -- เงื่อนไขที่ 1: ลูกค้าระดับ Gold
 OR discovery_channel = 'tiktok' -- เงื่อนไขที่ 2: ค้นพบแบรนด์ผ่าน TikTok (ผ่านเงื่อนไขใดเงื่อนไขหนึ่ง)
ORDER BY customer_id -- เรียงตามรหัสลูกค้า
LIMIT 10; -- แสดงเพียง 10 แถวแรก
customer_id age_group gender income_segment loyalty_tier discovery_channel
1002 Gen_Z_25_29 male high member tiktok
1003 Gen_Z_25_29 female low none tiktok
1008 Millennial_30_35 male high gold word_of_mouth
1009 Gen_Z_18_24 female high gold tiktok
1011 Gen_Z_25_29 male low member tiktok
1014 Millennial_30_35 male high member tiktok
1019 Gen_Z_18_24 male low none tiktok
1021 Gen_Z_25_29 female mid member tiktok
1024 Older_36plus male high silver tiktok
1029 Millennial_30_35 female high silver tiktok

ลำดับการประเมิน: SQL ประเมิน AND ก่อน OR เสมอ ถ้าต้องการเปลี่ยนลำดับ ให้ใช้วงเล็บกำกับลำดับการประเมินนิพจน์

-- SQL ประเมินเป็น: gold OR (tiktok AND high)
WHERE loyalty_tier = 'gold' OR discovery_channel = 'tiktok' AND income_segment = 'high'

-- วงเล็บเปลี่ยนลำดับเป็น: (gold OR tiktok) AND high
WHERE (loyalty_tier = 'gold' OR discovery_channel = 'tiktok') AND income_segment = 'high'

วงเล็บทำให้ความตั้งใจชัดเจน เหมือนบอกว่า “ตรวจ (gold หรือ tiktok) ก่อน แล้วค่อยตรวจ high” ไม่อย่างนั้นจะตรวจ tiktok กับ high ก่อน แล้วค่อย OR กับ gold ทำให้ได้ผลลัพธ์ที่ไม่ตรงเจตนา แนวปฏิบัติที่ดีคือใส่วงเล็บเสมอเมื่อใช้ AND กับ OR ร่วมกัน แม้ลำดับจะถูกต้องอยู่แล้ว เพราะช่วยให้คนอ่านเข้าใจได้ทันที

การใช้ IN ตรวจสอบเงื่อนไข

IN ทำงานเหมือน OR หลายตัวที่ตรวจสอบคอลัมน์เดียวกัน อ่านง่ายกว่าและบำรุงรักษาง่ายกว่า โดยเฉพาะเมื่อมีค่าที่ต้องเปรียบเทียบมากกว่า 2 ค่า

มาดูตัวอย่างการใช้งาน IN ในการดูสินค้าหมวด fruit_tea, blended และ pure_tea ทั้งหมดใน 3 หมวดนี้ ทำได้โดย

แบบที่ 1 ใช้ OR

SELECT sku_id, sku_name, family, list_price_thb
FROM menus
-- เลือกเฉพาะแถวที่ family เป็น fruit_tea, blended หรือ pure_tea
WHERE family = 'fruit_tea' OR family = 'blended' OR family = 'pure_tea'; 

แบบที่ 2 ใช้ IN เขียนสั้น กระชับ และอ่านง่ายกว่า

SELECT sku_id, sku_name, family, list_price_thb      
FROM menus
-- เลือกเฉพาะแถวที่ family เป็น fruit_tea, blended หรือ pure_tea                        
WHERE family IN ('fruit_tea', 'blended', 'pure_tea');

ผลลัพธ์ที่ได้จากทั้ง 2 แบบที่เขียนจะเหมือนกันดังนี้

sku_id sku_name family list_price_thb
15 Jasmine Grapefruit Tea L fruit_tea 105
16 Lemon Glutinous Green Tea L fruit_tea 105
17 Peach Jasmine Tea L fruit_tea 95
18 Fresh Orange Jasmine Tea L fruit_tea 150
19 Tie Guan Yin Pure Tea R pure_tea 110
20 Glutinous Green Pure Tea R pure_tea 85
21 Mango Sticky Rice Tea L fruit_tea 135
22 Thai Tea Frappe L blended 125

การใช้ NOT

NOT เป็นตัวดำเนินการเชิงตรรกะ (logical operator) ที่ใช้ในการ “ปฏิเสธเงื่อนไข” ของนิพจน์เดิม กล่าวคือ ถ้าเงื่อนไขเดิมเป็นจริง (TRUE) เมื่อเติม NOT จะกลายเป็นเท็จ (FALSE) และหากเงื่อนไขเดิมเป็นเท็จ เมื่อเติม NOT จะกลายเป็นจริง

หากต้องการเลือกเฉพาะแถวที่ family ไม่ใช่ milk_tea และไม่ใช่ pure_tea

SELECT sku_id, sku_name, family, list_price_thb
FROM menus
WHERE family NOT IN ('milk_tea', 'pure_tea'); -- เลือกเฉพาะแถวที่ family ไม่ใช่ milk_tea และไม่ใช่ pure_tea
sku_id sku_name family list_price_thb
15 Jasmine Grapefruit Tea L fruit_tea 105
16 Lemon Glutinous Green Tea L fruit_tea 105
17 Peach Jasmine Tea L fruit_tea 95
18 Fresh Orange Jasmine Tea L fruit_tea 150
21 Mango Sticky Rice Tea L fruit_tea 135
22 Thai Tea Frappe L blended 125

ในแง่ตรรกะ เงื่อนไข NOT IN สามารถเขียนใหม่ให้อยู่ในรูปของหลายเงื่อนไขด้วยตัวดำเนินการเปรียบเทียบและตัวดำเนินการ AND ได้ เช่น

SELECT sku_id, sku_name, family, list_price_thb
FROM menus
WHERE family <> 'milk_tea' AND family <> 'pure_tea';

ตัวดำเนินการเงื่อนไขที่ผ่านมาทั้ง =, BETWEEN, AND, OR, IN, NOT ทำงานกับค่าที่ระบุชัดเจน อย่างไรก็ตาม บางครั้งผู้เรียนต้องค้นหาจาก “บางส่วนของข้อความ” เช่น เมนูที่มีคำว่า Milk อยู่ในชื่อ ซึ่งเป็นกรณีที่ LIKE และ ILIKE มีบทบาทสำคัญ

การใช้ LIKE และ ILIKE ทำ pattern matching

LIKE และ ILIKE เป็นตัวดำเนินการ pattern matching สำหรับข้อความ โดยใช้สัญลักษณ์พิเศษ 2 ตัว

  • % ใช้แทนตัวอักษรใด ๆ ก็ได้ รวมถึงไม่มีตัวใดเลย (zero-length character)
  • _ แทนตัวอักษรตัวเดียว
WHERE sku_name LIKE '%Milk%'    -- มีคำว่า Milk ที่ไหนก็ได้ (case-sensitive)
WHERE sku_name ILIKE '%milk%'   -- มีคำว่า milk ที่ไหนก็ได้ (ไม่สนว่าเป็นตัวพิมพ์ใหญ่หรือพิมพ์เล็ก)
WHERE sku_name LIKE 'Jasmine%'  -- ขึ้นต้นด้วย Jasmine
WHERE sku_name LIKE '%Tea L'    -- ลงท้ายด้วย Tea L
WHERE sku_name LIKE '___ Tea'   -- มี 3 ตัวอักษร แล้วตามด้วย Tea

DuckDB รองรับ ILIKE ซึ่งไม่สนตัวพิมพ์ใหญ่เล็ก (case-insensitive) ทำให้ ILIKE '%milk%' จับได้ทั้ง “Milk Tea” และ “milk tea” ขณะที่ LIKE '%milk%' จับเฉพาะตัวพิมพ์เล็กเท่านั้น ในทางปฏิบัติ ILIKE เป็นตัวเลือกที่ปลอดภัยกว่าเมื่อไม่แน่ใจว่าข้อมูลเก็บเป็นตัวพิมพ์ใหญ่หรือพิมพ์เล็ก

ตัวอย่างเช่น หากต้องการหาเมนูที่มีคำว่า milk อยู่ในชื่อ

SELECT sku_id, sku_name, family, list_price_thb
FROM menus
WHERE sku_name ILIKE '%milk%' -- กรองเฉพาะเมนูที่ชื่อมีคำว่า "milk" (ไม่แยกตัวพิมพ์ใหญ่-เล็ก)
ORDER BY list_price_thb DESC; -- เรียงจากราคาแพงสุดไปถูกสุด
sku_id sku_name family list_price_thb
4 Tie Guan Yin Milk Tea L milk_tea 130
6 Da Hong Pao Milk Tea L milk_tea 130
8 Pu’er Milk Tea L milk_tea 115
10 Osmanthus Milk Tea L milk_tea 115
12 Jasmine Milk Tea L milk_tea 115
14 White Peach Milk Tea L milk_tea 115
2 Glutinous Green Milk Tea L milk_tea 100
3 Tie Guan Yin Milk Tea R milk_tea 110
5 Da Hong Pao Milk Tea R milk_tea 110

หากต้องการหาเมนูที่ไม่มีคำว่า ‘milk’ ทำได้โดยใส่ NOT หน้า ILIKE

-- ดึงข้อมูลเมนูที่ไม่มีคำว่า milk ในชื่อ เรียงตามราคาจากสูงไปต่ำ
SELECT sku_id, sku_name, family, list_price_thb
FROM menus -- ตารางข้อมูลเมนู
WHERE sku_name NOT ILIKE '%milk%' -- กรองเฉพาะเมนูที่ชื่อไม่มีคำว่า milk (ไม่แยกตัวพิมพ์เล็ก-ใหญ่)
ORDER BY list_price_thb DESC; -- เรียงจากราคาสูงสุดไปต่ำสุด
sku_name family list_price_thb
Fresh Orange Jasmine Tea L fruit_tea 150
Mango Sticky Rice Tea L fruit_tea 135
Thai Tea Frappe L blended 125
Tie Guan Yin Pure Tea R pure_tea 110
Jasmine Grapefruit Tea L fruit_tea 105
Lemon Glutinous Green Tea L fruit_tea 105
Peach Jasmine Tea L fruit_tea 95
Glutinous Green Pure Tea R pure_tea 85

การใช้ LIMIT จำกัดจำนวนแถวของผลลัพธ์

ใช้ LIMIT เพื่อจำกัดจำนวนแถวของผลลัพธ์ เช่น ระบุ LIMIT 10 ไว้ท้ายสุดของชุดคำ เพื่อให้แสดงเพียง 10 แถวแรกของผลลัพธ์ มีประโยชน์เมื่อตารางมีแถวข้อมูลจำนวนมาก การดูเพียงบางส่วนของแถวข้อมูลช่วยให้ตรวจสอบ ก่อนรันคำสั่งเต็มรูปแบบ

ตัวอย่างต่อไปนี้จะแสดงการใช้ LIMIT ร่วมกับ WHERE และ ORDER BY เพื่อสำรวจออเดอร์แบบ delivery ที่มีมูลค่าสูงในช่วงวันหยุดสุดสัปดาห์ ซึ่งสามารถนำไปใช้ประกอบการออกแบบโปรโมชั่นเฉพาะกลุ่มได้

-- ดึงคำสั่งซื้อ Delivery ช่วงวันหยุดสุดสัปดาห์ที่มียอดขายรวมตั้งแต่ 300 บาทขึ้นไป
SELECT transaction_id, order_date, day_of_week, store_id,
       basket_qty, gross_sales_thb, channel
FROM transactions -- ตารางหลักที่เก็บข้อมูลธุรกรรม
WHERE channel = 'delivery' -- กรองเฉพาะคำสั่งซื้อช่องทาง Delivery
 AND gross_sales_thb >= 300 -- กรองเฉพาะรายการที่มียอดขายรวมตั้งแต่ 300 บาทขึ้นไป
 AND weekend_flag = 1 -- กรองเฉพาะวันหยุดสุดสัปดาห์
ORDER BY gross_sales_thb DESC -- เรียงจากยอดขายสูงสุดไปต่ำสุด
LIMIT 10; -- แสดงเพียง 10 แถวแรก
transaction_id order_date day_of_week store_id basket_qty gross_sales_thb channel
41627 2026-03-21 Saturday 6 4 665 delivery
48503 2026-03-28 Saturday 2 4 614 delivery
35100 2026-03-14 Saturday 4 4 609 delivery
33407 2026-03-15 Sunday 1 4 582 delivery
42831 2026-03-21 Saturday 5 3 519 delivery
52244 2026-03-28 Saturday 6 3 500 delivery
14951 2026-02-21 Saturday 1 4 490 delivery
15413 2026-02-22 Sunday 2 4 486 delivery
19667 2026-02-28 Saturday 1 4 469 delivery
37505 2026-03-15 Sunday 6 3 457 delivery

ออเดอร์แบบ delivery มูลค่าสูงสุดในวันหยุดสุดสัปดาห์อยู่ที่ 665 บาท (store_id = 6 คือ สาขา ICONSIAM วันเสาร์ที่ 21 มีนาคม) basket_qty ของทุกออเดอร์อยู่ระหว่าง 3–4 รายการ ซึ่งอาจบ่งชี้ว่าเป็นการสั่งเป็นกลุ่มหรือสั่งให้ครอบครัว

ข้อมูลนี้แสดงถึงโอกาสในการสร้างแพ็คเกจกลุ่ม (family bundle) เช่น “สั่ง 4 แก้ว ลด 10%” สำหรับ delivery ในวันหยุดสุดสัปดาห์ โดยเฉพาะสาขา ICONSIAM (store_id = 6) และ centralwOrld (store_id = 1) ที่ปรากฏใน 10 อันดับแรกบ่อยที่สุด อย่างไรก็ตาม ควรตรวจสอบด้วยว่า basket_qty = 4 เป็นเพดานสูงสุดของระบบหรือเป็นพฤติกรรมจริงของลูกค้า

Noteลำดับการประมวลผลของ SQL

SQL ไม่ได้ประมวลผลตามลำดับที่เขียน แม้จะเขียน SELECT ก่อน แต่ฐานข้อมูลจะอ่าน FROM ก่อนเพื่อระบุตาราง จากนั้นกรองด้วย WHERE แล้วจึงเลือกคอลัมน์ตาม SELECT และเรียงด้วย ORDER BY เป็นขั้นสุดท้าย การเข้าใจลำดับนี้ช่วยให้เขียน Query ได้ถูกต้องและแก้ข้อผิดพลาดได้เร็วขึ้น

FROM → WHERE → SELECT → ORDER BY → LIMIT

จนถึงจุดนี้ ผู้เรียนได้ใช้ WHERE เพื่อคัดเลือกแถวที่ตรงเงื่อนไขแล้ว อย่างไรก็ตาม ในบางคำถาม สิ่งที่ต้องการทราบไม่ใช่ “แถวใดผ่านเงื่อนไข” แต่เป็น “มีค่าประเภทใดปรากฏอยู่บ้างในคอลัมน์หนึ่ง” ซึ่งเป็นหน้าที่ของ DISTINCT

ตัวดำเนินการทั้งหมดที่ผ่านมา ไม่ว่าจะเป็น =, BETWEEN, IN หรือ LIKE ล้วนทำงานกับค่าที่ปรากฏอยู่จริงในข้อมูล แต่ในทางปฏิบัติ ข้อมูลบางช่องอาจไม่มีการบันทึกค่าไว้เลย ซึ่ง SQL เรียกสถานะนี้ว่า NULL ## ค่าว่าง (NULL)

ค่า NULL ใน SQL คือค่าพิเศษที่ระบุว่า “ไม่มีข้อมูล” หรือ “ไม่ทราบค่า” ซึ่งแตกต่างจาก 0 และสตริงว่าง (’’) โดยสิ้นเชิง ต้องใช้ IS NULL หรือ IS NOT NULL ในการตรวจสอบ ไม่สามารถใช้ = NULL ได้

ในฐานข้อมูล PiCha มีค่า NULL ในคอลัมน์ customer_id ในตาราง transactions หมายความว่าประมาณ 74% ของออเดอร์ไม่มี customer_id หรือก็คือ ลูกค้าไม่ได้ลงทะเบียนหรือไม่ได้สแกนสมาชิก

มาดูตัวอย่างจริงจากข้อมูล PiCha กัน ในตาราง transactions หากลูกค้าที่มาใช้บริการไม่ได้เป็นสมาชิก ค่าของ customer_id จะมีค่าเป็น NULL

-- ดึงข้อมูลคำสั่งซื้อของสาขา 1 วันวาเลนไทน์ 2026 เฉพาะลูกค้าที่ไม่ระบุตัวตน
SELECT transaction_id, customer_id, order_datetime, net_sales_thb, channel
FROM transactions -- ตารางหลักที่เก็บข้อมูลธุรกรรม
WHERE store_id = 1 -- กรองเฉพาะสาขา 1
 AND order_date = '2026-02-14' -- กรองเฉพาะวันที่ 14 กุมภาพันธ์ 2026
 AND customer_id IS NULL -- กรองเฉพาะรายการที่ไม่มีข้อมูลลูกค้า (Anonymous)
LIMIT 5; -- แสดงเพียง 5 แถวแรก
transaction_id customer_id order_datetime net_sales_thb channel
9146 NULL 2026-02-14 09:00:35 260.0 dine_in
9149 NULL 2026-02-14 09:03:04 130.0 dine_in
9150 NULL 2026-02-14 09:03:16 225.7 delivery
9155 NULL 2026-02-14 09:09:16 95.0 dine_in
9158 NULL 2026-02-14 09:11:08 85.0 to_go

หากต้องการดูรายการของลูกค้าที่เป็นสมาชิกกับทางร้านทำได้โดยกำหนดเงื่อนไข customer_id IS NOT NULL

-- ดึงข้อมูลคำสั่งซื้อของสาขา 1 วันวาเลนไทน์ 2026 เฉพาะลูกค้าที่ระบุตัวตน
SELECT transaction_id, customer_id, order_datetime, net_sales_thb, channel
FROM transactions -- ตารางหลักที่เก็บข้อมูลธุรกรรม
WHERE store_id = 1 -- กรองเฉพาะสาขา 1
 AND order_date = '2026-02-14' -- กรองเฉพาะวันที่ 14 กุมภาพันธ์ 2026
 AND customer_id IS NOT NULL -- กรองเฉพาะรายการที่มีข้อมูลลูกค้า
LIMIT 5; -- แสดงเพียง 5 แถวแรก
transaction_id customer_id order_datetime net_sales_thb channel
10344 3108 2026-02-14 20:38:58 150.0 delivery

ใช้ AS

ในภาษา SQL ใช้ AS สำหรับกำหนด “ชื่อแทน” (alias) หรือจะมองเป็นชื่อเล่นก็ได้ ให้กับคอลัมน์และตารางในผลลัพธ์ของคำสั่ง SELECT ชื่อแทนนี้มีผลเฉพาะระหว่างการประมวลผลคำสั่งเท่านั้น และไม่เปลี่ยนแปลงโครงสร้างจริงของฐานข้อมูล เช่น ชื่อตารางหรือชื่อคอลัมน์ในระบบฐานข้อมูล

รูปแบบการใช้ AS

SELECT
    column_name AS alias_column_name
FROM
    table_name AS alias_table_name;
  • column_name คือชื่อคอลัมน์จริงในตาราง
  • alias_column_name คือชื่อแทนที่ต้องการให้แสดงในผลลัพธ์
  • table_name คือชื่อตารางจริงในฐานข้อมูล
  • alias_table_name คือชื่อใช้แทนตารางจริง

ตัวอย่างเช่น

SELECT
 s.store_name                    AS ชื่อสาขา,    -- ชื่อสาขา
 s.branch_type                   AS ประเภทสาขา,     -- ประเภทสาขา
 COUNT(*)                        AS order_count,   -- จำนวนคำสั่งซื้อ
 ROUND(SUM(t.net_sales_thb), 0)  AS total_revenue  -- รายได้รวม (บาท)
FROM 
 transactions AS t  -- ใช้ t เป็น alias แทนตาราง transactions
INNER JOIN
 stores       AS s  -- ใช้ s เป็น alias แทนตาราง stores
ON
 t.store_id = s.store_id -- ใช้ alias t และ s แทนที่จะเขียน transactions.store_id = stores.store_id 
GROUP BY
 s.store_name, s.branch_type -- ใช้ s เป็น alias แทนตาราง stores
ORDER BY total_revenue DESC; -- เรียงตามรายได้รวมจากมากไปน้อย
ชื่อสาขา ประเภทสาขา order_count total_revenue
PiCha centralwOrld mall 11,935 2,451,113
PiCha Siam Square street 10,738 2,311,860
PiCha ICONSIAM mall_flagship 8,702 1,763,926
PiCha Vanit Village mixed_use 7,257 1,465,764
PiCha Park Silom office 7,611 1,450,877
PiCha Exchange Tower office 7,080 1,343,861
Noteเทคนิคการใช้ AS

ในทางปฏิบัติ เราสามารถละคำว่า AS ได้ โดยเขียนในรูป column_name alias_column_name หรือ table_name alias_table_name แทน ทั้งนี้เพื่อความชัดเจนและอ่านง่าย มักแนะนำให้เขียน AS กำกับไว้เสมอ

สรุป

บทนี้ได้แนะนำ clause และ keyword พื้นฐานที่ใช้บ่อยในการดึง คัดกรอง และจัดเรียงข้อมูลจากตาราง PiCha ซึ่งเป็นจุดเริ่มต้นสำคัญของการเขียน SQL เพื่อสำรวจข้อมูลและเตรียมข้อมูลสำหรับการวิเคราะห์ในขั้นถัดไป แต่ละคำสั่งมีบทบาทต่างกัน ตั้งแต่การเลือกคอลัมน์ การระบุแหล่งข้อมูล การตั้งเงื่อนไขกรอง ไปจนถึงการจัดรูปแบบผลลัพธ์ให้อ่านง่ายขึ้น

ตารางต่อไปนี้สรุปหน้าที่ของ clause และ keyword สำคัญที่ใช้ในบทนี้

Clause / Keyword หน้าที่
SELECT เลือกคอลัมน์ที่ต้องการแสดงผล
FROM ระบุตารางที่เป็นแหล่งข้อมูล
WHERE กรองแถวตามเงื่อนไขที่กำหนด
ORDER BY เรียงลำดับผลลัพธ์ เช่น จากน้อยไปมากหรือมากไปน้อย
DISTINCT ตัดค่าที่ซ้ำกันออกจากผลลัพธ์
BETWEEN กรองข้อมูลที่อยู่ในช่วงค่าที่กำหนด โดยรวมค่าขอบเขตทั้งสองด้าน
AND / OR ใช้เชื่อมหลายเงื่อนไขเข้าด้วยกัน
IN ตรวจสอบว่าค่าอยู่ในชุดที่กำหนดหรือไม่
NOT ใช้ปฏิเสธเงื่อนไขเดิม
LIKE / ILIKE ค้นหาข้อความตามรูปแบบที่ต้องการ โดย ILIKE ไม่แยกตัวพิมพ์เล็กและใหญ่
LIMIT จำกัดจำนวนแถวที่แสดง
IS NULL / IS NOT NULL ใช้ตรวจสอบว่าค่าเป็นค่าว่างหรือไม่
AS กำหนดชื่อแทนให้คอลัมน์หรือตาราง

เมื่อเข้าใจคำสั่งพื้นฐานเหล่านี้แล้ว เราจะสามารถเขียน query เพื่อค้นหา ตรวจสอบ และคัดเลือกข้อมูลได้อย่างมั่นใจมากขึ้น ในบทถัดไป เราจะต่อยอดจากการดึงข้อมูลรายแถวไปสู่การสรุปข้อมูลในระดับกลุ่มด้วย aggregate functions

คำถามท้ายบท

  1. เขียนคำสั่ง SQL เพื่อดูสาขาทั้งหมดที่มี daily_capacity มากกว่า 300 พร้อมชื่อและประเภท

ผลลัพธ์ที่คาดหวัง

store_name branch_type daily_capacity
PiCha centralwOrld mall 400
PiCha Siam Square street 450
PiCha ICONSIAM mall_flagship 600
คลิกเพื่อดูเฉลย
SELECT store_name, branch_type, daily_capacity 
FROM stores 
WHERE daily_capacity > 300;
  1. เขียนคำสั่ง SQL เพื่อนับจำนวนลูกค้าในแต่ละ discovery_channel

ผลลัพธ์ที่คาดหวัง

discovery_channel cnt
tiktok 695
instagram 614
word_of_mouth 546
delivery_app 351
walk_in 294
คลิกเพื่อดูเฉลย
SELECT discovery_channel, COUNT(DISTINCT customer_id) AS cnt 
FROM customers 
GROUP BY discovery_channel
ORDER BY cnt DESC;
  1. เขียนคำสั่ง SQL เพื่อดูออเดอร์ทั้งหมดของสาขา store_id = 1 ที่มี basket_qty >= 3 เรียงตาม gross_sales_thb จากสูงไปต่ำ

ผลลัพธ์ที่คาดหวัง

transaction_id store_id basket_qty gross_sales_thb
13622 1 4 608
10669 1 4 600
43834 1 4 596
34110 1 4 594
คลิกเพื่อดูเฉลย
SELECT transaction_id, store_id, basket_qty, gross_sales_thb 
FROM transactions 
WHERE store_id = 1 AND basket_qty >= 3 
ORDER BY gross_sales_thb DESC;