count คือ Function สำหรับนับจำนวน record หากเรา Query ข้อมูลแล้วต้องการทราบว่า จำนวนที่เรา Query ได้มีกี่ record ก็สามารถใช้ Function นี้ได้ค่ะ แต่การใช้ count มันพิเศษกว่า นั้นค่ะ เรามาดูต่อเลยนะคะ
ควรรู้
- สามารถนับจำนวน Record ข้อมูลทั้งหมดได้
- สามารถนับจำนวน Record ข้อมูลเฉพาะที่ไม่ซ้ำกันได้
- ไม่สามารถนับค่าที่เป็น NULL ได้
- หากการ Select ข้อมูล มี filed อื่นๆ ที่ไม่ได้ใช้ count หรือ Function ประเภทเดียวกับ count ให้นำมาทำ Group By เสมอ
รูปแบบการใช้คำสั่ง
แบบที่ 1
count([ชื่อ filed]) ใช้เมื่อ ต้องการนับจำนวน record โดยที่จะต้องมีข้อมูลของ Filed ที่ใช้นับ (หากเป็น NULL จะไม่สนใจ)
แบบที่ 2
count(distinct [ชื่อ Field]) ใช้เมื่อ ต้องการนับจำนวน record โดยที่ Filed ที่ใช้นับจะต้องไม่ซ้ำกัน (โดยที่ซ้ำกันนับแต่ 1)
แนะนำตารางที่ใช้อ้างถึง
t_patient คือ ตารางเก็บข้อมูลทั่วไปของผู้ป่วยที่เคยมารับบริการ 1 คนจะมี 1 record
t_visit คือ ตารางเก็บข้อมูลรับบริการของผู้ป่วยที่มารับบริการแต่ละครั้ง 1 ครั้งจะมี 1 Record ผู้ป่วยสามารถรับบริการได้มากกว่า 1 ครั้ง
ตัวอย่างการใช้คำสั่ง
- นับจำนวนข้อมูล visit ทั้งหมด
SELECT count(t_visit.t_visit_id) FROM t_visit WHERE t_visit.f_visit_status_id <> '4';
- นับจำนวน visit ของแต่ละวัน
SELECT substring(t_visit.visit_financial_discharge_time,1,10) as dateserv ,count(t_visit.t_visit_id) as count_visit
FROM t_visit
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 dateserv;
- นับจำนวนผู้ป่วยมารับบริการในเดือน ว่ามีกี่คน (ใน 1 เดือนอาจจะรับบริการมากกว่า 1 ครั้ง ดังนั้น Record ที่ได้จากการ Select อาจจะมากกว่าจำนวนคน)
SELECT count(distinct t_visit.t_patient_id) --ใช้ distinct เพื่อนับเป็นรายบุคคล
FROM t_visit
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';
- นับจำนวนคน จำนวนครั้ง ของการมารับบริการใน Query เดียวกัน
SELECT count(distinct t_visit.t_patient_id) as count_pat --ใช้ distinct เพื่อนับเป็นรายบุคคล
,count(t_visit.t_visit_id) as count_visit --นับครั้งของการรับบริการ
FROM t_visit
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';
มาถึงจุดเปลี่ยน ... สิ่งที่กล่าวมาข้างต้นเรื่องรูปแบบการใช้งาน นั่นคือ "ทฤษฎี" แต่เวลา "ปฏิบัติ" มันย่อมซับซ้อนกว่าเป็นธรรมดาค่ะ เรามาดูตัวอย่างที่ซับซ้อนขึ้นอีกนิดนะคะ
SELECT
count(case when t_patient.f_sex_id = '1'
then t_patient.t_patient_id
else NULL
end) as man --นับเฉพาะเพศชายเท่านั้น
,count(case when t_patient.f_sex_id = '2'
then t_patient.t_patient_id
else NULL
end) as women --นับเฉพาะเพศหญิงเท่านั้น
FROM t_patient;
จากตัวอย่างข้างต้นจะเห็นว่าเราไม่สามารถนับได้เลยนะคะ เราต้องมีเงื่อนไขก่อนที่จะนับซึ่งตรงนี้เราทราบคุณสมบัติของการ Count ว่าจะไม่นับค่า NULL ดังนั้น เราก็ทำให้เกิดค่า NULL ใน Record ที่เราไม่ต้องการนับโดยการขอความช่วยเหลือจาก CASE WHEN เพื่อให้สามารถนับข้อมูลได้ตรงตามจริง
งั้นมาทำความรู้จักกับ CASE WHEN คร่าวๆกันก่อนะคะ สำหรับคำสั่ง CASE WHEN จะช่วยให้เราสร้างเงื่อนไขกับข้อมูลได้ และได้มากกว่า 1 เงื่อนไข ค่ะ
รูปแบบ
CASE WHEN [ผลลัพธ์ของเงื่อนไข จริง หรือ เท็จ]
THEN [ให้แสดงหรือทำอะไรถ้า when เป็นจริง]
WHEN [ผลลัพธ์ของเงื่อนไข จริง หรือ เท็จ]
THEN [ให้แสดงหรือทำอะไรถ้า when เป็นจริง]
--when คู่กับ then เสมอ แต่จะมีกี่เงื่อนไขก็ได้ มี when then กี่ตู่ก็ได้
ELSE [ให้แสดงหรือทำอะไรถ้า when ทั้งหมดเป็นเท็จ] --จะมีหรือไม่มี else ก็ได้
END --จบ case ด้วย end เสมอ
การทำงาน
จะเริ่มจาก WHEN จะตรวจสอบเงื่อนไขเพื่อบอกว่าเราต้องการข้อมูลอะไร ซึ่งหาก WHEN ได้ข้อสรุปจากเงื่อนไขว่าเป็นจริง ก็จะทำงานตามที่ THEN สั่งการแล้วถ้าเป็น เท็จ ก็จะทำให้ ELSE แทน
งั้นเรามาทำความเข้าใจกับคำสั่งข้างต้นกันนะคะที่เอา CASE WHEN เข้ามาเป็นเงื่อนไขการนับ
โจทย์ : ต้องการนับจำนวนผู้ป่วยเพศชาย และจำนวนผู้ป่วยเพศหญิง แยกกัน 2 Field
จากโจทย์ เราก็ต้องกลับไปดูที่ข้อมูลของเราก่อนนะคะว่าจัดเก็บข้อมูลอย่างไร
t_patient คือ ตารางเก็บข้อมูลทั่วไปของผู้ป่วยที่เคยมารับบริการ 1 คนจะมี 1 record
t_patient.f_sex_id คือ Field ที่เก็บข้อมูลรหัสเพศ โดยที่ 1=ชาย, 2=หญิง
จากข้อมูลที่เรามีลองทำทีละขั้นตอนให้เห็นที่มาที่ไปก่อนจะ Count กันนะคะ
ขั้นตอนที่ 1 : เราลอง Query ข้อมูลจากตารางนี้ดูก่อนนะคะ
คำสั่ง
SELECT t_patient.f_sex_id
FROM t_patient;
ผลลัพธ์ที่ได้
f_sex_id |
1 |
1 |
2 |
1 |
2 |
ขั้นตอนที่ 2 : เราลองนำ CASE WHEN เข้ามาเป็นเงื่อนไขเพื่อแยกข้อมูลของเราค่ะ
คำสั่ง
SELECT
t_patient.f_sex_id
,case when t_patient.f_sex_id = '1'
then t_patient.t_patient_id
else NULL
end as man --เพศชายเท่านั้น
,case when t_patient.f_sex_id = '2'
then t_patient.t_patient_id
else NULL
end as women --เพศหญิงเท่านั้น
FROM t_patient;
ผลลัพธ์ที่ได้
f_sex_id | man | women |
1 | 2063096106908 | NULL |
1 | 2063096106910 | NULL |
2 | NULL | 2063096104000 |
1 | 2063096108000 | NULL |
2 | NULL | 2063096106911 |
ขั้นตอนที่ 3 : จากข้อมูลเราจะเห็นว่าจะมีค่า NULL เกิดขึ้นในกรณีที่เงื่อนไขเป็นเท็จ งั้นเราลองนับจำนวนกันดูนะคะว่าจะได้ตามที่เราต้องการหรือไม่ ชาย = 3 คน และ หญิง = 2 คน (เราต้องเอา Field t_patient.f_sex_id ออกนะคะ เพราะไม่เช่นนั้นเราจะต้องนำมาทำ Group By ซึ่ง Output ไม่ตรงตามที่เราต้องการ เพราะตอนนี้เราต้องการให้แยก Field กันน่ะ)
คำสั่ง
SELECT
count(case when t_patient.f_sex_id = '1'
then t_patient.t_patient_id
else NULL
end) as man --นับเฉพาะเพศชายเท่านั้น
,count(case when t_patient.f_sex_id = '2'
then t_patient.t_patient_id
else NULL
end) as women --นับเฉพาะเพศหญิงเท่านั้น
FROM t_patient;
ผลลัพธ์ที่ได้
man | women |
3 | 2 |
ในทางกลับกันบางทีมันก็ง่ายกว่าที่เราทำอยู่หากโจทย์ไม่ได้ Fixed ว่าต้องการข้อมูลแบบไหนแต่ต้องการทราบว่าจำนวนเท่าไหร่ ซึ่งบางข้อมูลเราอาจจะไม่จำเป็นต้องใช้ CASE WHEN เขา้ามาช่วยเลย เพราะในข้อมูลของมันเองสามารถทำ GROUP BY และได้ผลลัพธ์เช่นเดียวกันได้ ดูตัวอย่างนับตามเพศนะคะเราก็ใช้รหัสเพศมาเป็นตัวแปรของการนับเลยค่ะ (นับตามรหัสเพศ)
คำสั่ง
SELECT
t_patient.f_sex_id
,count(t_patient.t_patient_id) as count_by_gender
FROM t_patient
GROUB BY t_patient.f_sex_id;
ผลลัพธ์ที่ได้
f_sex_id | count_by_gender |
1 | 3 |
2 | 2 |
คำถามต่อไป แล้วทำไมเราต้อง COUNT(distinct ...) ด้วยล่ะ ?
อันนี้ก็เนื่องจากเราไม่รู้ว่าข้อมูลที่ JOIN มาเนี๊ยะ มีข้อมูลอะไรบ้างยังไงล่ะ หากเอา visit ไป join กับ ICD10 หรือ Order ก็อาจจะพบ record มากกว่า 1 เป็นแน่แต่โจทย์กลับบอกว่าให้นับจำนวนคน...เมื่อ Record เบิ้ล ก็จะทำให้การนับมีความผิดพลาดได้ค่ะ
งั้นเราลองมาทำโจทย์นี้ดูค่ะ แล้วจะรู้ว่าเราจำเป็นต้องใช้ Count เมื่อเรานับ "คน" "ครั้ง" ก็ด้วย
โจทย์ : ต้องการทราบจำนวนคน ครั้ง ของผู้ที่มารัับบริการ ในแต่ละเดือนของปีงบประมาณ 2553
ขั้นตอนที่ 1 : ออกแบบผลลัพธ์ที่โจทย์ต้องการ ได้ดังนี้
month_visit | count_pat | count_visit |
ตุลาคม 2552 | 2000 | 4500 |
พฤศจิกายน 2552 | 2340 | 5600 |
... | ... | ... |
... | ... | ... |
กันยายน 2553 | 2200 | 4999 |
ขั้นตอนที่ 2 : ตรวจสอบที่มาของข้อมูลที่จะสามารทำให้เกิดผลลัพธ์ได้ ก็ต้องดูโทย์ว่าต้องการอะไรและจาก Output เราออกแบบไว้อย่างไร ก็คงไม่พ้นตาราง t_visit แหละค่ะ เป็นตารางข้อมูลรับบริการน่ะ จะมีทั้งวันที่รับบริการ และ รหัสผู้ป่วย(t_visit.t_patient_id)ที่นับ "คน" ได้ และ รหัสการเข้ารับบริการ(t_visit.t_visit_id)ที่นับ "ครั้ง" ได้
ขั้นตอนที่ 3 : ลอง Query ข้อมูลรับบริการของปังบประมาณ 2553 ดูนะคะ
คำสั่ง
SELECT
t_visit.visit_financial_discharge_time --วันที่จำหน่ายทางการเงิน
,t_visit.t_patient_id --รหัสผู้ป่วย
,t_visit.t_visit_id --รหัสการเข้ารับบริการ
FROM t_visitWHERE t_visit.f_visit_status_id <> '4' --ข้อมูลการรับบริการไม่ถูกยกเลิก
And substring(t_visit.visit_financial_discharge_time,1,10) Between '2552-10-01' And '2553-09-30';
ผลลัพธ์ที่ได้
visit_financial_discharge_time | t_patient_id | t_visit_id |
2553-01-19,16:49:38 | 2060001841876 | 2555003061312 |
2553-01-20,17:38:00 | 2060001841876 | 2552391103429 |
2553-01-22,10:30:53 | 2060005621621 | 2554092156596 |
2553-01-30,13:40:55 | 2060011666878 | 2554250372159 |
2553-01-30,15:30:59 | 2060038042639 | 2553850962391 |
ขั้นตอนที่ 4 : จากผลลัพธ์ของขั้นตอนที่ 3 นั้นเห็นว่าข้อมูลของรหัสผู้ป่วย(t_patient_id) เป็นข้อมูลที่ซ้ำกันได้ในเดือนเดียวกัน เนื่องจาก 1 คนสามารถเข้ารับบริการได้มากกว่า 1 ครั้งนั้นเองและจากความรู้ของการ Count หากเรานับเลยจะทำให้จำนวน "คน" และ "ครั้ง" เพราะต้างก็มีข้อมูล ดังนั้น เราใช้โอกาสนี้นับแบบ Distinct คือ นับค่าที่ซ้ำกันแค่ 1 เท่านั้น และอีกส่วนหนึ่งที่เราจะสนใจ คือวันที่ค่ะ จากโจทย์เราต้องการนับเป็นเดือน จากข้อมูลของวันที่นั้นเราก็ต้องใช้วิชากันหน่อยค่ะ
คำสั่ง
SELECT
(case when substring(t_visit.visit_financial_discharge_time,6,2) = '10' --ตัดวันที่ตั้งแต่ตำแหน่งที่ 6 มา 2 ตัวจะได้เลขเดือน 2 หลัก และเทียบว่าเป็น 10 หรือไม่
then 'ตุลาคม' --หาก when เป็นจริง คือ เดือน 10 จะให้แสดงเป็นข้อความว่า "ตุลาคม"
when substring(t_visit.visit_financial_discharge_time,6,2) = '11'
then 'พฤศจิกายน'
when substring(t_visit.visit_financial_discharge_time,6,2) = '12'
then 'ธันวาคม'
when substring(t_visit.visit_financial_discharge_time,6,2) = '01'
then 'มกราคม'
when substring(t_visit.visit_financial_discharge_time,6,2) = '02'
then 'กุมภาพันธ์'
when substring(t_visit.visit_financial_discharge_time,6,2) = '03'
then 'มีนาคม'
when substring(t_visit.visit_financial_discharge_time,6,2) = '04'
then 'เมษายน'
when substring(t_visit.visit_financial_discharge_time,6,2) = '05'
then 'พฤษภาคม'
when substring(t_visit.visit_financial_discharge_time,6,2) = '06'
then 'มิถุนายน'
when substring(t_visit.visit_financial_discharge_time,6,2) = '07'
then 'กรกฎาคม'
when substring(t_visit.visit_financial_discharge_time,6,2) = '08'
then 'สิงหาคม'
when substring(t_visit.visit_financial_discharge_time,6,2) = '09'
then 'กันยายน'
end) || ' ' || substring(t_visit.visit_financial_discharge_time,1,4) as month_visit --เมื่อได้เดือนที่เป็นข้อความแล้วให้นำมาต่อกับ ช่องว่าง และต่อด้วยเลขปีซึ่งเราตัดวันที่ในตำแหน่งที่ 1 มา 4 ตัวจะได้เลขปี พ.ศ.
,count(distinct t_visit.t_patient_id) as count_pat --นับคน
,count(t_visit.t_visit_id) as count_visit --นับครั้ง
FROM t_visitWHERE t_visit.f_visit_status_id <> '4' --ข้อมูลการรับบริการไม่ถูกยกเลิก
And substring(t_visit.visit_financial_discharge_time,1,10) Between '2552-10-01' And '2553-09-30'
GROUP BY month_visit
,substring(t_visit.visit_financial_discharge_time,1,7) --หากทำ Order By ก็ต้องนำมา Group By ด้วยนะคะ ถึงแม้จะไม่ได้แสดงก็ตาม
ORDER BY substring(t_visit.visit_financial_discharge_time,1,7); --ใช้วิชามารนิดหน่อยเพื่อให้ได้ข้อมูลที่สวยงาม
ผลลัพธ์ที่ได้ (ลองนำไป Query ดูนะคะ แต่ละที่จะได้ข้อมูลไม่เท่ากันค่ะ)
month_visit | count_pat | count_visit |
ตุลาคม 2552 | 2000 | 4500 |
พฤศจิกายน 2552 | 2340 | 5600 |
... | ... | ... |
... | ... | ... |
กันยายน 2553 | 2200 | 4999 |
* เท่านี้เราก็ได้ตามโจทย์ที่เราต้องการแล้วค่ะ วันนี้พอก่อนแล้วกันค่ะเด็กไม่ยอมนอนกล่อมเด็กก่อนค่ะ "ไว้ติดตามกันตอนต่อไปนะคะ"
ไม่มีความคิดเห็น:
แสดงความคิดเห็น