Last record in each group

ใน MySQL กรณีเราต้องการหาค่าสูงสุด ตำสุด ในชุดข้อมูลนั้น ๆ จะหาได้จากการใช้ฟังก์ชั่น MIN() ซึ่งจะคืนค่าตำสุดและ MAX() จะคืนค่าสูงสุดในฟิลด์ที่ระบุ เช่น

SELECT MIN(column_name) FROM TABLE_NAME

กรณีที่หาค่าต่ำสุด/สูงสุด แยกตาม กลุ่มใด ๆ ก็ตามเพียงแค่ใช้ GROUP BY ต่อแค่นั้น

จบแล้วเหรอ เด๋วก่อนนนนนน !!! ชีวิตมันไม่ง่ายขนาดนั้น มันจะมีความต้องการอีกกรณีนึงคือ

อยากได้ค่า X ในเรคคอร์ดที่มีค่าสูงสุดในฟิลด์ที่ระบุ (Y) แต่ไม่ใช่ค่า Y นะ แยกตามกลุ่ม Z

ดูตัวอย่างข้อมูลดีกว่า

ค่า X คือ LABRESULT
ค่า Y คือ DATE_SERV
ค่า Z คือ CID

เพราะฉะนั้นผลลัพธ์ที่ได้ควรจะเป็นเช่นนี้ ที่เธอต้องการ

เราสามารถเขียนคำสั่งได้หลายแบบ อาทิ

แบบที่ 1

SELECT * FROM (
    SELECT HOSPCODE,CID,DATE_SERV,LABTEST,LABRESULT FROM demolasteachgroup ORDER BY DATE_SERV DESC
) t1 GROUP BY CID;

แบบที่ 2

SELECT *
FROM demolasteachgroup
WHERE DATE_SERV IN (
    SELECT MAX(DATE_SERV)
    FROM demolasteachgroup
    GROUP BY CID
);

แบบที่ 3

SELECT
    t1.*
FROM demolasteachgroup t1 
    LEFT JOIN demolasteachgroup t2 ON (t1.CID = t2.CID AND t1.DATE_SERV < t2.DATE_SERV)
WHERE t2.CID IS NULL;

ปล 1. จากการทดสอบพบว่า

MySQL Version แบบที่ 1 แบบที่ 2 แบบที่ 3
5.5
5.6
5.7 ×
10.2.2 (MariaDB) ×

√  ผลลัพธ์ถูกต้อง
× ผลลัพธ์ผิด

ปล. 2 ตัดเงื่อนไขเรื่อง Big O นะ มองถึงผลลัพธ์ที่ถูกต้องอย่างเดียว

ปล. 3 มีประเด็นสงสัยจากเพื่อนในวงการ(สุขภาพ) ถามมาที่จับใจความได้ก็ (ถ้ามีเพิ่มเติมก็ discuss กันได้นะครับ ยินดี)
Q : ยกตัวอย่างที่เป็นรูปธรรมเห็นได้หน่อยที่บอกว่าความต้องการอีกกรณีนึง
A : เช่น ต้องการหาค่าน้ำตาลล่าสุดxxของผู้ป่วยหรือผลแลป HbA1C ล่าสุดของผู้ป่วย เป็นต้น กรณีหากได้ค่าที่ผิดพลาดออกมามีผลอย่างมาก เพราะ 2 รายการข้างต้นเป็นหนึ่งในเงื่อนไขที่บอกถึงการควบคุมน้ำตาลได้ดีของผู้ป่วย ซึ่งอยู่ใน KPI ทุก ๆ ปีของคนทำงานด้านสุขภาพ (คนไทยเป็นเบาหวานเยอะมากนะ อันนี้เป็นเรื่องน่ากังวลเหมือนกัน) KPI เกือบผ่านหรือเกือบไม่ผ่านความรุนแรงในการตำหนิต่างกันเยอะ ^_^

Q : ทดสอบกับ MySQL คนละ Version ให้ผลไม่เหมือนกัน อะไร ยังไง
A : ที่ให้ผลลัพธ์ไม่ถูกต้องนั้น เฉพาะแบบที่ 1 นะ ตามปล. 1 คือตัวนี้

SELECT * FROM (
    SELECT HOSPCODE,CID,DATE_SERV,LABTEST,LABRESULT FROM demolasteachgroup ORDER BY DATE_SERV DESC
) t1 GROUP BY CID;

ดูรูปหล่ะกัน เราจัดเรียงข้อมูลตามวันที่เพื่อหาล่าสุด กวาด ๆ ด้วยตาจะเห็นว่ากรอบสีแดงคือเรคคอร์ดที่ถูกต้อง ที่คำสั่ง SQL ควรจะคืนผลลัพธ์มา

แต่ผลลัพธ์จะผิดทันทีถ้าใช้ MySQL Version 5.7 เป็นต้นไป (ปัจจุบันเวอร์ชั่น 5.7.17)

** ไม่ได้หาสาเหตุนะว่าผิดเพราะอะไร ใครจะลองไปทดสอบเวอร์ชั่นอื่น ๆ คำสั่งที่ใช้ทดสอบก็ตามนี้
MySQL 5.5 – http://sqlfiddle.com/#!2/38ef1b/2
MySQL 5.6 – http://sqlfiddle.com/#!9/38ef1b/4
MySQL 5.7 – http://rextester.com/JALDD35756