วัตถุประสงค์การเรียนรู้
เมื่อจบบทนี้ ผู้เรียนจะสามารถ
- เขียนคำสั่ง
SELECTเพื่อเลือกคอลัมน์ที่ต้องการจากตาราง - กรองแถวข้อมูลด้วย
WHEREพร้อมตัวดำเนินการเปรียบเทียบ (=,>,<,>=,<=,<>) - รวมเงื่อนไขหลายเงื่อนไขด้วย
AND,ORและNOT - ใช้
LIKEและILIKEเพื่อค้นหารูปแบบข้อความ - เรียงลำดับผลลัพธ์ด้วย
ORDER BYทั้งแบบASCและDESC - ใช้
INเป็นทางลัดแทนORหลายเงื่อนไข - ใช้
BETWEENเพื่อกรองข้อมูลในช่วงที่กำหนด - ใช้
DISTINCTเพื่อหาค่าที่ไม่ซ้ำกันในคอลัมน์ - จัดการค่า
NULLอย่างถูกต้องด้วยIS NULLและIS NOT NULL - ใช้
ASสำหรับกำหนด “ชื่อแทน” (alias)
บทนำ
บทนำ
ในบทที่ 1 ผู้เรียนได้ทำความรู้จักโครงสร้างฐานข้อมูลของ PiCha และตารางข้อมูลหลักทั้ง 6 ตารางไปแล้ว บทนี้จะพาผู้เรียนก้าวจากการทำความเข้าใจโครงสร้างข้อมูล ไปสู่การลงมือเขียนคำสั่ง SQL เพื่อตอบคำถามทางธุรกิจที่เกิดขึ้นจริง
ตัวอย่างเช่น ผู้บริหารอาจต้องการทราบว่า “ลูกค้าระดับ Gold ที่รู้จักแบรนด์ผ่าน TikTok มีใครบ้าง” เพื่อใช้วางแผนแคมเปญการตลาดเฉพาะกลุ่ม การแปลงคำถามทางธุรกิจลักษณะนี้ให้เป็นคำสั่ง SQL ที่ชัดเจน เป็นระบบ และทำซ้ำได้ ถือเป็นทักษะสำคัญที่จะช่วยให้การวิเคราะห์ข้อมูลมีประสิทธิภาพและนำไปใช้ประโยชน์ได้จริง
SELECT คำสั่งพื้นฐานสำหรับการดึงข้อมูล
คำสั่ง SELECT เป็นคำสั่งพื้นฐานและใช้บ่อยที่สุดใน SQL ซึ่งทำหน้าที่ดึงข้อมูลจากตารางในฐานข้อมูลมาแสดงผล โดยเป็นการดำเนินการแบบ read-only ที่ไม่ทำการเปลี่ยนแปลงข้อมูลต้นฉบับแต่อย่างใด ทำให้เหมาะสำหรับการวิเคราะห์ข้อมูลทางธุรกิจโดยไม่ต้องกังวลเรื่องความปลอดภัยของข้อมูล
คำสั่ง SELECT มีหลายรูปแบบการใช้งาน เริ่มจากรูปแบบพื้นฐานที่สุดคือ
SELECT *สำหรับดึงข้อมูลทุกคอลัมน์
รูปแบบนี้ใช้เครื่องหมาย asterisk (*) เป็นตัวแทนของ “ทุกคอลัมน์” ในตาราง เหมาะสำหรับการสำรวจข้อมูลเบื้องต้น (exploratory data analysis) เมื่อต้องการทำความเข้าใจโครงสร้างและเนื้อหาของตารางอย่างรวดเร็ว
คำสั่งนี้จะดึงข้อมูลทุกคอลัมน์จากตาราง 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 * เหมาะสำหรับการสำรวจข้อมูลเบื้องต้นเมื่อต้องการดูโครงสร้างทั้งหมดอย่างรวดเร็ว แต่ไม่แนะนำในงานวิเคราะห์จริงเพราะดึงคอลัมน์ที่ไม่จำเป็น ทำให้ช้าและอ่านยากขึ้น นอกจากนี้หากโครงสร้างตารางเปลี่ยน ผลลัพธ์อาจเปลี่ยนตามโดยไม่ได้ตั้งใจ และในงานจริงควรระบุชื่อคอลัมน์ที่ต้องการเสมอ
SELECTสำหรับเลือกเฉพาะบางคอลัมน์
| 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 ใช้สำหรับ กรองข้อมูล ที่ต้องการจากตาราง โดยมีไวยากรณ์พื้นฐานดังนี้
ตัวอย่างแรกจะแสดงการประยุกต์ใช้ WHERE กับโจทย์ทางธุรกิจอย่างง่าย โดยตรวจสอบว่าเมนูใดในตาราง menus มีราคาสูงกว่า 120 บาท ผ่านเงื่อนไข WHERE list_price_thb > 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
| 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 หลังคอลัมน์ที่ต้องการเรียงลำดับ ดังนี้
หากต้องการเรียงราคาจากสูงสุดไปต่ำ ทำได้โดยใช้ ORDER BY โดยระบุ 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 สามารถเรียงตามหลายคอลัมน์ได้ คอลัมน์แรกเป็นเกณฑ์หลัก คอลัมน์ที่สองใช้เรียงเมื่อคอลัมน์แรกมีค่าเท่ากัน
| 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 ใช้เพื่อดึงเฉพาะค่าที่ไม่ซ้ำกันในคอลัมน์ที่ระบุ โดยมีรูปแบบพื้นฐานดังนี้
หากฝ่ายสมาชิกต้องการทราบว่า PiCha Tea House มีระดับสมาชิกอะไรบ้าง เพื่อออกแบบแคมเปญ onboard สำหรับแต่ละกลุ่ม คำถามนี้แสดงวิธีใช้ DISTINCT เพื่อต้องการรู้ว่า “มีค่าอะไรบ้าง” ในคอลัมน์นั้น (ในกรณีนี้คือคอลัมน์ 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 ใช้ได้กับตัวเลข วันที่ และข้อความ
สำหรับวันที่ ต้องระวังว่า '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
ถ้าเปลี่ยนเป็น 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 เสมอ ถ้าต้องการเปลี่ยนลำดับ ให้ใช้วงเล็บกำกับลำดับการประเมินนิพจน์
วงเล็บทำให้ความตั้งใจชัดเจน เหมือนบอกว่า “ตรวจ (gold หรือ tiktok) ก่อน แล้วค่อยตรวจ high” ไม่อย่างนั้นจะตรวจ tiktok กับ high ก่อน แล้วค่อย OR กับ gold ทำให้ได้ผลลัพธ์ที่ไม่ตรงเจตนา แนวปฏิบัติที่ดีคือใส่วงเล็บเสมอเมื่อใช้ AND กับ OR ร่วมกัน แม้ลำดับจะถูกต้องอยู่แล้ว เพราะช่วยให้คนอ่านเข้าใจได้ทันที
การใช้ IN ตรวจสอบเงื่อนไข
IN ทำงานเหมือน OR หลายตัวที่ตรวจสอบคอลัมน์เดียวกัน อ่านง่ายกว่าและบำรุงรักษาง่ายกว่า โดยเฉพาะเมื่อมีค่าที่ต้องเปรียบเทียบมากกว่า 2 ค่า
มาดูตัวอย่างการใช้งาน IN ในการดูสินค้าหมวด fruit_tea, blended และ pure_tea ทั้งหมดใน 3 หมวดนี้ ทำได้โดย
แบบที่ 1 ใช้ OR
แบบที่ 2 ใช้ IN เขียนสั้น กระชับ และอ่านง่ายกว่า
ผลลัพธ์ที่ได้จากทั้ง 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
| 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 ได้ เช่น
ตัวดำเนินการเงื่อนไขที่ผ่านมาทั้ง =, 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 ตัวอักษร แล้วตามด้วย TeaDuckDB รองรับ ILIKE ซึ่งไม่สนตัวพิมพ์ใหญ่เล็ก (case-insensitive) ทำให้ ILIKE '%milk%' จับได้ทั้ง “Milk Tea” และ “milk tea” ขณะที่ LIKE '%milk%' จับเฉพาะตัวพิมพ์เล็กเท่านั้น ในทางปฏิบัติ ILIKE เป็นตัวเลือกที่ปลอดภัยกว่าเมื่อไม่แน่ใจว่าข้อมูลเก็บเป็นตัวพิมพ์ใหญ่หรือพิมพ์เล็ก
ตัวอย่างเช่น หากต้องการหาเมนูที่มีคำว่า milk อยู่ในชื่อ
| 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 เป็นเพดานสูงสุดของระบบหรือเป็นพฤติกรรมจริงของลูกค้า
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
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 |
ในทางปฏิบัติ เราสามารถละคำว่า 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
คำถามท้ายบท
- เขียนคำสั่ง SQL เพื่อดูสาขาทั้งหมดที่มี daily_capacity มากกว่า 300 พร้อมชื่อและประเภท
ผลลัพธ์ที่คาดหวัง
| store_name | branch_type | daily_capacity |
|---|---|---|
| PiCha centralwOrld | mall | 400 |
| PiCha Siam Square | street | 450 |
| PiCha ICONSIAM | mall_flagship | 600 |
คลิกเพื่อดูเฉลย
- เขียนคำสั่ง SQL เพื่อนับจำนวนลูกค้าในแต่ละ discovery_channel
ผลลัพธ์ที่คาดหวัง
| discovery_channel | cnt |
|---|---|
| tiktok | 695 |
| 614 | |
| word_of_mouth | 546 |
| delivery_app | 351 |
| walk_in | 294 |
คลิกเพื่อดูเฉลย
- เขียนคำสั่ง 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 |
| … | … | … | … |