สถานการณ์
มีตารางเก็บคู่ค่า (Name1, Name2) ต้องการนับจำนวนครั้งที่คู่ค่านี้ปรากฏ โดยถือว่า (A, B) และ (B, A) เป็นคู่เดียวกัน (Order-independent)
วิธีแก้: ใช้ GREATEST() และ LEAST()
GREATEST(a, b)คืนค่าที่มากกว่าLEAST(a, b)คืนค่าที่น้อยกว่า- จับคู่ (A, B) และ (B, A) ให้กลายเป็นคู่เดียวกัน (LEAST, GREATEST)
ตัวอย่างการใช้งาน
1. สร้างตาราง
mysql> CREATE TABLE DemoTable (
-> Name1 VARCHAR(20),
-> Name2 VARCHAR(20)
-> );
Query OK, 0 rows affected (0.61 sec)
2. เพิ่มข้อมูล
mysql> INSERT INTO DemoTable VALUES('John', 'Adam');
Query OK, 1 row affected (0.13 sec)
mysql> INSERT INTO DemoTable VALUES('Chris', 'David');
Query OK, 1 row affected (0.17 sec)
mysql> INSERT INTO DemoTable VALUES('Robert', 'Mike');
Query OK, 1 row affected (0.18 sec)
mysql> INSERT INTO DemoTable VALUES('David', 'Chris'); -- สลับลำดับ
Query OK, 1 row affected (0.11 sec)
mysql> INSERT INTO DemoTable VALUES('Mike', 'Robert'); -- สลับลำดับ
Query OK, 1 row affected (0.18 sec)
3. แสดงข้อมูลทั้งหมด
mysql> SELECT * FROM DemoTable;
ผลลัพธ์:
+--------+--------+ | Name1 | Name2 | +--------+--------+ | John | Adam | | Chris | David | | Robert | Mike | | David | Chris | | Mike | Robert | +--------+--------+ 5 rows in set (0.00 sec)
4. นับจำนวนคู่ (ถือว่า Chris-David = David-Chris)
mysql> SELECT GREATEST(Name1, Name2), LEAST(Name1, Name2), COUNT(*) AS Occurrences
-> FROM DemoTable
-> GROUP BY GREATEST(Name1, Name2), LEAST(Name1, Name2);
ผลลัพธ์:
+-----------------------+--------------------+-------------+ | greatest(Name1,Name2) | least(Name1,Name2) | Occurrences | +-----------------------+--------------------+-------------+ | John | Adam | 1 | | David | Chris | 2 | | Robert | Mike | 2 | +-----------------------+--------------------+-------------+ 3 rows in set (0.02 sec)
การประยุกต์ใช้
สำหรับตัวเลข (หาคู่ที่ผลรวม/ผลคูณเท่ากัน)
SELECT GREATEST(Val1, Val2), LEAST(Val1, Val2), COUNT(*) FROM Numbers GROUP BY GREATEST(Val1, Val2), LEAST(Val1, Val2);
สำหรับวันที่ (หาช่วงเวลาที่ทับซ้อน)
SELECT GREATEST(Start1, Start2), LEAST(End1, End2), COUNT(*) FROM Events GROUP BY GREATEST(Start1, Start2), LEAST(End1, End2);
ข้อควรระวัง
- GREATEST/LEAST คืนค่า NULL หากมี Argument ใดเป็น NULL
- ใช้ COALESCE() จัดการ NULL ก่อน Group By หากจำเป็น
- สำหรับ String ใช้ Collation ในการเปรียบเทียบ
- Performance: ไม่สามารถใช้ Index บน Expression ได้โดยตรง (MySQL 8.0+ ใช้ Functional Index ได้)
กรณีการใช้งานจริง
- Social Network: นับจำนวน Interaction ระหว่าง User A และ B (ไม่สนทิศทาง)
- E-commerce: นับคู่สินค้าที่มักซื้อร่วมกัน (Product A + Product B)
- Logistics: นับเส้นทางไป-กลับ (Origin-Destination vs Destination-Origin)
- Fraud Detection: หาคู่บัญชีที่โอนเงินไปมาซ้ำกัน