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

ควบคุมแถวที่เลือกใน GROUP BY ด้วย Subquery ใน MySQL


สถานการณ์

มีข้อมูลนักเรียนหลายคนมีคะแนนเท่ากัน (StudentMarks = 79) ต้องการเลือกแค่ 1 คนต่อ 1 คะแนน (เช่น คนที่ Id น้อยที่สุด)

วิธีแก้: Subquery หา Min Id ต่อ Group

SELECT * FROM DemoTable1572 
WHERE StudentId IN (SELECT MIN(StudentId) FROM DemoTable1572 GROUP BY StudentMarks);

ตัวอย่างการใช้งาน

1. สร้างตาราง

mysql> CREATE TABLE DemoTable1572 (
    -> StudentId INT,
    -> StudentMarks INT,
    -> StudentName VARCHAR(20)
    -> );
Query OK, 0 rows affected (0.56 sec)

2. เพิ่มข้อมูล (Sam และ Bob คะแนนเท่ากัน 79)

mysql> INSERT INTO DemoTable1572 VALUES(1, 79, 'Sam');
Query OK, 1 row affected (0.10 sec)
mysql> INSERT INTO DemoTable1572 VALUES(2, 89, 'Chris');
Query OK, 1 row affected (0.12 sec)
mysql> INSERT INTO DemoTable1572 VALUES(3, 98, 'David');
Query OK, 1 row affected (0.22 sec)
mysql> INSERT INTO DemoTable1572 VALUES(4, 79, 'Bob');
Query OK, 1 row affected (0.10 sec)

4. Query: เลือกคนแรก (Min Id) ของแต่ละคะแนน

mysql> SELECT * FROM DemoTable1572 
    -> WHERE StudentId IN (SELECT MIN(StudentId) FROM DemoTable1572 GROUP BY StudentMarks);

ผลลัพธ์ (Sam Id=1 ถูกเลือก แทน Bob Id=4 สำหรับ Marks=79):

+-----------+--------------+-------------+
| StudentId | StudentMarks | StudentName |
+-----------+--------------+-------------+
| 1         | 79           | Sam         |
| 2         | 89           | Chris       |
| 3         | 98           | David       |
+-----------+--------------+-------------+
3 rows in set (0.10 sec)

วิธีอื่นๆ (MySQL 8.0+ แนะนำ)

1. ROW_NUMBER() - ควบคุมได้ละเอียดที่สุด

SELECT StudentId, StudentMarks, StudentName FROM (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY StudentMarks ORDER BY StudentId) AS rn
    FROM DemoTable1572
) t WHERE rn = 1;

2. JOIN กับ Derived Table

SELECT d.* FROM DemoTable1572 d
JOIN (SELECT StudentMarks, MIN(StudentId) AS MinId FROM DemoTable1572 GROUP BY StudentMarks) m
ON d.StudentMarks = m.StudentMarks AND d.StudentId = m.MinId;

3. GROUP BY ด้วย ANY_VALUE (MySQL 5.7+ ONLY_FULL_GROUP_BY)

SELECT StudentMarks, ANY_VALUE(StudentId), ANY_VALUE(StudentName)
FROM DemoTable1572 GROUP BY StudentMarks;

เปรียบเทียบวิธีเลือกแถวใน Group

วิธี ควบคุมได้ว่าเลือกแถวไหน Performance MySQL Version
ANY_VALUE() ไม่ได้ (Random) เร็ว 5.7+
MIN/MAX Id + JOIN ได้ (Min/Max Id) ดี All
ROW_NUMBER() ได้หมด (Order By ใดๆ) ดีมาก 8.0+
Subquery IN ได้ (Min/Max) ปานกลาง All

กรณีการใช้งานจริง

  • Deduplication: เก็บแถวแรก/ล่าสุดของแต่ละ Group
  • Top-N per Group: สินค้าขายดี Top 3 ต่อ Category
  • Latest Record: สถานะล่าสุดของแต่ละ Device/User
  • First Purchase: ลูกค้าซื้อครั้งแรกของแต่ละ Product