เราก็คงพอเข้าใจกับการทำ Group By มาแล้วนะคะ สำหรับใครที่ยังไม่เข้าใจว่า Group By คิดยังไงขอให้กลับไปทำความเข้าใจกันก่อนค่ะ ที่ มาทำความเข้าใจคำว่า Group By ขึ้นพื้นฐาน
งั้นเรามาต่อกันเลยค่ะ ... ส่วนใหญ่การ Group By เราจะนำความสามารถของมัน มาใช้ร่วมกับ Function ต่างๆ เช่น การนับ(Count), การรวม(Sum), หาค่าสูงสุด(Max), หาค่าต่ำสุด(Min), หาค่ากลาง (Avg) เป็นต้น
โดยที่ทำงานกับ Record ที่ทำ Group By กันได้ เช่นการ Count คือ การนับจำนวน Record หาก เรา Group By ได้ 3 Record ผลที่จะได้จากการใช้ Count คือ 3 ... คนเขียนก็ งง คนอ่านไม่รู้ งง หม้าย
ความเดิมจาก ตาราง test_table
field_a | field_b | field_c |
123 | ทดสอบ | 1 |
123 | ทดสอบ | 2 |
123 | ทดสอบ | 2 |
123 | ทดสอบ | 2 |
123 | ทดสอบ | 3 |
หากเราต้องการนับจำนวน Record ที่มีค่าเหมือนกัน
ใช้คำสั่ง
SELECT field_a, field_b, field_c, COUNT(*) as count_rec
FROM test_table
GROUP BY field_a, field_b, field_c;
ผลลัพธ์ที่ได้ คือ
field_a | field_b | field_c | count_rec |
123 | ทดสอบ | 1 | 1 |
123 | ทดสอบ | 2 | 3 |
123 | ทดสอบ | 3 | 1 |
จะเห็นว่าจำนวน Record จะถูกนับใน Field count_rec โดยจะนับจำนวน Record ตามที่ Group By ได้
ในส่วนนี้จะขอยกตัวอย่างจากการ Query ข้อมูลของ Hospital OS ประกอบเพื่อจะได้เห็นภาพ การทำงานที่ชัดเจนยิ่งขึ้น(หรือเปล่า) มาทำพร้อมๆกันนะคะ
เริ่มจาก
โจทย์ : ต้องการหารายชื่อ และ จำนวนครั้งที่มารับบริการ ใน 1 เดือนของผู้ป่วยที่มารับบริการเกิน 4 ครั้ง
ตีโจทย์ :
- หากต้องการายชื่อของผู้ป่วย สามารถหามาได้จากตาราง t_patient
- หากต้องการหากข้อมูลรับบริการ สามารถหามาได้จากตาราง t_visit
- สรุปว่าข้อมูล 2 ตารางนี้ก็น่าจะเพียงพอกับ โจทย์ ที่ต้องการนะคะ
ลองทำดู ทำเป็นขั้นเป็นตอน ดูผลเป็นขั้นเป็นตอนเพื่อทำความเข้าใจ ดังนี้
ขั้นตอนที่ 1 : นำข้อมูลมาเชื่อมโยงกันเพื่อให้ได้ข้อมูลชื่อและรายการที่รับบริการออกมาก่อน โดยเลือกข้อมูลที่คิดว่าจำเป็นในการนำมาใช้
คำสั่ง
SELECT t_patient.t_patient_id as patient_id --ใช้ as เพื่อตั้งชื่อใหม่
,t_patient.patient_hn as hn
,t_patient.patient_firstname as fname
,t_patient.patient_lastname as lname
,t_patient.patient_birthday as dob
,t_patient.patient_pid as pid
,t_visit.t_visit_id as visit_id --Field นี้มาจากตาราง t_visit เป็นข้อมูลรับบริการ เราเอามานับได้ค่ะ
FROM t_patient INNER JOIN t_visit
ON t_patient.t_patient_id = t_visit.t_patient_id
WHERE t_visit.f_visit_status_id <> '4' --ไม่ยกเลิกการเข้ารับบริการ
and substring(t_visit.visit_financial_discharge_time,1,10) Between '2553-04-01' And '2553-04-30'; --ข้อมูลรับบริการเดือนเมษายน
ผลลัพธ์ คือ ข้อมูลรับบริการในเดือนเมษายน 2553 ตาม Field ที่เรา Select (ลองเอาไป "รัน" ดูนะคะ) ก็ประมาณนี้ค่ะ ขึ้นอยู่กับข้อมูลของเรา
patient_id | hn | fname | lname | dob | pid | visit_id |
206000037798 | 330000001 | ทดสอบชื่อ | ทดสอบนามสกุล | 2534-04-11 | 2554063684111 | |
206000037798 | 330000001 | ทดสอบชื่อ | ทดสอบนามสกุล | 2534-04-11 | 2554063684202 | |
206000037798 | 330000001 | ทดสอบชื่อ | ทดสอบนามสกุล | 2534-04-11 | 2554063684330 | |
206000037798 | 330000001 | ทดสอบชื่อ | ทดสอบนามสกุล | 2534-04-11 | 2554063684890 | |
206000037798 | 330000001 | ทดสอบชื่อ | ทดสอบนามสกุล | 2534-04-11 | 2554063684999 | |
206000037798 | 330000001 | ทดสอบชื่อ | ทดสอบนามสกุล | 2534-04-11 | 2554063685000 | |
... | ... | ... | ... | ... | ... | ... |
ขั้นตอนที่ 2 :จากผลลัพธ์ของครั้งแรกจะเห็นว่าข้อมูลที่ซ้ำๆ กัน จะมาจากตาราง t_patient(ข้อมูลผู้ป่วย) ส่วนข้อมูลที่เป็นการรับบริการจะไม่ซ้ำกัน สังเกตุใน field visit_id เป็นเลขลำดับไม่ซ้ำกัน ซึ่งข้อมูลมาจากตาราง t_visit(ข้อมูลรับบริการ)
ดังนั้น เราจะต้องนับจำนวนรับบริการตามรายละเอียดของผู้ป่วยที่เหมือนกัน(ที่มาจากตาราง t_patient ทั้งหมด) แสดงว่าข้อมูลที่มาจากตาราง t_patient เราจะต้องนำไปทำ Group By เพื่อรวมเป็น Record เดียวกัน และนับจำนวน Record ที่ Group By กันได้(ข้อมูลเหมือนกันในทุก Field ที่กำหนดให้ Group By) ...อ่านแล้ว งง เองอีกแระ มาดูคำสั่งและผลลัพธ์ดีกว่า
คำสั่ง
SELECT t_patient.t_patient_id as patient_id --ใช้ as เพื่อตั้งชื่อใหม่
,t_patient.patient_hn as hn
,t_patient.patient_firstname as fname
,t_patient.patient_lastname as lname
,t_patient.patient_birthday as dob
,t_patient.patient_pid as pid
,count(t_visit.t_visit_id) as count_visit
FROM t_patient INNER JOIN t_visit
ON t_patient.t_patient_id = t_visit.t_patient_id
WHERE t_visit.f_visit_status_id <> '4' --ไม่ยกเลิกการเข้ารับบริการ
and substring(t_visit.visit_financial_discharge_time,1,10) Between '2553-04-01' And '2553-04-30' --ข้อมูลรับบริการเดือนเมษายน
GROUP BY patient_id,hn,fname,lname,dob,pid; --ตรงนี้จะบอกว่าเอาชื่อ Field ที่เราทำ as มาใช้เลยก็ได้ค่ะ แต่ถ้าอยากให้อ่านแล้วเข้าใจเลยก็เอาชื่อ Field มาอ้างได้ค่ะ ได้ผลเหมือนกัน
ผลลัพธ์ คือ ข้อมูลจำนวนครั้งของผู้ป่วยที่มารับบริการในเดือนเมษายน (ลองเอาไป "รัน" ดูนะคะ) เหมือนเดิม ข้อมูลก็อาจจะเป็น ขึ้นอยู่กับข้อมูลของเรา
patient_id | hn | fname | lname | dob | pid | count_visit |
206000037798 | 330000001 | ทดสอบชื่อ | ทดสอบนามสกุล | 2534-04-11 | 6 | |
206000037799 | 330000002 | ทดสอบชื่อ2 | ทดสอบนามสกุล2 | 2524-09-10 | 2 | |
206000037800 | 330000003 | ทดสอบชื่อ3 | ทดสอบนามสกุล3 | 2521-04-19 | 5 |
ขั้นตอนที่ 3 : จากผลลัพธ์ในข้อสองนั้น เราก็ยังไม่ได้ตามที่โจทย์ต้องการย้อนกลับไปอ่านโจทย์ เรายังขาดเรื่องของจำนวนครั้งที่มารับบริการมากกว่า 4 ครั้ง ดังนั้นเราก็มาทำตามที่ควรจะได้กันดีกว่า
การที่เราจะกำหนดเงื่อนไขจากผลลัพธ์ของ Function Count (หรือ Function อะไรก็แล้วแต่ที่ใช้ร่วมกับ Group By) เราจะต้องรู้จักคำนี้ Having -> นั่นคือตัวช่วยของเราค่ะ
Having
- จำเป็นต้องใช้ร่วมกับการทำ Group By ค่ะ จู่ๆ จะมา Having ไม่ได้ (หรือได้ งง)
- จะต้องอยู่หลัง Group By
- จะต้องยกคำสั่งมาเป็นเงื่อนไข หมายถึง ยกการทำงานของ Function มาเป็นเงื่อนไข เช่น count(t_visit.t_visit_id) > 4
งั้นเรามาดูคำสั่งแบบเต็มๆ กันเลยนะคะ
คำสั่ง
SELECT t_patient.t_patient_id as patient_id --ใช้ as เพื่อตั้งชื่อใหม่
,t_patient.patient_hn as hn
,t_patient.patient_firstname as fname
,t_patient.patient_lastname as lname
,t_patient.patient_birthday as dob
,t_patient.patient_pid as pid
,count(t_visit.t_visit_id) as count_visit
FROM t_patient INNER JOIN t_visit
ON t_patient.t_patient_id = t_visit.t_patient_id
WHERE t_visit.f_visit_status_id <> '4' --ไม่ยกเลิกการเข้ารับบริการ
and substring(t_visit.visit_financial_discharge_time,1,10) Between '2553-04-01' And '2553-04-30' --ข้อมูลรับบริการเดือนเมษายน
GROUP BY patient_id,hn,fname,lname,dob,pid
HAVING count(t_visit.t_visit_id) > 4; --มารับบริการมากกว่า 4 ครั้งในช่วงเดือนที่กำหนด ในที่นี้คือ เมษายน
ผลลัพธ์ คือ ข้อมูลจำนวนครั้งของผู้ป่วยที่มารับบริการในเดือนเมษายนที่มารับบริการมากกว่า 4 (ลองเอาไป "รัน" ดูนะคะ) เหมือนเดิม ข้อมูลก็อาจจะเป็น ขึ้นอยู่กับข้อมูลของเรา
patient_id | hn | fname | lname | dob | pid | count_visit |
206000037798 | 330000001 | ทดสอบชื่อ | ทดสอบนามสกุล | 2534-04-11 | 6 | |
206000037800 | 330000003 | ทดสอบชื่อ3 | ทดสอบนามสกุล3 | 2521-04-19 | 5 |
เท่านี้เราก็ได้ข้อมูลตามโจทย์แล้วค่ะ ... วันนี้ยากจัง ก็ยากตรงที่เขียนยังไงให้คนที่มาอ่านเข้าใจ มันก็เป็นอะไรที่ต้องคิดหนักนะ แต่จะพยายามสู้ๆค่ะ
งั้น ขอแถมอีกนิดนะคะ เผื่อมีประโยชน์ ถ้าเราต้องการเรียงลำดับจากมากไปหาน้องของโทจทย์นี้ก็เพิ่ม Order By ไปท้ายสุดเลยค่ะ
คำสั่ง
SELECT t_patient.t_patient_id as patient_id --ใช้ as เพื่อตั้งชื่อใหม่
,t_patient.patient_hn as hn
,t_patient.patient_firstname as fname
,t_patient.patient_lastname as lname
,t_patient.patient_birthday as dob
,t_patient.patient_pid as pid
,count(t_visit.t_visit_id) as count_visit
FROM t_patient INNER JOIN t_visit
ON t_patient.t_patient_id = t_visit.t_patient_id
WHERE t_visit.f_visit_status_id <> '4' --ไม่ยกเลิกการเข้ารับบริการ
and substring(t_visit.visit_financial_discharge_time,1,10) Between '2553-04-01' And '2553-04-30' --ข้อมูลรับบริการเดือนเมษายน
GROUP BY patient_id,hn,fname,lname,dob,pid
HAVING count(t_visit.t_visit_id) > 4 --มารับบริการมากกว่า 4 ครั้งในช่วงเดือนที่กำหนด ในที่นี้คือ เมษายน
ORDER BY count_visit DESC;
เพิ่มเติมสำหรับ Order By ที่ควรรู้
- Order By ต้องอยู่ท้ายสุดของการเขียน Query
- หากเราไม่เติมคำสั่งบังคับหลัง Order By จะถือว่าเรียงจากน้อยไปหามาก โดยปริยาย เช่น ORDER BY field_a
- หากต้องการกำกับให้เรียงจากมากไปหาน้อย ให้ใส่ DESC ตามหลัง Field ที่กำหนดให้เรียง เช่น ORDER BY field_a DESC
- หากต้องการกำกับให้เรียงจากน้อยไปหามาก ให้ใส่ ASC ตามหลัง Field ที่กำหนดให้เรียง เช่น ORDER BY field_a ASC
- สามารถเรียงลำดับมากกว่า 1 Field ได้ โดยจะทำงานจากซ้าย(ติดกับ Order By) โดยมี comma คั้นระหว่าง Field และมีตัวกำกับหลัง Field แตกต่างกับได้ (DESC,ASC) เช่น ORDER BY field_a DESC, field_b ASC, field_c DESC
อันนี้ก็ต้องลองถึงจะรู้นะคะ ... สำหรับวันนี้คงพอก่อนเอาไว้มาต่อเรื่อง Group By , Distinct กันอีกนะคะ ...to be continue