เปรียบเทียบผลการ Query ระหว่างมีการใช้ HAVING และการใช้คำสั่งคำนวณลงใน WHERE

ลองดูคำสั่ง SQL ด้านล่างทั้งสองคำสั่งนี้นะครับ
คำสั่งทั้งสองให้ผลลัพท์เหมือนกัน แตกต่างกันที่ คำสั่งแรกมีการใช้ 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 (คำสั่งจริงๆมีมากกว่านี้) เลยตัดเอามาให้ดูเป็นตัวอย่างเฉพาะส่วนที่มีปัญหาเท่านั้น เผื่อต้องเจอเคสลักษณะนี้
ผู้เขียน goragod โพสต์เมื่อ 08 ต.ค. 2566 เปิดดู 2,920 ป้ายกำกับ MySQL
^