ความแตกต่างระหว่าง ประโยค HAVING กับ ประโยค WHERE

ความแตกต่างระหว่าง ประโยค HAVING กับ ประโยค WHERE
สำหรับบทความนี้ ผู้เขียนเคยได้รับคำถามเกี่ยวกับความแตกต่างระหว่างประโยค HAVING กับประโยค WHERE ในคำสั่ง SELECT ค่อนข้างบ่อย
ดังนั้น เพื่อความกระจ่างแก่ผู้อ่าน จึง ขอยกเรื่องลำดับในการประมวลผลของประโยคต่าง ๆ ในคำสั่ง SELECT มาให้ทบทวน หรือ ทำความเข้าใจกัน
ดังตารางต่อไปนี้
สำหรับบทความนี้ ผู้เขียนเคยได้รับคำถามเกี่ยวกับความแตกต่างระหว่างประโยค HAVING กับประโยค WHERE ในคำสั่ง SELECT ค่อนข้างบ่อย
ดังนั้น เพื่อความกระจ่างแก่ผู้อ่าน จึง ขอยกเรื่องลำดับในการประมวลผลของประโยคต่าง ๆ ในคำสั่ง SELECT มาให้ทบทวน หรือ ทำความเข้าใจกัน
ดังตารางต่อไปนี้

ตารางแสดงลำดับการประมวลผลคำสั่ง SELECT
ที่ผู้เขียนต้องปูพื้นฐานเกี่ยวกับลำดับการประมวลผลในคำสั่ง SELECT นั้นเป็นเพราะมีผู้อ่านจำนวนไม่น้อยเลย ที่เข้าใจว่าคำสั่ง SELECT จะมีลำดับการประมวลผลจากประโยค SELECT เป็นลำดับแรก
แต่หากดูจาก ตารางแสดงลำดับการประมวลผลคำสั่ง SELECT จะพบว่า
ประโยค SELECT นั้นได้รับการประมวลผลเป็นลำดับที่ 5 เสียด้วยซ้ำ
(ผู้เขียนเรียกคำสั่ง SELECT หมายถึงทั้งคำสั่งในการ Query แต่ หากเรียกว่าประโยค SELECT จะหมายถึง คำว่า “SELECT” เพียงคำเดียวจากทั้งหมดที่จะประกอบด้วย FROM, WHERE, GROUP BY, HAVING และ ORDER BY ขอผู้อ่านอย่าสับสน)
ผู้เขียนขอยกตัวอย่างให้เห็นอย่างง่าย ๆ ดังนี้

จากรูปจะเห็นว่าผู้เขียนได้ตั้งชื่อเล่น (Alias) ให้กับตารางในประโยค FROM แล้วจึงนำไปอ้างอิงถึงในประโยค WHERE และ SELECT
หากลำดับการประมวลผลทำจากบนลงล่างอย่างหลายคนเข้าใจ Query นี้คงจะฟ้องข้อผิดพลาดออกมา เพราะมีการนำชื่อเล่นไปใช้ก่อน ที่จะประกาศให้มีชื่อเล่นเสียด้วยซ้ำ
แต่ Query นี้ประมวลผลผ่านฉลุย ไม่ฟ้องข้อผิดพลาดแต่ประการใด เพราะหากพิจารณาตาม ตารางแสดงลำดับการประมวลผลคำสั่ง SELECT
จะเห็นว่าการประกาศชื่อเล่นให้กับตารางนั้น ทำในประโยค FROM ซึ่งได้รับการประมวลผลเป็นลำดับที่ 1 แล้ว
จึงนำชื่อเล่นที่ประกาศไปใช้งานในประโยค SELECT ซึ่งถูกประมวลผลเป็นลำดับที่ 5 เลยสามารถกระทำได้
คราวนี้กลับมาที่ประโยค WHERE กับ HAVING ต่างกันตรงไหน เพราะทั้งสองประโยคใช้เป็น <Search Condition> ด้วยกันทั้งคู่
จาก ตารางแสดงลำดับการประมวลผลคำสั่ง SELECT จะเห็นว่า
ประโยค WHERE นั้นมีลำดับการประมวลผลเป็นลำดับที่ 2 ถัดจากประโยค FROM
แต่ HAVING นั้นมีลำดับการประมวลผลเป็นลำดับที่ 4 ถัดจากประโยค GROUP BY
นั่นหมายความว่าประโยค WHERE จะใช้กรองผลลัพธ์จากประโยค FROM ก่อนที่ส่งต่อผลลัพธ์นั้นเข้าประมวลผลในประโยค GROUP BY
จากนั้นประโยค HAVING จึงกรองผลลัพธ์ที่ได้จากประโยค GROUP BY อีกทอดหนึ่ง
ผู้เขียนได้แสดงให้เห็นผลลัพธ์ข้อมูล เทียบเคียงลำดับในการประมวลผล ได้ผลดังต่อไปนี้
ลำดับที่ 1 SELECT ข้อมูลพนักงานทุกแถวข้อมูล โดย Query ต่อไปนี้
หากลำดับการประมวลผลทำจากบนลงล่างอย่างหลายคนเข้าใจ Query นี้คงจะฟ้องข้อผิดพลาดออกมา เพราะมีการนำชื่อเล่นไปใช้ก่อน ที่จะประกาศให้มีชื่อเล่นเสียด้วยซ้ำ
แต่ Query นี้ประมวลผลผ่านฉลุย ไม่ฟ้องข้อผิดพลาดแต่ประการใด เพราะหากพิจารณาตาม ตารางแสดงลำดับการประมวลผลคำสั่ง SELECT
จะเห็นว่าการประกาศชื่อเล่นให้กับตารางนั้น ทำในประโยค FROM ซึ่งได้รับการประมวลผลเป็นลำดับที่ 1 แล้ว
จึงนำชื่อเล่นที่ประกาศไปใช้งานในประโยค SELECT ซึ่งถูกประมวลผลเป็นลำดับที่ 5 เลยสามารถกระทำได้
คราวนี้กลับมาที่ประโยค WHERE กับ HAVING ต่างกันตรงไหน เพราะทั้งสองประโยคใช้เป็น <Search Condition> ด้วยกันทั้งคู่
จาก ตารางแสดงลำดับการประมวลผลคำสั่ง SELECT จะเห็นว่า
ประโยค WHERE นั้นมีลำดับการประมวลผลเป็นลำดับที่ 2 ถัดจากประโยค FROM
แต่ HAVING นั้นมีลำดับการประมวลผลเป็นลำดับที่ 4 ถัดจากประโยค GROUP BY
นั่นหมายความว่าประโยค WHERE จะใช้กรองผลลัพธ์จากประโยค FROM ก่อนที่ส่งต่อผลลัพธ์นั้นเข้าประมวลผลในประโยค GROUP BY
จากนั้นประโยค HAVING จึงกรองผลลัพธ์ที่ได้จากประโยค GROUP BY อีกทอดหนึ่ง
ผู้เขียนได้แสดงให้เห็นผลลัพธ์ข้อมูล เทียบเคียงลำดับในการประมวลผล ได้ผลดังต่อไปนี้
ลำดับที่ 1 SELECT ข้อมูลพนักงานทุกแถวข้อมูล โดย Query ต่อไปนี้

ผลลัพธ์ที่ได้คือ

จะเห็นว่าผลลัพธ์มีทั้งหมด 31,465 แถวข้อมูล เป็นข้อมูลยอดขายแต่ละรายการขาย
โดยประกอบด้วยเลขที่การขาย, เลขที่ใบสั่งซื้อ, เลขประจำตัวพนักงานขาย, ยอดขาย และวันที่ขาย
จะเห็นได้ว่าบางรายการขายนั้นไม่มีเลขที่ใบสั่งซื้อ และเลขประจำตัวพนักงานขาย เนื่องมาจากเป็นการขายผ่านอินเตอร์เน็ต
ลำดับที่ 2 กรองข้อมูลด้วยประโยค WHERE โดย Query ต่อไปนี้
โดยประกอบด้วยเลขที่การขาย, เลขที่ใบสั่งซื้อ, เลขประจำตัวพนักงานขาย, ยอดขาย และวันที่ขาย
จะเห็นได้ว่าบางรายการขายนั้นไม่มีเลขที่ใบสั่งซื้อ และเลขประจำตัวพนักงานขาย เนื่องมาจากเป็นการขายผ่านอินเตอร์เน็ต
ลำดับที่ 2 กรองข้อมูลด้วยประโยค WHERE โดย Query ต่อไปนี้

ผลลัพธ์ที่ได้คือ

จะเห็นว่าผลลัพธ์เหลือเพียง 12,443 แถวข้อมูล จากประโยค WHERE ที่เพิ่มเข้าไป
ลำดับที่ 3 รวมกลุ่มข้อมูลที่กรองมาแล้ว (ประโยค WHERE) ด้วยประโยค GROUP BY โดย Query ต่อไปนี้
ลำดับที่ 3 รวมกลุ่มข้อมูลที่กรองมาแล้ว (ประโยค WHERE) ด้วยประโยค GROUP BY โดย Query ต่อไปนี้

Query ดังกล่าวเป็นการรวมกลุ่มงานขายของพนักงานขายแต่ละคนเข้าด้วยกัน
โดยงานขายใดระบุพนักงานขายไม่ได้ในไปรวมกันอยู่ใน Internet Sales
ผลลัพธ์ที่ได้คือ
โดยงานขายใดระบุพนักงานขายไม่ได้ในไปรวมกันอยู่ใน Internet Sales
ผลลัพธ์ที่ได้คือ

จะเห็นว่าเมื่อรวมกลุ่มข้อมูลแล้วเหลือเพียง 18 แถวข้อมูลเท่านั้น
ลำดับที่ 4 กรองข้อมูลที่รวมกลุ่มแล้วด้วยประโยค HAVING โดย Query ต่อไปนี้
ลำดับที่ 4 กรองข้อมูลที่รวมกลุ่มแล้วด้วยประโยค HAVING โดย Query ต่อไปนี้

ผลลัพธ์ที่ได้คือ

ผลลัพธ์ที่ได้เหลือเพียง 5 แถวข้อมูลเป็น แถวข้อมูลที่มียอดรวม (เกิดจากประโยค GROUP BY ก่อนหน้า) เกินกว่า 3 ล้าน
สรุป
จากผลการทดลองทั้งหมดจะเห็นว่าข้อมูลสามารถถูกกรองก่อนนำไปหาผลรวมผ่านประโยค WHERE
และภายหลังจากการหาผลรวมแล้วก็สามารถกรองผลของผลรวมได้ด้วยประโยค HAVING อีกทอดหนึ่ง
ผู้อ่านคงจะพอเห็นประโยชน์ของทั้งสองประโยค แม้ว่าจะเป็น <Search Condition> ด้วยกันทั้งคู่
แต่เมื่อทำในลำดับการประมวลผลที่ต่างกันแล้ว ก็สามารถนำไปใช้ประโยชน์กันคนละแบบ
หากผู้อ่านยังสงสัยอยู่ให้ลองนำเอา Query ในแต่ละลำดับขั้นตอนที่ผู้เขียนนำเสนอไว้นี้ ไปทดลองกับฐานข้อมูล AdventureWorks เองอีกครั้งเพื่อความเข้าใจ
แต่สิ่งที่ผู้เขียนอยากให้ผู้อ่านคำนึงถึง และเห็นจะเป็นประโยชน์มากกว่า ก็คือ
ลำดับการประมวลผลคำสั่ง SELECT เพราะหากผู้อ่านแม่นยำในลำดับดังกล่าว
การเขียน Query ของผู้อ่านก็จะง่าย และ สามารถมองภาพรวมออกเสมอ ด้วยความปรารถนาดี
สรุป
จากผลการทดลองทั้งหมดจะเห็นว่าข้อมูลสามารถถูกกรองก่อนนำไปหาผลรวมผ่านประโยค WHERE
และภายหลังจากการหาผลรวมแล้วก็สามารถกรองผลของผลรวมได้ด้วยประโยค HAVING อีกทอดหนึ่ง
ผู้อ่านคงจะพอเห็นประโยชน์ของทั้งสองประโยค แม้ว่าจะเป็น <Search Condition> ด้วยกันทั้งคู่
แต่เมื่อทำในลำดับการประมวลผลที่ต่างกันแล้ว ก็สามารถนำไปใช้ประโยชน์กันคนละแบบ
หากผู้อ่านยังสงสัยอยู่ให้ลองนำเอา Query ในแต่ละลำดับขั้นตอนที่ผู้เขียนนำเสนอไว้นี้ ไปทดลองกับฐานข้อมูล AdventureWorks เองอีกครั้งเพื่อความเข้าใจ
แต่สิ่งที่ผู้เขียนอยากให้ผู้อ่านคำนึงถึง และเห็นจะเป็นประโยชน์มากกว่า ก็คือ
ลำดับการประมวลผลคำสั่ง SELECT เพราะหากผู้อ่านแม่นยำในลำดับดังกล่าว
การเขียน Query ของผู้อ่านก็จะง่าย และ สามารถมองภาพรวมออกเสมอ ด้วยความปรารถนาดี
บทความโดย
อาจารย์ภัคพงศ์ กฤตวัฒน์
วิทยากรดูแลและออกแบบหลักสูตร
กลุ่มวิชา SQL Server/Window Server