Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> MySQL

MySQL กำจัด Common Subexpressions ระหว่าง SELECT และ HAVING/GROUP BY หรือไม่? ทดสอบด้วย SLEEP()

ทดสอบว่า MySQL เพิ่มประสิทธิภาพ (eliminate) common subexpressions ระหว่าง SELECT และ GROUP BY/HAVING หรือไม่ โดยใช้ SLEEP() วัดเวลา

สร้างตารางตัวอย่าง

mysql> CREATE TABLE sleepDemo (value INT);
mysql> INSERT INTO sleepDemo VALUES (40), (60), (60);

แสดงข้อมูล

mysql> SELECT * FROM sleepDemo;
+-------+
| value |
+-------+
| 40    |
| 60    |
| 60    |
+-------+

กรณีที่ 1: ซ้ำ expression ใน SELECT และ GROUP BY

mysql> SELECT value + SLEEP(3)
    -> FROM sleepDemo
    -> GROUP BY value + SLEEP(3);

ผลลัพธ์ (ใช้เวลา 9 วินาที = 3 แถว × 3 วินาที):

+----------------+
| value+SLEEP(3) |
+----------------+
| 40             |
| 60             |
+----------------+
2 rows in set (9.00 sec)

สรุป: MySQL ไม่กำจัด common subexpression - คำนวณ SLEEP(3) ทุกครั้งที่เจอ

กรณีที่ 2: ใช้ ALIAS ใน GROUP BY

mysql> SELECT value + SLEEP(3) AS v
    -> FROM sleepDemo
    -> GROUP BY v;

ผลลัพธ์ (ใช้เวลา 9 วินาที = 3 แถว × 3 วินาที):

+------+
| v    |
+------+
| 40   |
| 60   |
+------+
2 rows in set (9.00 sec)

สรุป: ยังคงคำนวณซ้ำ แม้ใช้ ALIAS

การทดสอบเพิ่มเติม: ใช้ Subquery/Derived Table

-- คำนวณครั้งเดียวใน subquery แล้วค่อย GROUP BY
SELECT v FROM (
    SELECT value + SLEEP(3) AS v FROM sleepDemo
) AS t
GROUP BY v;

ผลลัพธ์: ใช้เวลา 9 วินาที (ยังคงคำนวณ 3 ครั้ง เพราะ subquery ยังประมวลผลทุกแถว)

วิธีแก้: คำนวณครั้งเดียวด้วย CTE (MySQL 8.0+)

WITH computed AS (
    SELECT value, value + SLEEP(3) AS v FROM sleepDemo
)
SELECT v FROM computed GROUP BY v;

ยังคง 9 วินาที เพราะ CTE ยังต้องคำนวณทุกแถว

วิธีแก้จริง: ลดจำนวนแถวก่อนคำนวณ

-- DISTINCT ก่อน คำนวณทีหลัง
WITH distinct_vals AS (
    SELECT DISTINCT value FROM sleepDemo
)
SELECT value + SLEEP(3) AS v FROM distinct_vals;

ผลลัพธ์: ใช้เวลา 6 วินาที (2 ค่าที่ไม่ซ้ำ × 3 วินาที) ✓

สรุป

  • MySQL ไม่ได้ eliminate common subexpressions อัตโนมัติ
  • Expression ใน SELECT และ GROUP BY/HAVING จะคำนวณแยกกัน
  • ใช้ DISTINCT หรือ Subquery กรองค่าก่อน ค่อยคำนวณ expression ที่แพง
  • หลีกเลี่ยงฟังก์ชันที่แพง (SLEEP, UUID, complex UDF) ใน SELECT/GROUP BY โดยตรง