วันอังคาร, กันยายน 13, 2548

เรื่องของ SQL ตอนที่ 4

ว่าจะมาเฉลยสิ่งที่เคยถามไว้ว่า ถ้าอยากจะทราบว่าในวันที่ 15 กรกฏาคม 2548 มีผู้ป่วยนอกมารับบริการแยกเพศ ชาย,หญิง,รวม จำแนกตามสิทธิบัตร โดยแสดงสิทธิบัตรออกมาจนครบ ตั้งแต่สุดสัปดาห์ที่ผ่านมาแล้ว แต่ก็มีเหตุให้ต้องเลื่อนเนื่องจากวันเสาร์ - อาทิตย์ที่ผ่านมามีภารกิจที่ต้องทำ (อย่างยิ่งยวด) โดยในวันเสาร์ที่โรงเรียนของลูกสาวมีการจัดแข่งขันกีฬาสี ก็ต้องพาเด็กๆ ไปร่วมงานตั้งแต่เช้า กว่าจะกลับมาถึงบ้านได้ก็บ่ายแก่ๆ อากาศร้อนอบอ้าวมากทั้งๆ ที่เป็นฤดูฝน (แล้วจัดทำไมล่ะ ก็แปลกดี ปกติแล้วกีฬาสีเนี่ยเค้าจัดกันตอนเทอมสองกันทั้งน้าน นี่ยังไม่ปิดเทอมแรกเลย ) เหนื่อยทั้งผู้ปกครองทั้งนักเรียน ทั้งครู กลับมาก็นอนพักกันทั้งบ้าน นอนพักกันจนเย็น เย็นมากจนขี้เกียจไปซะ วันอาทิตย์ก็ต้องไปประชุมเกี่ยวกับพฤติกรรมการบริการ ที่ทางทีมงานพัฒนาทรัพยากรมนุษย์เค้าจัดขึ้นอีก พอกลับมาก็ไฟดับยาวไปถึงตอนดึก ช่วงนี้ไฟดับบ่อยมากทั้งๆ ที่บ้านก็อยู่ใกล้สำนักงานไฟฟ้านะ (เกียวกันด้วยเร๊อะ) เลยเถิดมาถึงวันนี้จนได้

เอาล่ะ เฉลยเลยแล้วกัน ตอนแรกเราก็นับเฉพาะเพศชายก่อนโดย (สีแดงคือสิ่งที่เพิ่มเข้าไป) ตั้งชื่อไว้ก่อนว่า temp1

SELECT
b_contract_plans.contract_plans_number AS contract_plans_number
,COUNT(t_visit.t_visit_id) AS total
FROM
b_contract_plans,t_visit,t_visit_payment,t_patient
WHERE
t_visit.t_visit_id=t_visit_payment.t_visit_id
AND t_visit.visit_hn = t_patient.patient_hn
AND t_visit_payment.b_contract_plans_id=b_contract_plans.b_contract_plans_id
AND t_visit.f_visit_type_id='0'
AND t_visit.f_visit_status_id='3'
AND t_patient.f_sex_id='1'
AND (SUBSTRING(t_visit.visit_financial_discharge_time,0,11) BETWEEN '2548-07-15' and '2548-07-15')
GROUP BY
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description

เมื่อได้เพศชายแล้วก็นับเพศหญิงต่อโดยใช้ SQL ชุดเดียวกันเพียงแต่เปลี่ยน t_patient.f_sex_id จาก 1 เป็น 2 ตั้งชื่อไว้ว่า temp2

SELECT
b_contract_plans.contract_plans_number AS contract_plans_number
,COUNT(t_visit.t_visit_id) AS total
FROM
b_contract_plans,t_visit,t_visit_payment,t_patient
WHERE
t_visit.t_visit_id=t_visit_payment.t_visit_id
AND t_visit.visit_hn = t_patient.patient_hn
AND t_visit_payment.b_contract_plans_id=b_contract_plans.b_contract_plans_id
AND t_visit.f_visit_type_id='0'
AND t_visit.f_visit_status_id='3'
AND t_patient.f_sex_id='2'
AND (SUBSTRING(t_visit.visit_financial_discharge_time,0,11) BETWEEN '2548-07-15' and '2548-07-15')
GROUP BY
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description

แล้วก็นับทั้งหมด (รวมชายและหญิง) ตั้งชื่อเป็น temp3 ดังนี้

SELECT
b_contract_plans.contract_plans_number AS contract_plans_number
,COUNT(t_visit.t_visit_id) AS total
FROM
b_contract_plans,t_visit,t_visit_payment
WHERE
t_visit.t_visit_id=t_visit_payment.t_visit_id
AND t_visit_payment.b_contract_plans_id=b_contract_plans.b_contract_plans_id
AND t_visit.f_visit_type_id='0'
AND t_visit.f_visit_status_id='3'
AND (SUBSTRING(t_visit.visit_financial_discharge_time,0,11) BETWEEN '2548-07-15' and '2548-07-15')
GROUP BY
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description

ทีนี้ก็นับสิทธิบัตรทั้งหมดออกมา ตั้งชื่อว่า temp4

SELECT
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description
FROM
b_contract_plans

เริ่มการ JOIN โดยการใช้ temp4 เป็นตัวตั้ง แล้วเอา temp1,temp2,temp3 มา JOIN ตามที่ร่างไว้อย่างนี้

SELECT
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description
,temp1.total AS male
,temp2.total AS female
,temp3.total AS total_all

FROM
(b_contract_plans
LEFT JOIN
(
SQL ชุดที่หนึ่ง
) AS temp1 ON b_contract_plans.contract_plans_number=temp1.contract_plans_number
LEFT JOIN
(
SQL ชุดที่สอง
) AS temp2 ON b_contract_plans.contract_plans_number=temp2.contract_plans_number
LEFT JOIN
(
SQL ชุดที่สาม
) AS temp3 ON b_contract_plans.contract_plans_number=temp3.contract_plans_number
)
GROUP BY
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description
,male
,female
,total_all


เมื่อแทนค่า temp1 , temp2 , temp3 ด้วยชุดคำสั่งแล้วก็จะได้เป็นดังนี้

SELECT
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description
,temp1.total AS male
,temp2.total AS female
,temp3.total AS total_all
FROM
(b_contract_plans LEFT JOIN
(
SELECT
b_contract_plans.contract_plans_number AS contract_plans_number
,COUNT(t_visit.t_visit_id) AS total
FROM
b_contract_plans,t_visit,t_visit_payment,t_patient
WHERE
t_visit.t_visit_id=t_visit_payment.t_visit_id
AND t_visit.visit_hn = t_patient.patient_hn
AND t_visit_payment.b_contract_plans_id=b_contract_plans.b_contract_plans_id
AND t_patient.f_sex_id='1'
AND t_visit.f_visit_type_id='0'
AND t_visit.f_visit_status_id='3'
AND (SUBSTRING(t_visit.visit_financial_discharge_time,0,11) BETWEEN '2548-07-15' and '2548-07-15')
GROUP BY
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description
) AS temp1 ON b_contract_plans.contract_plans_number=temp1.contract_plans_number
LEFT JOIN
(
SELECT
b_contract_plans.contract_plans_number AS contract_plans_number
,COUNT(t_visit.t_visit_id) AS total
FROM
b_contract_plans,t_visit,t_visit_payment,t_patient
WHERE
t_visit.t_visit_id=t_visit_payment.t_visit_id
AND t_visit.visit_hn = t_patient.patient_hn
AND t_visit_payment.b_contract_plans_id=b_contract_plans.b_contract_plans_id
AND t_patient.f_sex_id='2'
AND t_visit.f_visit_type_id='0'
AND t_visit.f_visit_status_id='3'
AND (SUBSTRING(t_visit.visit_financial_discharge_time,0,11) BETWEEN '2548-07-15' and '2548-07-15')
GROUP BY
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description
) AS temp2 ON b_contract_plans.contract_plans_number=temp2.contract_plans_number
LEFT JOIN
(
SELECT
b_contract_plans.contract_plans_number AS contract_plans_number
,COUNT(t_visit.t_visit_id) AS total
FROM
b_contract_plans,t_visit,t_visit_payment
WHERE
t_visit.t_visit_id=t_visit_payment.t_visit_id
AND t_visit_payment.b_contract_plans_id=b_contract_plans.b_contract_plans_id
AND t_visit.f_visit_type_id='0'
AND t_visit.f_visit_status_id='3'
AND (SUBSTRING(t_visit.visit_financial_discharge_time,0,11) BETWEEN '2548-07-15' and '2548-07-15')
GROUP BY
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description
) AS temp3 ON b_contract_plans.contract_plans_number=temp3.contract_plans_number
)
GROUP BY
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description
,male
,female
,total_all
ORDER BY
b_contract_plans.contract_plans_number

ก็ได้ผลของการคิวรี่ดังนี้
A1     ชำระเงินเอง     1     3     4
A11 ผู้ประสบภัยจากรถ (หลักฐานครบ)
A12 ผู้ประสบภัยจากรถ (หลักฐานไม่ครบ) 3 3
A21 เบิกได้ (ข้าราชการ/ลูกจ้างประจำ) 8 8 16
A22 เบิกได้ (รัฐวิสาหกิจ,ส่วนท้องถิ่น)
A71 บัตรประกันสังคม (คู่สัญญา)
A72 บัตรประกันสังคม (คลอด ชำระเอง)
A73 บัตรประกันสังคม (ญาติสายตรง)
A74 บัตรประกันสังคม (เจ้าหน้าที่โรงพยาบาล) 1 1
A75 บัตรประกันสังคม (ทำฟัน ชำระเอง)
A76 บัตรประกันสังคม (ไม่ใช่คู่สัญญา ชำระเงินเอง) 1 1
AA บัตรทองมี ท (เด็ก 0-5 ปี) 3 3
AA1 บัตรทองมี ท (เด็ก 6-12 ปี) 1 2 3
AB บัตรทองมี ท (ผู้มีรายได้น้อย) 9 14 23
AC บัตรทองมี ท (นักเรียนมัธยมต้น) 2 2 4
AD บัตรทองมี ท (ผู้พิการ) 1 1
AE บัตรทองมี ท (ทหารผ่านศึก) 1 1
AF บัตรทองมี ท (สมณเพศ) 2 2
AG บัตรทองมี ท (ผู้สูงอายุ) 13 14 27
AH บัตรทองมี ท (บัตรชั่วคราว)
AH1 บัตรทองมี ท (พอสว)
AH2 บัตรทองมี ท (นอกเครือข่าย ชำระเงินเอง)
AH3 บัตรทองมี ท (นอกเครือข่ายฉุกเฉิน) 1 1
AH4 บัตรแรงงานต่างด้าว (ในเครือข่าย)
AH5 บัตรแรงงานต่างด้าว (นอกเครือข่าย)
AH6 บัตรทองมี ท (ในเครือข่าย ชำระเงินเอง)
AJ บัตรทองมี ท (ผู้นำชุมชน) 2 2 4
AK1 บัตรทองมี ท (อสม ในเขตอำเภอ) 2 2 4
AK2 บัตรทองมี ท (อสม นอกเขตอำเภอ)
AZ สังคมสงเคราะห์
UC1 บัตรทองเสียค่าธรรมเนียม 12 18 30
UC2 บัตรทองเสียค่าธรรมเนียม (นอกเครือข่าย ชำระเงินเอง) 1 1
UC3 บัตรทองเสียค่าธรรมเนียม (นอกเครือข่ายฉุกเฉิน)
UC4 บัตรทองเสียค่าธรรมเนียม (ในเครือข่าย ชำระเงินเอง)

รู้สึกว่ามันเยิ่นเย้อ มากไปหน่อย ลองดูอีกวิธีดีกว่า ตัด temp2, temp3 ออกไป temp4 คงเดิม แต่ temp1 มีการโมดิฟายเล็กน้อยโดยเอา temp1, temp2, temp3 มารวมกัน อ่า รวมได้ยังงัย เทคนิคนี้เรียกว่า เทคนิคการคิวรี่แบบ Pivot Table ดูกันเล้ย

SELECT
b_contract_plans.contract_plans_number AS contract_plans_number
,SUM(CASE when t_patient.f_sex_id = '1' THEN 1 ELSE 0 END) AS male
,SUM(CASE when t_patient.f_sex_id = '2' THEN 1 ELSE 0 END) AS female
,COUNT(t_visit.t_visit_id) AS total

FROM
b_contract_plans,t_visit,t_visit_payment,t_patient
WHERE
t_visit.t_visit_id=t_visit_payment.t_visit_id
AND t_visit.visit_hn = t_patient.patient_hn
AND t_visit_payment.b_contract_plans_id=b_contract_plans.b_contract_plans_id
AND t_visit.f_visit_type_id='0'
AND t_visit.f_visit_status_id='3'
AND (SUBSTRING(t_visit.visit_financial_discharge_time,0,11) BETWEEN '2548-07-15' and '2548-07-15')
GROUP BY
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description
ORDER BY
b_contract_plans.contract_plans_number

SUM(CASE when t_patient.f_sex_id = '1' THEN 1 ELSE 0 END) AS male

อธิบายนิดนึงครับ ว่าตั้งแต่ CASE ........ไปจนถึง END หมายความว่า..

เมื่อ t_patient.f_sex_id='1' ให้มีค่าเท่ากับ 1 ส่วนค่าอื่นๆ (t_patient.f_sex_id='2') ไม่สนใจให้เป็นศูนย์ ไปจนครบเงื่อนไขตามที่เรา WHERE ไว้ เมื่อครบแล้วก็ให้ SUM ได้เท่าไรเก็บไว้ใน male ส่วนค่า female นั้นก็ให้นับคล้ายๆ กันเพียงแต่บังคับให้นับเฉพาะ t_patient.f_sex_id='2' เท่านั้นให้เป็น 1 บรรทัดสุดท้ายคือ COUNT(t_visit.t_visit_id) ก็คือนับทั้ง t_patient.f_sex_id='1' และ t_patient.f_sex_id='2' เก็บไว้ในค่า total พอได้ temp1 เช่นนี้แล้วก็เอามา JOIN กับ temp4 เหมือนเดิม ได้ดังนี้

SELECT
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description
,temp1.male AS male
,temp1.female AS female
,temp1.total AS total_all

FROM
(b_contract_plans LEFT JOIN
(
SELECT
b_contract_plans.contract_plans_number AS contract_plans_number
,SUM(CASE when t_patient.f_sex_id = '1' THEN 1 ELSE 0 END) AS male
,SUM(CASE when t_patient.f_sex_id = '2' THEN 1 ELSE 0 END) AS female
,COUNT(t_visit.t_visit_id) AS total

FROM
b_contract_plans,t_visit,t_visit_payment,t_patient
WHERE
t_visit.t_visit_id=t_visit_payment.t_visit_id
AND t_visit.visit_hn = t_patient.patient_hn
AND t_visit_payment.b_contract_plans_id=b_contract_plans.b_contract_plans_id
AND t_visit.f_visit_type_id='0'
AND t_visit.f_visit_status_id='3'
AND (SUBSTRING(t_visit.visit_financial_discharge_time,0,11) BETWEEN '2548-07-15' and '2548-07-15')
GROUP BY
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description
ORDER BY
b_contract_plans.contract_plans_number
) AS temp1 ON b_contract_plans.contract_plans_number=temp1.contract_plans_number
)
GROUP BY
b_contract_plans.contract_plans_number
,b_contract_plans.contract_plans_description
,male
,female
,total_all
ORDER BY
b_contract_plans.contract_plans_number
พอลองรันดูก็ได้ผลดังนี้

A1 ชำระเงินเอง 1 3 4
A11 ผู้ประสบภัยจากรถ (หลักฐานครบ)
A12 ผู้ประสบภัยจากรถ (หลักฐานไม่ครบ) 3 3
A21 เบิกได้ (ข้าราชการ/ลูกจ้างประจำ) 8 8 16
A22 เบิกได้ (รัฐวิสาหกิจ,ส่วนท้องถิ่น)
A71 บัตรประกันสังคม (คู่สัญญา)
A72 บัตรประกันสังคม (คลอด ชำระเอง)
A73 บัตรประกันสังคม (ญาติสายตรง)
A74 บัตรประกันสังคม (เจ้าหน้าที่โรงพยาบาล) 1 1
A75 บัตรประกันสังคม (ทำฟัน ชำระเอง)
A76 บัตรประกันสังคม (ไม่ใช่คู่สัญญา ชำระเงินเอง) 1 1
AA บัตรทองมี ท (เด็ก 0-5 ปี) 3 3
AA1 บัตรทองมี ท (เด็ก 6-12 ปี) 1 2 3
AB บัตรทองมี ท (ผู้มีรายได้น้อย) 9 14 23
AC บัตรทองมี ท (นักเรียนมัธยมต้น) 2 2 4
AD บัตรทองมี ท (ผู้พิการ) 1 1
AE บัตรทองมี ท (ทหารผ่านศึก) 1 1
AF บัตรทองมี ท (สมณเพศ) 2 2
AG บัตรทองมี ท (ผู้สูงอายุ) 13 14 27
AH บัตรทองมี ท (บัตรชั่วคราว)
AH1 บัตรทองมี ท (พอสว)
AH2 บัตรทองมี ท (นอกเครือข่าย ชำระเงินเอง)
AH3 บัตรทองมี ท (นอกเครือข่ายฉุกเฉิน) 1 1
AH4 บัตรแรงงานต่างด้าว (ในเครือข่าย)
AH5 บัตรแรงงานต่างด้าว (นอกเครือข่าย)
AH6 บัตรทองมี ท (ในเครือข่าย ชำระเงินเอง)
AJ บัตรทองมี ท (ผู้นำชุมชน) 2 2 4
AK1 บัตรทองมี ท (อสม ในเขตอำเภอ) 2 2 4
AK2 บัตรทองมี ท (อสม นอกเขตอำเภอ)
AZ สังคมสงเคราะห์
UC1 บัตรทองเสียค่าธรรมเนียม 12 18 30
UC2 บัตรทองเสียค่าธรรมเนียม (นอกเครือข่าย ชำระเงินเอง) 1 1
UC3 บัตรทองเสียค่าธรรมเนียม (นอกเครือข่ายฉุกเฉิน)
UC4 บัตรทองเสียค่าธรรมเนียม (ในเครือข่าย ชำระเงินเอง)

ตัดออกไปได้หลายบรรทัดเลยล่ะ ดูโค๊ด SQL ก็ง่ายกว่า อิ อิ ตอนแรกว่าจะไม่เอามาพูดแล้วนะ กลัวมันจะยาวไปจนเอียน แต่ทนไม่ได้ต้องเอามาพูดให้รู้แจ้งเห็นจริง เหมือนที่เรามีประสบการณ์เคยทำมา จะได้รู้สึกว่ามัน เข้าถึงจริงๆ มีความรู้สึกว่าถ้าไม่ได้พูดเสียเดี๋ยวนี้มันอึดอัดกับความรู้สึก ไม่ใช่ว่าสักแต่พูด แต่พูดต้องพูดให้หมด ไม่มีกั๊กไว้ คนที่เข้ามาอ่านจะได้อะไรกลับไปมากๆ หน่อย สมกับที่เสียเวลาเข้ามาอ่านตั้งนาน จะได้เอาไปประยุกต์ใช้ได้ให้เหมาะกับงาน เราเองก็ไม่ต้องอึดอัด สรุปแล้วก็ Happy กัน จบล่ะนะ



แหล่งความรู้และอ้างอิง



แสดงความคิดเห็น
Powered for by Blogger Templates free hit counter code
Copyright ? 2008-2009 Uthai Lueadnakrop. All Rights reserved