Mine

รูปภาพของฉัน
หาดใหญ่, ภาคใต้, Thailand
ชอบเอาใจ แต่ก็เอาแต่ใจ

วันศุกร์ที่ 2 กรกฎาคม พ.ศ. 2553

Query รายงานและแสดงผล lab ในแนวนอนมาดูกัน

สวัสดีค่ะ วันก่อนมี it โรงพยาบาลสอบถามมาเรื่องจะทำรายงานยังไงให้นำผล Lab มาแสดงในแนวนอนได้ จะบอกว่าดีใจมากเลยค่ะที่น้องตั้งใจและคอยศึกษาเพิ่มเติมอยู่เสมอ เมื่อติดปัญหาก็นึกถึง (ดีใจจริงๆนะเนี๊ยะ) และก็คิดว่าสิ่งนี้น่าจะเป็นประโยชน์สำหรับอีกหลายๆคนที่กำลังทำรายงานด้วยตนเองอยู่ อย่างไรก็ตามขอให้มี case มาถามบ่อยๆนะคะ จะได้นำมาเผยแพร่ให้กับทุกคนได้ร่วมเรียนรู้และเดินไปด้วยกันค่ะ

สำหรับคำถามที่ถามมาก็ตอบไปค่ะ แต่ก็ไม่ได้เขียนให้นะ ก็แนะนำไปค่ะ ตอนนี้ก็ไม่รู้ว่าได้ผลยังไงบ้างแต่ก็คงเพราะโอ๋เองไม่ค่อยได้ online (บ้านนอก..on กับมือถือ อิอิ

คำถามหากวิเคราะห์ออกมาเป็น Output ที่ต้องการก็น่าจะเป็น
namepat hn pid vn CHOLESTEROL TRIGRYCERIDE HDL-CHOLESTEROL LDL-CHOLESTERAL[CALCULATE] 
ทดสอบ1 490004971 3461200371659 530014001 191 105 32 138
ทดสอบ2 470002778 530014001 275 201 56 179

จาก Output ที่ต้องการนั้น โดยความสามารถของ PostgreSQL8.0 ยังไม่มีในส่วนของการทำ Crosstab Table ค่ะ (version ใหม่ๆ เหมือนจะมีแล้วนะคะ...มั้ง) 
ที่จริงหากไม่ต้องการทำอะไรให้ยุ่งยาก หรือไม่ต้องการเขียน Query ที่ยุ่งยาก ก็นำผลที่เราเขียน Query นั้น Save เป็น Excel หลังจากนั้นก็ใช้ตวามสามารถของ Excel ต่อได้เลยค่ะ ง่ายๆ สบายมาก หรืออีกวิธี คือ เราก็ทำ Query ใน Asscess ไปเลยมี crosstab อยู่แล้วค่ะ เพียงเรา Link ข้อมูลเข้าไปที่ Access ก็สามารถทำ Query ได้ค่ะ

แต่ถ้าต้องการให้ได้ข้อมูลในผลที่ได้จาก QUery ของเราก็พอมีทางอยู่ค่ะ แต่มีข้อจำกัดพอสมควร คือ ต้องทำการ Fixed เฉพาะข้อมูล Lab ที่ต้องการเท่านั้น ในที่นี้เราต้องการทราบ LIPID PROFILE ซึ่งมี Lab test อยู่ 4 ตัว คือ CHOLESTEROL,TRIGRYCERIDE,HDL-CHOLESTEROL,LDL-CHOLESTERAL[CALCULATE] เรารู้แน่ว่าเราต้องการผลนี้ และในฐานข้อมูลเราก็ Set item สำหรับใช้งานเรียบร้อยแล้ว ก็มาเริ่มต้นทำงานกันค่ะ  ก่อนทำงานสำรวจข้อมูล ดังนี้
รหัสของ LIPID PROFILE ในตาราง b_item
174000009974453508    C481    LIPID PROFILE

ชื่อของ LAB Test ที่ใช้
CHOLESTEROL
TRIGRYCERIDE
HDL-CHOLESTEROL
LDL-CHOLESTERAL[CALCULATE]

ขั้นที่ 1 เราจะต้อง Query ข้อมูลให้ได้ตามที่เราต้องการขึ้นมาก่อน
select
(t_patient.patient_firstname || '  ' || t_patient.patient_lastname) as namepat
,t_patient.patient_hn as hn
, t_patient.patient_pid as pid
, t_visit.visit_vn as vn
, t_order.order_common_name as labname
, t_result_lab.result_lab_name as labtest
, t_result_lab.result_lab_value as labval
, t_result_lab.result_lab_unit as labunit
from t_patient inner join t_visit on t_patient.t_patient_id = t_visit.t_patient_id
inner join t_order on t_order.t_visit_id = t_visit.t_visit_id
inner join t_result_lab on t_order.t_order_id = t_result_lab.t_order_id
where  t_order.b_item_id = '174000009974453508' --รหัสของ LIPID PROFILE
and t_order.f_order_status_id = '4' --สถานะที่รายงานผลเรียบร้อยแล้ว
and t_visit.f_visit_status_id <> '4' --ต้องไม่ยกเลิกการเข้ารับบริการ
and t_result_lab.result_lab_active = '1' --ไม่ถูกยกเลิกผล
and substring(t_visit.visit_financial_discharge_time,1,10) Between '2553-05-31' and '2553-05-31' --วันที่ที่เราสนใจจากวันที่จำหน่ายออก หรือเราจะเลือกใช้วันอื่นก็ได้เช่น วันที่สั่ง lab วันที่เข้ารับบริการ

ผลลัพธ์ที่จะได้ คือ Record ที่เท่ากับจำนวน LABTEST รวมกัน คือ 1 visit จะมี 4 LABTEST นั่นคือเราจะได้ visit ละ 4 record
namepat hn pid vn labname labtest labval labunit
ทดสอบ1  ผลแลป1 510006338 053017052 LIPID PROFILE CHOLESTEROL 215 mg/dl [125-200]  
ทดสอบ1  ผลแลป1 510006338 053017052 LIPID PROFILE TRIGRYCERIDE  113 mg/dl [45 - 150 ]
ทดสอบ1  ผลแลป1 510006338 053017052 LIPID PROFILE HDL-CHOLESTEROL 71 mg/dl [40 - 60 ] 
ทดสอบ1  ผลแลป1 510006338 053017052 LIPID PROFILE LDL-CHOLESTERAL[CALCULATE] 121 mg/dl [0-130]  
ทดสอบ2  ผลแลป2 350022285 053017048 LIPID PROFILE CHOLESTEROL 139 mg/dl [125-200]  
ทดสอบ2  ผลแลป2 350022285 053017048 LIPID PROFILE TRIGRYCERIDE  206 mg/dl [45 - 150 ]
ทดสอบ2  ผลแลป2 350022285 053017048 LIPID PROFILE HDL-CHOLESTEROL 39 mg/dl [40 - 60 ] 
ทดสอบ2  ผลแลป2 350022285 053017048 LIPID PROFILE LDL-CHOLESTERAL[CALCULATE] 59 mg/dl [0-130]  
ทดสอบ 3  ผลแลป 3 500000960 053017086 LIPID PROFILE CHOLESTEROL 188 mg/dl [125-200]  
ทดสอบ 3  ผลแลป 3 500000960 053017086 LIPID PROFILE TRIGRYCERIDE  125 mg/dl [45 - 150 ]
ทดสอบ 3  ผลแลป 3 500000960 053017086 LIPID PROFILE HDL-CHOLESTEROL 39 mg/dl [40 - 60 ] 
ทดสอบ 3  ผลแลป 3 500000960 053017086 LIPID PROFILE LDL-CHOLESTERAL[CALCULATE] 124 mg/dl [0-130]  

ขั้นที่ 2 นำเอา Output จากข้อแรกมา ทำการจัดรูปแบบแสดงผลใหม่ โดยใช้ความสามารถของ Case....when....then....else นะคะ
select
(t_patient.patient_firstname || '  ' || t_patient.patient_lastname) as namepat
,t_patient.patient_hn as hn
, t_patient.patient_pid as pid
, t_visit.visit_vn as vn
, case when t_result_lab.result_lab_name ilike 'CHOLESTEROL'
            then t_result_lab.result_lab_value
            else ''
  end as "CHOLESTEROL"
, case when t_result_lab.result_lab_name ilike 'TRIGRYCERIDE'
            then t_result_lab.result_lab_value
            else ''
  end as "TRIGRYCERIDE"
, case when t_result_lab.result_lab_name ilike 'HDL-CHOLESTEROL'
            then t_result_lab.result_lab_value
            else ''
  end as "HDL-CHOLESTEROL"
, case when t_result_lab.result_lab_name ilike 'LDL-CHOLESTERAL[CALCULATE]'
            then t_result_lab.result_lab_value
            else ''
  end as "LDL-CHOLESTERAL[CALCULATE]"
from t_patient inner join t_visit on t_patient.t_patient_id = t_visit.t_patient_id
inner join t_order on t_order.t_visit_id = t_visit.t_visit_id
inner join t_result_lab on t_order.t_order_id = t_result_lab.t_order_id
where  t_order.b_item_id = '174000009974453508' --รหัสของ LIPID PROFILE
and t_order.f_order_status_id = '4' --สถานะที่รายงานผลเรียบร้อยแล้ว
and t_visit.f_visit_status_id <> '4' --ต้องไม่ยกเลิกการเข้ารับบริการ
and t_result_lab.result_lab_active = '1' --ไม่ถูกยกเลิกผล
and substring(t_visit.visit_financial_discharge_time,1,10) Between '2553-05-31' and '2553-05-31' --วันที่ที่เราสนใจจากวันที่จำหน่ายออก หรือเราจะเลือกใช้วันอื่นก็ได้เช่น วันที่สั่ง lab วันที่เข้ารับบริการ

ผลลัพธ์ที่จะได้ คือ Record ยังคงเป็น 4 record แต่เราจะได้ Column ของผล LAB แยกเป็น 4 ตัว
namepat hn pid vn CHOLESTEROL TRIGRYCERIDE HDL-CHOLESTEROL LDL-CHOLESTERAL[CALCULATE]
ทดสอบ1  ผลแลป1 510006338 053017052 215
ทดสอบ1  ผลแลป1 510006338 053017052 113
ทดสอบ1  ผลแลป1 510006338 053017052 71
ทดสอบ1  ผลแลป1 510006338 053017052 121
ทดสอบ2  ผลแลป2 350022285 053017048 139
ทดสอบ2  ผลแลป2 350022285 053017048 206
ทดสอบ2  ผลแลป2 350022285 053017048 39
ทดสอบ2  ผลแลป2 350022285 053017048 59
ทดสอบ 3  ผลแลป 3 500000960 053017086 188
ทดสอบ 3  ผลแลป 3 500000960 053017086 125
ทดสอบ 3  ผลแลป 3 500000960 053017086 39
ทดสอบ 3  ผลแลป 3 500000960 053017086 124

ขั้นที่ 3 จากขั้นที่สองไม่ได้ตามที่ต้องการเพราะ Record เบิ้ลอยู่ค่ะ ก็ทำให้มันสวยซะ ในขั้นนี้ใช้การลักไก่นิดหน่อยค่ะ อิอิ ....เราจะสามารถลักไก่ในขั้นที่สามได้ เราจะต้องตั้งใจทำให้เกิดการลักไก่

จากขั้นที่สองค่ะ ลองสังเกตดูนะว่าใน case ของเราทำอะไรไว้บ้าง

, case when t_result_lab.result_lab_name ilike 'CHOLESTEROL'
    then t_result_lab.result_lab_value --ค่านี้เป็นค่าของ Lab ค่ะ จะเป็นอะไรก็แล้วแต่ขึ้นอยู่กับผลที่ได้
    else '' --อันนี้แหละเป็นค่า '' ซึ่งนำมาลักไก่ได้ค่ะ
end as "CHOLESTEROL"

เราจะใช้ความสามารถของ function max(...) เข้ามาช่วยค่ะ max ก็คือ ค่าที่มากที่สุดเพียงค่าเดียวค่ะ ดูจาก case ที่เราสร้างไว้หลัง else ถ้าเข้าเงื่อนไขนี้จะเป็นค่าว่างเสมอนะคะ ส่วนหลัง then นั้นจะเป็นค่าอะไรก็แล้วแต่ ในความหมายของมันคือ ต้องมากกว่า '' แน่นอนค่ะ  ที่สำคัญคือ การที่เราจะใช้ function max ได้เราจะต้องบอกว่าเรา max ตามค่าอะไรบ้าง นั่นคือต้อง group by ให้มันทราบค่ะ จะได้ทำงานถูกต้อง ทั้นี้เราต้องการข้อมูลของคนๆนึง ดังนั้น Group ที่จะจับคือ คนนั้น นั่นเอง

select
(t_patient.patient_firstname || '  ' || t_patient.patient_lastname) as namepat
,t_patient.patient_hn as hn
, t_patient.patient_pid as pid
, t_visit.visit_vn as vn
, max(case when t_result_lab.result_lab_name ilike 'CHOLESTEROL'
            then t_result_lab.result_lab_value
            else ''
  end) as "CHOLESTEROL"
, max(case when t_result_lab.result_lab_name ilike 'TRIGRYCERIDE'
            then t_result_lab.result_lab_value
            else ''
  end) as "TRIGRYCERIDE"
, max(case when t_result_lab.result_lab_name ilike 'HDL-CHOLESTEROL'
            then t_result_lab.result_lab_value
            else ''
  end) as "HDL-CHOLESTEROL"
, max(case when t_result_lab.result_lab_name ilike 'LDL-CHOLESTERAL[CALCULATE]'
            then t_result_lab.result_lab_value
            else ''
  end) as "LDL-CHOLESTERAL[CALCULATE]"
from t_patient inner join t_visit on t_patient.t_patient_id = t_visit.t_patient_id
inner join t_order on t_order.t_visit_id = t_visit.t_visit_id
inner join t_result_lab on t_order.t_order_id = t_result_lab.t_order_id
where  t_order.b_item_id = '174000009974453508' --รหัสของ LIPID PROFILE
and t_order.f_order_status_id = '4' --สถานะที่รายงานผลเรียบร้อยแล้ว
and t_visit.f_visit_status_id <> '4' --ต้องไม่ยกเลิกการ เข้ารับบริการ
and t_result_lab.result_lab_active = '1' --ไม่ถูกยกเลิกผล
and substring(t_visit.visit_financial_discharge_time,1,10) Between '2553-05-31' and '2553-05-31' --วันที่ที่เราสนใจจากวันที่ จำหน่ายออก หรือเราจะเลือกใช้วันอื่นก็ได้เช่น วันที่สั่ง lab วันที่เข้ารับบริการgroup by namepat,hn,pid,vn --เราจะต้องนำ column ที่เราไม่ได้ใช้กับ function มา group by ทั้งหมดนะคะ ในที่นี้ใช้ชื่อที่ as ไว้แล้วได้เลยค่ะ

ผลลัพธ์ที่จะได้ คือ ได้ข้อมูลผล LAB ตามแนวนอนแล้วค่ะ และไม่เบิ้ลด้วย

namepat hn pid vn CHOLESTEROL TRIGRYCERIDE HDL-CHOLESTEROL LDL-CHOLESTERAL[CALCULATE]
ทดสอบ 3  ผลแลป 3 500000960 053017086 188 125 39 124
ทดสอบ2  ผลแลป2 350022285 053017048 139 206 39 59
ทดสอบ1  ผลแลป1 510006338 053017052 215 113 71 121


ทิ้งท้ายเอาไว้ แล้วถ้าเราต้องการดู Lab 2 ตัวล่ะ จะเพิ่มเข้าไปยังไงดี ลองทำขั้นตอนที่ 4 ต่อนะคะ  หรือเราต้องการค่าของวันที่ตรวจเพิ่มเติม ทำยังไงดี .... ทุกอย่างมีคำตอบ หากลงมือทำ ....
ดังนั้น เราก็ได้สิ่งที่เราต้องการแล้วค่ะ จริงๆเราก็ต้องดูเป็นขั้นตอนว่าข้อมูลมายังไง เราจึงจะทำตรงนี้ได้ค่ะ ค่อยๆทำทีละขั้น และทำความเข้าใจ และหาทางแก้มันซะ มันไม่ยากหากเราตั้งใจค่ะ สู้ๆ สู้ๆ สู้ตาย ......

และขอบคุณ อิง โรงพยาบาลเทพา ที่ถามคำถามชวนตอบค่ะ

วันพฤหัสบดีที่ 17 มิถุนายน พ.ศ. 2553

ช่วงบน และช่วงล่าง ของความดันแยกจากกันได้มั้ย

สวัสดีค่ะ ...
เนื่องจากมีเสียงสมาชิก Hospital OS สอบถามมาหลายเสียง เรื่อง "การหาค่า บน และ ค่าล่าง ของความดัน" ทำอย่างไร เช่น 120/80 ต้องการทราบว่าค่าบน คือ 120 ส่วนค่าล่างคือ 80 จะทำได้อย่างไร เพราะความจริงแล้วเราไม่สามารถระบุได้ว่าค่าบนจะเป็น 3 หลักเสมอไป เพราะผู้ป่วยบาง case อาจจะมี 2 หลักก็เป็นได้ เช่น 82/59

ซึ่งตรงนี้คิดว่า คนที่ไม่ถามก็อาจจะอยากรู้ หรือ คนที่ได้บอกไปแล้ว ก็อาจจะได้มาทำความเข้าใจกันค่ะ

มาดูกันเลยค่ะ ...

ถ้าเรามองข้อมูลกันให้ดีๆ จะเห็นว่าเป็น Pattern เดียวกันค่ะ
120/80
82/59

จะเห็นว่าข้อมูลที่จัดเก็บนั้น จะคั่นค่าชั้นบน กับค่าชั้นล่าง ด้วย "/"
เราสามารถนำ "/" มาช่วยในการตัดข้อมูลแยกจากกันได้ค่ะ
แต่ว่า เราจะทราบได้อย่างไรว่าตำแหน่งของ "/" จะอยู่ที่ไหนกันแน่

ก่อนอื่นมาทบทวนกันก่อนค่ะ เรื่อง String ใน PostgreSQL
การนับ String หรือ สายอักษร เราจะเริ่มนับที่ 1 ค่ะ เช่น 120/80
1 -> นับเป็นตำแหน่งที่ 1
2 -> นับเป็นตำแหน่งที่ 2
0 -> นับเป็นตำแหน่งที่ 3
/ -> นับเป็นตำแหน่งที่ 4
8 -> นับเป็นตำแหน่งที่ 5
0 -> นับเป็นตำแหน่งที่ 6

ขอนำเสนอคำสั่ง position ซึ่งเป็นคำสั่งในการหาตำแหน่งของคำที่เรากำหนดค่ะ
รูปแบบคำสั่ง
position('[คำที่เราต้องการหาตำแหน่ง]' in [ชื่อ Filed ที่เราต้องการให้หาตำแหน่งของคำ])
จะแสดงผลลัพธ์เป็น
จะแสดงค่าตำแหน่งของคำที่ค้นหา
ถ้าพบ -> จะแสดงตำแหน่งโดยเริ่มนับจาก 1
ถ้าไม่พบ -> จะแสดงเลข 0
ตัวอย่าง จากข้อมูลความดัน
120/80
82/59
คำสั่งที่เขียน(ขออ้างอิงตารางของ HOspital OS)
select
visit_vital_sign_blood_presure --ข้อมูลที่เก็บในฐานข้อมูล
, position('/' in visit_vital_sign_blood_presure) --แสดงตำแหน่งของ "/" ที่หาพบในข้อมูล
from t_visit_vital_sign
ผลลัพธ์
120/80    4
82/59    3

เมื่อเราได้ตำแหน่งของ "/" ตามที่ต้องการแล้ว มาดูกันต่อว่าเราจะนำมาใช้งานได้อย่างไร

เราจำคำสั่ง substring ได้หรือเปล่าคะ ยังไงก็มาดูก็แล้วกัน
รูปแบบคำสั่ง1 
substring([ชื่อ Filed ที่เราต้องการตัด],[ตำแหน่งเริ่มต้นของการตัด],[จำนวนที่เราต้องการตัด])
จะแสดงผลลัพธ์เป็น
แสดงอักษรจากจุดที่เริ่มตัด นับไปเท่ากับจำนวนที่เรากำหนด
ตัวอย่าง จากข้อมูลความดัน
120/80
82/59
คำสั่งที่เขียน(ขออ้างอิงตารางของ HOspital OS)
select
visit_vital_sign_blood_presure --ข้อมูลที่เก็บในฐานข้อมูล
, substring(visit_vital_sign_blood_presure,1,3) --แสดงข้อมูลที่ต้องการตัด ตำแหน่งที่ 1 นับไป 3 ตัว
from t_visit_vital_sign
ผลลัพธ์
120/80    120
82/59    82/

รูปแบบคำสั่ง2
substring([ชื่อ Filed ที่เราต้องการตัด],[ตำแหน่งเริ่มต้นของการตัด])
จะแสดงผลลัพธ์เป็น
แสดงอักษรจากจุดที่เริ่มตัดไปจนสิ้นสุดอักขระของ String นั้น
ตัวอย่าง จากข้อมูลความดัน
120/80
82/59
คำสั่งที่เขียน(ขออ้างอิงตารางของ HOspital OS)
select
visit_vital_sign_blood_presure --ข้อมูลที่เก็บในฐานข้อมูล
, substring(visit_vital_sign_blood_presure,5) --แสดงข้อมูลที่ต้องการตัด ตำแหน่งที่ 5 เป็นต้นไป
from t_visit_vital_sign
ผลลัพธ์
120/80    80
82/59    9

เราเรียนรู้คำสั่งในการตัด String แล้วแต่เราก็ยังไม่สามารถนำมาใช้โดยตรงได้ค่ะ เพราะจากลักษณะของข้อมูลแล้วมีความเป็นไปได้ตามข้อมูลตัวอย่างข้างต้น เมื่อเรากับหนดค่าที่ตัดลงไปแบบ Fixed เราก็จะได้ผลลัพธ์ที่ไม่ถูกต้องค่ะ เราต้องนำความรู้มาผสมผสานให้เกิดสิ่งที่เราต้องการให้ได้ นั่นคือ การนำ position เข้ามาเป็นส่วนหนึ่งของการตัด String ถ้าเราสังเกตุตัวอย่าง จะเห็นว่า

ลองสันนิฐานดูว่า
จากตัวอย่างข้อมูลความดัน 120/80   
เราต้องการตัด string 3 หลักแรก คือ substring(visit_vital_sign_blood_presure,1,3)
ค่า position ของ "/" คือ 4  
เป็นไปได้หรือไม่ว่า จำนวนที่เราต้องการ คือ position-1 นั่นคือ 4-1 = 3

จากตัวอย่างข้อมูลความดัน 82/59   
เราต้องการตัด string 2 หลักแรก คือ substring(visit_vital_sign_blood_presure,1,2)
ค่า position ของ "/" คือ 3  
เป็นไปได้หรือไม่ว่า จำนวนที่เราต้องการ คือ position-1 นั่นคือ 3-1 = 2

ดังนั้น จากข้อสันนิฐาน เราก็นำมาพิสูจน์กันค่ะ ลองเขียนคำสั่งที่นำมาผสมผสานกันดูนะคะ
จะได้เป็น
select visit_vital_sign_blood_presure --ค่าในฐานข้อมูล
, position('/' in visit_vital_sign_blood_presure) --ตำแหน่งของ "/"
, substring(visit_vital_sign_blood_presure,1,(position('/' in visit_vital_sign_blood_presure)) -1) --ค่าความดันช่วงบน
from t_visit_vital_sign
where visit_vital_sign_blood_presure <> '' --ป้องกันค่าว่าง เพราะจะทำให้เกิด Error ได้ค่ะ
จะได้ผลลัพธ์เป็น
120/80    120
82/59    82

แล้วลองดูผลลัพธ์ที่เครื่องคุณนะคะ ว่าได้ผลที่ถูกต้องหรือเปล่า

ค่ะ อั้นนั้นมันเป็นเรื่องของเบื้องบน เราลองมาดูเบื้องล่างกันบ้างนะคะ
ลองสันนิฐานดูว่า
จากตัวอย่างข้อมูลความดัน 120/80   
เราต้องการตัด string หลังจาก "/" ทั้งหมด คือ substring(visit_vital_sign_blood_presure,5)
ค่า position ของ "/" คือ 4  
เป็นไปได้หรือไม่ว่า จุดเริ่มต้นที่เราต้องการ คือ position+1 นั่นคือ 4+1 = 5

จากตัวอย่างข้อมูลความดัน 82/59   
เราต้องการตัด string หลังจาก "/" ทั้งหมด คือ substring(visit_vital_sign_blood_presure,4)
ค่า position ของ "/" คือ 3  
เป็นไปได้หรือไม่ว่า จุดเริ่มต้นที่เราต้องการ คือ position+1 นั่นคือ 3+1 = 4

ดังนั้น จากข้อสันนิฐาน เราก็นำมาพิสูจน์กันค่ะ ลองเขียนคำสั่งที่นำมาผสมผสานกันดูนะคะ
จะได้เป็น
select visit_vital_sign_blood_presure --ค่าในฐานข้อมูล
, position('/' in visit_vital_sign_blood_presure) --ตำแหน่งของ "/"
, substring(visit_vital_sign_blood_presure,(position('/' in visit_vital_sign_blood_presure)) +1) --ค่าความดันช่วงล่าง
from t_visit_vital_sign
where visit_vital_sign_blood_presure <> '' --ป้องกันค่าว่าง เพราะจะทำให้เกิด Error ได้ค่ะ
จะได้ผลลัพธ์เป็น
120/80    80
82/59    59

ไหนๆ ก็ทำแล้วก็ลองเอามารวมกันเลยค่ะ จะได้รันทีเดียวได้ ทั้งช่วงบนช่วงล่าง
ตามนี้
select visit_vital_sign_blood_presure
, substring(visit_vital_sign_blood_presure,1,(position('/' in visit_vital_sign_blood_presure)) -1) --ค่าความดันช่วงบน
, substring(visit_vital_sign_blood_presure,(position('/' in visit_vital_sign_blood_presure)) +1) --ค่าความดันช่วงล่าง
from t_visit_vital_sign where visit_vital_sign_blood_presure <> ''

ค่ะ ถึงตอนนี้ก็อยู่ที่ว่าจะนำไปใช้ทำอะไรแล้วค่ะ ไม่ว่าจะเป็น
- ผู้ป่วยที่มีความดัน เกิน 140 มีใครบ้าง อะไรประมาณนี้

อ่อ...แล้วก็ ไม่จำเป็นว่าคำสั่งต่างๆที่เราใช้จะต้องอยู่ใน select นะคะ เพราะถ้าเรานำไปเป็นเงื่อนไขก็ต้องนำไปอยู่ที่ where ... ได้เช่นเดียวกัน ที่ยกตัวอย่างขึ้นมาเพื่อให้แสดงผลออกมาให้เห็นนะคะ

เอาเป็นว่าวันนี้ก็พอก่อนนะคะ เดี๋ยวมีคำถามก็ฝากไว้ได้ค่ะ และหวังว่าจะเกิดกับโยชน์กับทุกคนที่สนใจค่ะ หรือมีอะไรชี้แนะก็ส่งมาได้เสมอค่ะ

วันเสาร์ที่ 12 มิถุนายน พ.ศ. 2553

เรามาเขียนรายงานจากฐานข้อมูล 18 แฟ้ม กันดีกว่า ตอนที่2

555 ในที่สุดก็เดือนละ 1 ครั้ง เฮ้ยยยยย... ขออภัยอย่างแรงค่ะ

งั้นเริ่มเลยนะ ความเดิม
"ทำความเข้าใจกันก่อนดีกว่าว่า 18 แฟ้ม คือข้อมูลอะไรบ้าง และ เราจะจัดการข้อมูลให้เป็นชุดเดียวกันได้อย่างไร มีรูปแบบข้อมูลเป็นอย่างไร ก็เชิงการวิเคราะห์อะค่ะ ไม่ยาก แต่ค่อนข้างละเอียดอ่อน"

จากที่เคยกล่าวไว้เรื่องการจัดการชุดข้อมูลเดียวกัน ตอนนี้เราลองหยิบเอกสาร 18 แฟ้ม จาก file Structure2553_1_0_7.pdf(อ้างอิง: http://www.nhso.go.th/op)

เมื่อเราได้เอกสารชุดเดียวกันแล้ว มาตรฐานข้อมูลที่เราจะกล่าวถึงก็จะเป็นข้อมูลชุดเดียวกันแล้วค่ะ งั้นพร้อมแล้วก็มาทำความเข้าใจกันเลยค่ะ เอาแบบคุยกันง่ายๆ ไม่ทางการนะคะ
1. 18 แฟ้ม คือข้อมูลอะไรบ้าง
ให้อ่านรายละเอียดของแฟ้มทั้งหมดดูว่าเข้าใจว่าอย่างไร เหมือนกับที่จะสรุปหรือไม่
ตามความหมายของแต่ละแฟ้มแยกกลุ่มข้อมูลได้ ดังนี้
- ข้อมูลพื้นฐานประชากร หรือข้อมูลทั่วไปของประชากร
PERSON, DEATH, CHRONIC, SURVEIL, WOMAN, HOME
- ข้อมูลหลักประกันสุขภาพ
PERSON, CARD
- ข้อมูลการเข้ารับบริการ
PERSON, SERVICE, DIAG,PROCED,APPOINT,DRUG
- ข้อมูลส่งเสริมป้องกัน
PERSON, SERVICE,EPI,NUTRI, FP, ANC, PP, MCH
2. การจัดการข้อมูล 18 แฟ้ม
2.1 เราได้ข้อมูลมาในรูปแบบใด
ในที่นี้ก็คงจะยึดมาตรฐานจากที่ทางส่วนกลางกำหนดไปเลยนะคะ เป็น Text File รูปแบบอ้างอิงจากเอกสาร Structure2553_1_0_7.pdf
2.2 เราจะจัดเก็บข้อมูลมูลไว้ที่ไหนดี
เมื่อเราทราบว่าข้อมูลที่เรามีในมือนั้นเป็น Text File แล้วเราจะเอาไว้ที่ไหนดี อันนี้แนะนำให้เก็บอยู่ในฐานข้อมูลค่ะ ซึ่งตรงนี้ขอยกตัวอย่างเป็น PostgreSQL แล้วกันค่ะ เป็น Open Source ที่ดีค่ะ สำหรับท่านอื่นๆ จะใช้ฐานข้อมูลอะไรก็ได้นะคะ
2.3 เราได้ข้อมูลมาบ่อยแค่ไหน ซ้ำซ้อนหรือไม่
อันนี้ก็คาดว่า 1 เดือนเราก็คง Query ข้อมูลมา 1 ครั้ง หรือแล้วแต่ว่าจะมีกรณีแก้ไขข้อมูลแล้วนำเข้ามาใหม่หรือไม่ อันนั้นก็ไม่ว่ากันค่ะ บ่อยแค่ไหนไม่ค่อยสำคัญมากนัก แต่ข้อมูลยังไงเราก็ไม่สามารถการันตีได้ว่าจะไม่ซ้ำซ้อนกัน ดังนั้น เราก็ต้องมีขั้นตอนที่จะตรวจสอบความซ้ำซ้อนของข้อมูลให้ได้ และจะจัดการกับข้อมูลเหล่านั้นอย่างไร ให้เหมาะสม
2.4 เราต้องจัดการกับข้อมูลใน CUP ด้วยหรือไม่
ยากเข้าไปอีกขั้น เพราะเราไม่สามารถควบคุมข้อมูลได้ แต่ก็สามารถทำได้ค่ะ ต้องทำความเข้าใจร่วมกันก่อนว่าข้อมูลทั้งหมดจะนำมาวิเคราะห์ร่วมกัน ดังนั้น Key ที่ใช้เป็นตัวบ่งชี้ถึงบุคคล หรือประชากรคนเดียวกันที่สำคัญ คือ เลขบัตรประชาชน แต่เราไม่ได้ วิเคราะห์ข้อมูลที่เป็นคนไทยอย่างเดียว ดังนั้น เราจำเป็นจะต้องมีข้อมูลอื่นร่วมด้วย ที่จริงแล้วเคยได้มีโอกาสเข้าร่วมประชุมค่ะ

ตรงนี้ทางหน่วยงานที่เกี่ยวข้องกับข้อมูลต่างด้าวก็พยาบามที่จะให้สร้างเลข 13 หลักของคนต่างด้าวขึ้นมา ถ้าเกิดขึ้นมาเหมือนกับที่ให้เลข 13 หลักของคนไทยก็น่าจะนำมาใช้ได้ค่ะ แต่ไม่แน่ใจว่าเลขที่ให้ Gen ขึ้นมานั้น หาก นาย ก ชาวพม่า ไปรับบริการ 2 หน่วยบริการจะได้เลขเดียวกันหรือไม่ (อันนี้ก็ค่อยหาทางออกอีกทีนะคะ 555 ด้วยอะไรหลายๆอย่างทำให้ไม่สะดวกค่ะ) สรุปคือว่า เอาคนไทยก่อนนะ เอาไว้ว่างๆจะหาทางให้ละกัน

งั้นเรามาสร้างโครงสร้างรองรับกับข้อมูลกันดีกว่าค่ะ
จากเอกสารที่เรานำมาอ้างอิง อย่างน้อยที่สุดนั้นเราจะต้องมีตารางข้อมูลเท่ากับจำนวนแฟ้มค่ะ แต่ส่วนที่ต้องเพิ่มขึ้นมาเพื่อให้อธิบายรหัสต่างๆของข้อมูล จะต้องดูว่าแต่ละแฟ้มมีรหัสอ้างอิงไปยังข้อมูลอะไรบ้าง ยกตัวอย่างเช่น เราหยิบแฟ้ม PERSON ขึ้นมา เราจะได้ตาราง ดังนี้
1. ตาราง person
    เพื่อเก็บข้อมูลประชากร
2. ตาราง address
    เพื่อเก็บข้อมูลรหัสของ ตำบล อำเภอ จังหวัด ตามมาตรฐานของกรมการปกครอง
3. ตาราง mstatus
    เก็บสถานภาพสมรส
4. ตาราง occupation
    เก็บรหัสอาชีพ
5. ตาราง race
    รหัสเชื้อชาติมาตรฐานตามกรมการปกครอง
6. ตาราง nation
    รหัสสัญชาติมาตรฐานตามกรมการปกครอง
7. ตาราง religion
    รหัศาสนาสมาตรฐาน สำนักนโยบายและยุทธศาสตร์
8. ตาราง education
    รหัสการศึกษามาตรฐาน สำนักนโยบายและยุทธศาสตร์
9. ตาราง fstatus
    สถานะในครอบครัว
10. ตาราง discharge_status
    สถานะสาเหตุการจำหน่าย
11. ตาราง blood_group
    หมู่เลือด
12. ตาราง labor
    รหัสความเป็นคนต่างด้าว
13. ตาราง typearea
     สถานะบุคคล

จะเห็นว่าเพียง 1 แฟ้มทำให้เรามีงานทำเพื่อสร้างตารางถึง 13 ตารางค่ะ แต่เราก็ต้องดูว่ารหัสนี้ใช้กับแฟ้มอื่นด้วยหรือเปล่าถ้าใช้ก็ไม่ต้องสร้างซ้ำซ้อนค่ะ

งั้นมาทำตามขั้นตอนค่ะ
1. สร้างฐานข้อมูล 1 ฐาน
ตัวอย่าง คำสั่ง
createdb -E unicode dataset_18files -U postgres
เท่านี้เราก็ได้ฐานข้อมูลเปล่ามา 1 ฐานชื่อ dataset_18files
สำหรับฐานข้อมูลอื่นๆ ก็สามารถสร้างตามสะดวกค่ะ หรือแม่แต่ PostgreSQL ก็ใช้ Tool สร้างก็ได้นะคะ หากไม่ถนัด Command
2. สร้างตารางที่เรา List เอาไว้
ตัวอย่าง คำสั่ง
CREATE TABLE blood_group (
    stdcode    varchar(255) NOT NULL,
    description    varchar(255) NULL,
    PRIMARY KEY(stdcode,description)
);
จะได้ตารางชื่อ blood_group
ประกอบด้วย 2 Field คือ stdcode,description
โดยมี Key หลักคือ stdcode
* โดยส่วนใหญ่แล้วถ้ากรณีรหัสมาตรฐานก็น่าจะมีอย่างน้อย 2 Field คือ รหัส และ รายละเอียดคำอธิบาย
3. นำข้อมูลรหัสมาตรฐาน เข้าไปยังตารางที่สร้างไว้
หากเป็นข้อมูลรหัสมาตรฐานจะคงที่ไม่มีการเปลี่ยนแปลง กรณีเปลี่ยนแปลง เช่น เมื่อมีการปรับปรุงรหัสข้อมูลใหม่ นั่นหมายถึงการเปลี่ยน Data Dictionary นั่นเอง เราควรจัดการให้เนียนกว่าการมาเปลี่ยนรหัสโดยตรงค่ะ เพราะจะกระทบกับข้อมูลเดิม (ค่อยว่ากันนะคะ ชักยุ่งเหยิง)
ตัวอย่าง คำสั่ง
INSERT INTO blood_group(stdcode, description)
    VALUES('1', 'A');
INSERT INTO blood_group(stdcode, description)
    VALUES('2', 'B');
INSERT INTO blood_group(stdcode, description)
    VALUES('3', 'AB');
INSERT INTO blood_group(stdcode, description)
    VALUES('4', 'O');

เอาไว้ก่อนนะ ไปทำกับข้าวก่อนค่ะ พอดีโกชอว์(สามี)กลับมาแล้วค่ะ
ลองสร้างตารางเล่นๆกันไปก่อนนะคะ หากใครทำเสร็จก่อนก็นำมาเผยแพ่เพื่อช่วยกันตรวจสอบได้ค่ะ

หากไม่แน่ใจ หรือมีคำถามก็แจ้งกันมาได้ค่ะ ... ช่วยกัน