
SQL เกือบไม่ได้ใช้ชื่อนี้ เพราะโดนบริษัทเครื่องบินฟ้อง — เรื่องนี้ฟังดูเหมือนมุกตลก แต่เป็นเรื่องจริงที่อยู่เบื้องหลังภาษาที่โปรแกรมเมอร์ทั่วโลกใช้งานทุกวัน หลายคนพิมพ์คำสั่ง SELECT เป็นร้อยครั้งต่อวันโดยไม่เคยรู้เลยว่าเบื้องหลังนั้นฐานข้อมูลทำอะไรบ้างกว่าจะได้ผลลัพธ์กลับมาบนหน้าจอ บทความนี้จะพาไปดูตั้งแต่ต้นทางจนจบทาง ว่า Query หนึ่งคำสั่งเดินทางผ่านกลไกอะไรบ้างภายในเครื่องยนต์ของฐานข้อมูลอย่าง PostgreSQL หรือ MySQL
TL;DR: เมื่อรัน SQL Query ฐานข้อมูลจะประมวลผลผ่าน 4 ขั้นตอนหลักคือ Parser (ตรวจไวยากรณ์), Query Rewriter (ปรับโครงสร้าง), Query Optimizer (เลือกแผนที่ต้นทุนต่ำสุดโดยพิจารณา Index Scan, Join Algorithm และสถิติข้อมูล) และ Execution Engine (ดึงข้อมูลจริงผ่าน Buffer Pool แล้วส่งผลลัพธ์กลับมา) การเข้าใจกลไกนี้ช่วยให้เขียน Query ที่เร็วขึ้นได้หลายเท่าโดยไม่ต้องเปลี่ยนฮาร์ดแวร์เลย
- SQL เดิมชื่อ ‘SEQUEL’ คิดค้นโดย Donald Chamberlin และ Raymond Boyce ที่ IBM ในปี 1974 แต่ต้องเปลี่ยนชื่อเพราะ ‘SEQUEL’ เป็นเครื่องหมายการค้าที่จดไปแล้วโดยบริษัทเครื่องบินสัญชาติอังกฤษ Hawker Siddeley
- แม้ IBM จะเป็นผู้คิดค้น SQL ผ่านโปรเจกต์ System R แต่บริษัทที่วางขาย SQL เชิงพาณิชย์ตัวแรกในโลกกลับเป็น Oracle (ตอนนั้นชื่อ Relational Software Inc.) ในปี 1979 ซึ่งเร็วกว่า IBM เองถึง 2 ปี
- SQL ถูกใช้งานจริงในอุตสาหกรรมมานานกว่า 12 ปี ก่อนจะได้รับการรับรองเป็นมาตรฐาน ANSI อย่างเป็นทางการในปี 1986 และ ISO ในปี 1987
ทำไมต้องเข้าใจเบื้องหลังการประมวลผล SQL Query
เพราะการรู้ว่าฐานข้อมูลคิดอะไรอยู่ในหัวช่วยให้เขียนโค้ดที่เร็วขึ้นได้จริง โปรแกรมเมอร์ส่วนใหญ่เรียนรู้ SQL แบบ “ท่องคำสั่ง” คือรู้ว่า SELECT ... WHERE ... JOIN ต้องเขียนยังไง แต่ไม่รู้ว่าทำไม Query สองอันที่ให้ผลลัพธ์เหมือนกันเป๊ะถึงใช้เวลาต่างกันเป็น 10-100 เท่า คำตอบอยู่ที่กระบวนการภายในซึ่งประกอบด้วย Parser, Optimizer และ Execution Engine ที่ทำงานร่วมกันแบบมีลำดับขั้นชัดเจน
ทักษะนี้สำคัญมากสำหรับคนที่ทำงานสาย Backend, Data Engineer หรือแม้แต่คนที่กำลังศึกษา AI Agent ทำงานด้วย ReAct Loop อย่างไร เพราะ AI Agent จำนวนมากในปัจจุบันต้องสร้าง SQL Query อัตโนมัติเพื่อดึงข้อมูลจากฐานข้อมูล การเข้าใจว่า Query ที่ AI สร้างขึ้นมาจะถูกประมวลผลอย่างไรช่วยให้ตรวจสอบและปรับแต่งประสิทธิภาพได้ตั้งแต่ต้นทาง สำหรับใครที่สนใจเนื้อหาสาย Coding เพิ่มเติม บทความนี้จะเป็นพื้นฐานสำคัญที่ต่อยอดไปเรียนเรื่อง Indexing และ Query Tuning ได้ต่อ
สิ่งที่ต้องเตรียมก่อนเริ่ม
- ติดตั้ง PostgreSQL หรือ MySQL เวอร์ชันล่าสุดบนเครื่อง หรือใช้บริการ Cloud Database ก็ได้
- เครื่องมือรัน Query เช่น
psql,mysqlCLI, หรือโปรแกรมอย่าง DBeaver/TablePlus - พื้นฐานคำสั่ง SQL เบื้องต้น เช่น
SELECT,JOIN,WHERE,GROUP BY - ความเข้าใจพื้นฐานเรื่อง Data Structure เช่น Tree และ Array จะช่วยให้เข้าใจ Index ได้ง่ายขึ้น
- ตารางตัวอย่างที่มีข้อมูลพอสมควร (หลักพันแถวขึ้นไป) เพื่อให้เห็นความแตกต่างของ Execution Plan ชัดเจน
ขั้นตอนการประมวลผล SQL Query แบบละเอียด
ต่อไปนี้คือลำดับขั้นตอนจริงที่เกิดขึ้นภายในฐานข้อมูลเชิงสัมพันธ์ (Relational Database) ทุกครั้งที่มีการรัน Query ตั้งแต่วินาทีที่กด Enter จนถึงตอนที่ผลลัพธ์ปรากฏบนหน้าจอ
ขั้นตอนที่ 1: Parser ตรวจสอบไวยากรณ์และสร้าง Parse Tree
เมื่อรันคำสั่ง SELECT ฐานข้อมูลจะส่งข้อความไปให้ “Parser” ตรวจสอบไวยากรณ์และแปลงเป็นโครงสร้างต้นไม้ที่เรียกว่า Parse Tree ก่อน ขั้นตอนนี้ Parser จะเช็คว่าคำสั่งเขียนถูกไวยากรณ์ไหม เช่น มีคอมม่าครบไหม เขียน FROM ก่อน WHERE รึเปล่า ถ้าเขียนผิดจะได้ error ทันทีในขั้นนี้ก่อนที่ระบบจะไปแตะข้อมูลจริงเลยด้วยซ้ำ ซึ่งเป็นเหตุผลว่าทำไมพิมพ์ผิดถึงรู้ผลไวมาก
ขั้นตอนที่ 2: Query Rewriter ปรับโครงสร้างคำสั่ง
Parse Tree ถูกส่งต่อให้ “Query Rewriter” เพื่อขยาย View หรือแทนที่ Subquery ให้อยู่ในรูปแบบที่เหมาะกับการประมวลผล ตัวอย่างเช่นถ้า Query อ้างอิงถึง View ระบบจะแทนที่ด้วยคำสั่งจริงที่อยู่เบื้องหลัง View นั้น หรือถ้ามี Subquery ที่สามารถแปลงเป็น JOIN ได้ ตัว Rewriter ก็จะแปลงให้ เพื่อให้ขั้นตอนถัดไปทำงานง่ายขึ้น
ขั้นตอนที่ 3: Query Optimizer คำนวณแผนการทำงานที่เป็นไปได้
“Query Optimizer” คำนวณแผนการทำงานที่เป็นไปได้หลายแบบ (Execution Plan) โดยอ้างอิงสถิติข้อมูล เช่น จำนวนแถวและการกระจายตัวของค่าในคอลัมน์ นี่คือหัวใจสำคัญที่สุดของกระบวนการทั้งหมด เพราะ Query เดียวกันสามารถหาคำตอบได้หลายวิธี Optimizer จะจำลองแต่ละแนวทางแล้วประเมินว่าแบบไหนใช้ทรัพยากรน้อยที่สุด
โครงสร้างดัชนี B-Tree ที่ฐานข้อมูลอย่าง MySQL, PostgreSQL และ Oracle ใช้เร่งความเร็วการค้นหาข้อมูลทุกวันนี้ ถูกคิดค้นโดย Rudolf Bayer และ Edward McCreight ตั้งแต่ปี 1970 ที่ Boeing Research Labs ก่อน SQL จะถือกำเนิดเสียอีก
ขั้นตอนที่ 4: เลือกแผนต้นทุนต่ำสุดด้วย Cost-Based Optimization
Optimizer เลือกแผนที่มี “ต้นทุนต่ำสุด” (Cost-Based Optimization) โดยเปรียบเทียบว่า Index Scan หรือ Full Table Scan แบบไหนเร็วกว่าในสถานการณ์นั้น ถ้าตารางมีข้อมูลไม่มาก การทำ Full Table Scan อาจเร็วกว่าการเปิด Index ด้วยซ้ำ เพราะการอ่าน Index เองก็มีต้นทุน แต่ถ้าตารางมีข้อมูลหลักล้านแถวและ Query กรองข้อมูลแค่ไม่กี่แถว Index Scan จะชนะขาด ลองดูตัวอย่างการตรวจสอบแผนด้วย EXPLAIN ใน PostgreSQL:
EXPLAIN ANALYZE
SELECT customer_id, order_total
FROM orders
WHERE customer_id = 10245;
คำสั่ง EXPLAIN ANALYZE จะรัน Query จริงและแสดงแผนที่ Optimizer เลือกใช้ ถ้าเห็นคำว่า Index Scan using orders_customer_id_idx แปลว่าระบบใช้ดัชนีช่วยค้นหา แต่ถ้าเห็น Seq Scan on orders แปลว่ากำลังอ่านทุกแถวในตาราง ซึ่งช้ากว่ามากเมื่อข้อมูลเยอะ
ขั้นตอนที่ 5: เลือกอัลกอริทึม JOIN ที่เหมาะสม
หากมีการ JOIN หลายตาราง ระบบจะเลือกอัลกอริทึม เช่น Nested Loop, Hash Join หรือ Merge Join ตามขนาดและดัชนีของตารางที่เกี่ยวข้อง Nested Loop เหมาะกับกรณีที่ตารางหนึ่งมีข้อมูลน้อยและอีกตารางมี Index รองรับ ส่วน Hash Join เหมาะกับการ JOIN ตารางใหญ่สองตารางที่ไม่มี Index ตรงคอลัมน์ที่ใช้ JOIN ขณะที่ Merge Join จะทำงานได้ดีเมื่อทั้งสองตารางถูกเรียงลำดับไว้แล้ว
SELECT c.customer_name, o.order_total
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
WHERE c.region = 'Bangkok';
ในตัวอย่างนี้ ถ้าตาราง customers ที่กรองด้วย region = 'Bangkok' เหลือแค่ไม่กี่ร้อยแถว และ orders มี Index บน customer_id Optimizer มักเลือกใช้ Nested Loop เพราะวิ่งวนแค่จำนวนแถวน้อยๆ แล้ว lookup ผ่าน Index ในแต่ละรอบ
ขั้นตอนที่ 6: Execution Engine ดึงข้อมูลผ่าน Buffer Pool
แผนที่เลือกแล้วถูกส่งไปยัง “Execution Engine” ซึ่งดึงข้อมูลจริงจาก Storage Engine ผ่าน Buffer Pool ซึ่งเป็นหน่วยความจำแคชของฐานข้อมูล Buffer Pool คือพื้นที่ใน RAM ที่เก็บหน้าข้อมูล (Page) ที่เคยถูกอ่านมาแล้ว เพื่อไม่ต้องอ่านจากดิสก์ซ้ำทุกครั้ง นี่คือเหตุผลว่าทำไม Query เดียวกันรันครั้งที่สองมักเร็วกว่าครั้งแรก
ขั้นตอนที่ 7: จัดการ Page Fault เมื่อข้อมูลไม่อยู่ใน Cache
ถ้าข้อมูลที่ต้องการไม่มีอยู่ใน Buffer Pool จะเกิด “Page Fault” ทำให้ระบบต้องอ่านข้อมูลจากดิสก์เข้ามาเก็บใน RAM ก่อนประมวลผลต่อ กระบวนการนี้ช้ากว่าการอ่านจาก RAM มาก เพราะดิสก์ (แม้จะเป็น SSD) ก็ยังช้ากว่าหน่วยความจำหลายเท่าตัว ระบบฐานข้อมูลจึงพยายามขยาย Buffer Pool ให้ใหญ่พอที่จะเก็บข้อมูลที่ใช้บ่อยไว้ในหน่วยความจำเสมอ ซึ่งหลักการนี้คล้ายกับที่อธิบายไว้ใน AI ประมวลผลภาพ 250MP ได้ใน 0.3 วินาทีได้อย่างไร ที่การจัดการหน่วยความจำและแคชมีผลอย่างมากต่อความเร็วในการประมวลผลข้อมูลขนาดใหญ่
ขั้นตอนที่ 8: จัดเรียงผลลัพธ์และส่งกลับเป็น Result Set
ผลลัพธ์ถูกจัดเรียงตามคำสั่ง เช่น ORDER BY หรือ GROUP BY แล้วส่งกลับไปเป็น Result Set ให้ผู้ใช้งานเห็นบนหน้าจอ ขั้นตอนนี้อาจใช้หน่วยความจำเพิ่มเติมถ้าข้อมูลที่ต้องจัดเรียงมีขนาดใหญ่เกินกว่าจะทำใน RAM ได้ทั้งหมด ซึ่งจะทำให้ระบบต้องใช้ดิสก์ชั่วคราวช่วย (Disk-based Sort) และทำให้ Query ช้าลงอย่างเห็นได้ชัด
ตัวอย่างโค้ดสมบูรณ์: จาก Query ธรรมดาสู่ Recursive CTE
มาดูตัวอย่างที่รวมทุกแนวคิดที่อธิบายไปข้างต้น เริ่มจาก Query พื้นฐานที่มีทั้ง JOIN, WHERE และ ORDER BY:
SELECT
p.product_name,
SUM(oi.quantity) AS total_sold,
AVG(oi.unit_price) AS avg_price
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
WHERE oi.order_date >= '2026-01-01'
GROUP BY p.product_name
ORDER BY total_sold DESC
LIMIT 10;
อธิบายทีละส่วน: JOIN products p ON oi.product_id = p.product_id คือจุดที่ Optimizer ต้องตัดสินใจเลือกอัลกอริทึม JOIN ตามที่กล่าวไปในขั้นตอนที่ 5 ส่วน WHERE oi.order_date >= '2026-01-01' คือเงื่อนไขกรองที่ Optimizer จะพยายามใช้ Index ถ้ามีการสร้างดัชนีไว้บนคอลัมน์ order_date และ GROUP BY กับ ORDER BY คือขั้นตอนสุดท้ายที่เกิดขึ้นในขั้นตอนที่ 8 ของ Execution Engine
อีกตัวอย่างหนึ่งที่มือใหม่มักเข้าใจผิดว่าเป็นฟีเจอร์สมัยใหม่ คือ Recursive CTE ซึ่งใช้หาข้อมูลแบบลำดับชั้น เช่น โครงสร้างองค์กรที่มีหัวหน้า-ลูกน้องซ้อนกันหลายชั้น:
WITH RECURSIVE employee_hierarchy AS (
SELECT employee_id, name, manager_id, 1 AS level
FROM employees
WHERE manager_id IS NULL
UNION ALL
SELECT e.employee_id, e.name, e.manager_id, eh.level + 1
FROM employees e
JOIN employee_hierarchy eh ON e.manager_id = eh.employee_id
)
SELECT * FROM employee_hierarchy ORDER BY level;
ส่วนแรกของ WITH RECURSIVE (Anchor Member) คือจุดเริ่มต้นที่หาพนักงานที่ไม่มีหัวหน้า ส่วนที่สอง (Recursive Member) จะวนซ้ำหาลูกน้องของแต่ละคนไปเรื่อยๆ จนกว่าจะไม่มีข้อมูลเพิ่ม ฐานข้อมูลจะรันส่วนนี้วนซ้ำภายใน Execution Engine เหมือนเป็น Loop จนกว่าผลลัพธ์จะว่างเปล่า
เคล็ดลับและข้อควรระวังในการเขียน Query ให้เร็ว
- ใช้
EXPLAIN ANALYZEทุกครั้งที่สงสัยว่า Query ช้าตรงไหน อย่าเดาเอง เพราะ Optimizer อาจไม่ได้เลือกแผนที่คิดไว้ในหัว - อย่าสร้าง Index มากเกินไป เพราะทุก Index ที่เพิ่มขึ้นทำให้การ INSERT/UPDATE ช้าลง เนื่องจากต้องอัปเดตทั้งตารางหลักและ Index พร้อมกัน
- ระวังการใช้ฟังก์ชันครอบคอลัมน์ใน WHERE เช่น
WHERE YEAR(order_date) = 2026เพราะจะทำให้ Optimizer ไม่สามารถใช้ Index บนคอลัมน์นั้นได้ ควรเขียนเป็นWHERE order_date >= '2026-01-01'แทน - สถิติข้อมูล (Statistics) ที่ Optimizer ใช้คำนวณต้นทุนอาจล้าสมัยได้ ถ้าข้อมูลเปลี่ยนแปลงเยอะควรรันคำสั่ง
ANALYZE(PostgreSQL) หรือANALYZE TABLE(MySQL) เป็นระยะ - Buffer Pool ที่เล็กเกินไปเมื่อเทียบกับขนาดข้อมูลจะทำให้เกิด Page Fault บ่อย ควรปรับค่า
shared_buffers(PostgreSQL) หรือinnodb_buffer_pool_size(MySQL) ให้เหมาะกับ RAM ที่มี
ขั้นตอนต่อไป
หลังจากเข้าใจกลไกเบื้องหลังแล้ว ขั้นตอนถัดไปคือลองฝึกอ่าน Execution Plan จริงจากฐานข้อมูลของตัวเอง และทดลองสร้าง Index แบบต่างๆ เพื่อดูว่า Optimizer ตัดสินใจเปลี่ยนแปลงยังไง หากสนใจนำความรู้นี้ไปต่อยอดสร้างระบบที่ใช้ AI ช่วยวิเคราะห์หรือสร้าง Query อัตโนมัติ ลองดูบทความใน AI How-To หรือถ้าอยากศึกษาการใช้เครื่องมือ AI สายอื่นเพิ่มเติมก็ลองอ่าน AI Image Generation: สร้างภาพสวยงามด้วย AI (Midjourney, DALL-E 2) เพื่อเห็นภาพกว้างของวงการเทคโนโลยีในปัจจุบัน และหากอยากรู้ว่าทักษะ SQL และ Backend สามารถต่อยอดเป็นรายได้ได้อย่างไร ลองอ่าน โปรแกรมเมอร์สร้างรายได้เสริม 180K ดอลลาร์ต่อปีได้อย่างไร
คำถามที่พบบ่อย (FAQ)
Query Optimizer กับ Query Planner ต่างกันยังไง?
A: จริงๆ แล้วเป็นชื่อเรียกที่ใช้แทนกันได้ในหลายฐานข้อมูล PostgreSQL มักเรียกว่า Planner/Optimizer ส่วน MySQL เรียกว่า Optimizer ทั้งคู่ทำหน้าที่เดียวกันคือคำนวณและเลือกแผนการทำงานที่มีต้นทุนต่ำสุดจากหลายๆ แผนที่เป็นไปได้ก่อนจะส่งไปให้ Execution Engine ทำงานจริง
ทำไม Query ที่เขียนเหมือนกันบนคนละฐานข้อมูลถึงเร็วช้าต่างกัน?
A: เพราะแต่ละฐานข้อมูลมี Optimizer และ Storage Engine ที่พัฒนาแยกกัน MySQL ใช้ InnoDB เป็น Storage Engine หลักที่มีวิธีจัดการ Buffer Pool และ Index แบบเฉพาะตัว ขณะที่ PostgreSQL มีระบบ MVCC และ Statistics Collector ที่ต่างออกไป ทำให้แผนการทำงานและความเร็วที่ได้ไม่เท่ากันแม้ Query จะเขียนเหมือนกันเป๊ะ
ควรสร้าง Index ให้ทุกคอลัมน์ที่ใช้ใน WHERE เลยไหม?
A: ไม่ควร เพราะ Index แต่ละตัวมีต้นทุนในการดูแลรักษา ทุกครั้งที่มีการ INSERT, UPDATE หรือ DELETE ฐานข้อมูลต้องอัปเดต Index ทุกตัวที่เกี่ยวข้องไปด้วย ควรสร้าง Index เฉพาะคอลัมน์ที่ถูกใช้ค้นหาหรือกรองบ่อยจริงๆ และตรวจสอบผลด้วย EXPLAIN ANALYZE ก่อนตัดสินใจ
Buffer Pool กับ Cache ของระบบปฏิบัติการต่างกันยังไง?
A: Buffer Pool เป็นแคชที่ฐานข้อมูลจัดการเองโดยรู้จักโครงสร้างข้อมูลภายใน เช่น รู้ว่า Page ไหนคือ Index รู้ว่า Page ไหนถูกใช้บ่อย จึงตัดสินใจเก็บหรือลบข้อมูลได้ฉลาดกว่า ส่วน Cache ของระบบปฏิบัติการเป็นการแคชระดับไฟล์ทั่วไปที่ไม่รู้ความหมายของข้อมูลข้างใน การมี Buffer Pool ขนาดใหญ่พอจึงช่วยลดการอ่านดิสก์ได้อย่างมีประสิทธิภาพกว่า
แล้วทำไม Query หน้าตาเหมือนกันเป๊ะถึงเร็วช้าต่างกันเป็น 100 เท่า? คลิปหน้าจะเฉลยเรื่อง Index ที่มือใหม่เข้าใจผิดบ่อยที่สุด
อัปเดตล่าสุด: 23 September 2026 บน AiDevThai
ปลั๊กอิน WordPress จากเรา: Exit Pop Pro
ป๊อปอัพ exit-intent ที่แจก PDF ฟรี แลกอีเมล — เก็บ subscriber เข้า WordPress ของคุณโดยตรง จ่ายครั้งเดียว $29 ไม่มีค่ารายเดือน ไม่ต้องง้อ SaaS