เปรียบเทียบผลการ Query ระหว่างมีการใช้ HAVING และการใช้คำสั่งคำนวณลงใน WHERE
ลองดูคำสั่ง SQL ด้านล่างทั้งสองคำสั่งนี้นะครับ
คำสั่งทั้งสองให้ผลลัพท์เหมือนกัน แตกต่างกันที่ คำสั่งแรกมีการใช้ HAVING ส่วนอีกคำสั่งมีการใส่คำสั่งคำนวณลงใน WHERE ตรงๆ
ก่อนอื่น ขออธิบายเงื่อนไขคำสั่งของด้านบนก่อน มีการคำนวณช่วงเวลาที่แตกต่างกันเก็บไว้ที่ diff และหลังจากเลือกข้อมูลเสร็จแล้ว มีการใช้ HAVING กรองข้อมูล diff ตามเงื่อนไขที่กำหนดอีกครั้ง ซึ่งผลการ Query เป็นไปตามรูปด้านล่าง สังเกตุเวลาประมวลผล (ลูกศรสีแดง) ที่รูปนะครับ ทีนี้ลองมาเปลี่ยนคำสั่งใหม่อีกครั้งดู ในครั้งนี้จะไม่มีการใช้ HAVING แต่จะเป็นการคำนวณภายใน WHERE เลย
พิจารณาที่เวลาประมวลผลในรูปด้านล่างเลยนะครับ ทั้งสองวิธีใช้เวลาในการประมวลผลแตกต่างกันมากโขอยู่ (ตรงลูกศรสีแดง) มาดูคำอธิบายของ MySQL กัน (EXPLAIN) รูปด้านบนคือคำอธิบายของวิธีแรก ด้านล่างคือคำอธิบายของวิธีที่สอง สิ่งที่แตกต่างกันคือด้านบนได้ผลลัพท์ 2 แถว ด้านล่างได้แถวเดียว คำอธิบายคือ เนื่องจากในแบบแรก มีการใช้ HAVING กรองข้อมูลหลังจาก Query แล้ว ทำให้เกิดการสำเนาข้อมูลซ้ำอีกครั้ง มันเลยช้ากว่าในขั้นตอนการสำเนา ในขณะที่ ในแบบหลัง ผลลัพท์สามารถนำไปใช้ได้เลย ซึ่งจะสังเกตุได้ชัดขึ้นหากตัด SELECT * ด้านนอกออก
หมายเหตุ : เนื่องจากในคำสั่งนี้จำเป็นต้องใช้ SELECT ซ้อน SELECT (คำสั่งจริงๆมีมากกว่านี้) เลยตัดเอามาให้ดูเป็นตัวอย่างเฉพาะส่วนที่มีปัญหาเท่านั้น เผื่อต้องเจอเคสลักษณะนี้
คำสั่งทั้งสองให้ผลลัพท์เหมือนกัน แตกต่างกันที่ คำสั่งแรกมีการใช้ HAVING ส่วนอีกคำสั่งมีการใส่คำสั่งคำนวณลงใน WHERE ตรงๆ
SELECT * FROM (
SELECT
O.id,
O.id AS contract_id,
O.lot_no,
O.job_id,
O.contract_no,
O.create_date AS date,
FLOOR((UNIX_TIMESTAMP() - O.create_date) / 86400) AS diff,
3 AS type,
O.create_date_comment AS comment,
O.create_date_alarm AS alarm
FROM `cdms_db`.`app_contract` AS O
WHERE O.`department` IN (1)
HAVING `diff` >= 55 AND `diff` > O.`create_date_alarm`
) AS Z;
ก่อนอื่น ขออธิบายเงื่อนไขคำสั่งของด้านบนก่อน มีการคำนวณช่วงเวลาที่แตกต่างกันเก็บไว้ที่ diff และหลังจากเลือกข้อมูลเสร็จแล้ว มีการใช้ HAVING กรองข้อมูล diff ตามเงื่อนไขที่กำหนดอีกครั้ง ซึ่งผลการ Query เป็นไปตามรูปด้านล่าง สังเกตุเวลาประมวลผล (ลูกศรสีแดง) ที่รูปนะครับ ทีนี้ลองมาเปลี่ยนคำสั่งใหม่อีกครั้งดู ในครั้งนี้จะไม่มีการใช้ HAVING แต่จะเป็นการคำนวณภายใน WHERE เลย
SELECT * FROM (
SELECT
O.id,
O.id AS contract_id,
O.lot_no,
O.job_id,
O.contract_no,
O.create_date AS date,
FLOOR((UNIX_TIMESTAMP() - O.create_date) / 86400) AS diff,
3 AS type,
O.create_date_comment AS comment,
O.create_date_alarm AS alarm
FROM cdms_db.app_contract AS O
WHERE
O.department IN (1)
AND FLOOR((UNIX_TIMESTAMP() - O.create_date) / 86400) >= 55
AND FLOOR((UNIX_TIMESTAMP() - O.create_date) / 86400) > O.create_date_alarm
) AS Z;
พิจารณาที่เวลาประมวลผลในรูปด้านล่างเลยนะครับ ทั้งสองวิธีใช้เวลาในการประมวลผลแตกต่างกันมากโขอยู่ (ตรงลูกศรสีแดง) มาดูคำอธิบายของ MySQL กัน (EXPLAIN) รูปด้านบนคือคำอธิบายของวิธีแรก ด้านล่างคือคำอธิบายของวิธีที่สอง สิ่งที่แตกต่างกันคือด้านบนได้ผลลัพท์ 2 แถว ด้านล่างได้แถวเดียว คำอธิบายคือ เนื่องจากในแบบแรก มีการใช้ HAVING กรองข้อมูลหลังจาก Query แล้ว ทำให้เกิดการสำเนาข้อมูลซ้ำอีกครั้ง มันเลยช้ากว่าในขั้นตอนการสำเนา ในขณะที่ ในแบบหลัง ผลลัพท์สามารถนำไปใช้ได้เลย ซึ่งจะสังเกตุได้ชัดขึ้นหากตัด SELECT * ด้านนอกออก
หมายเหตุ : เนื่องจากในคำสั่งนี้จำเป็นต้องใช้ SELECT ซ้อน SELECT (คำสั่งจริงๆมีมากกว่านี้) เลยตัดเอามาให้ดูเป็นตัวอย่างเฉพาะส่วนที่มีปัญหาเท่านั้น เผื่อต้องเจอเคสลักษณะนี้