Mine

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

วันอังคารที่ 28 กันยายน พ.ศ. 2553

มาทำความเข้าใจคำว่า Group By ขึ้นพื้นฐาน

สวัสดีค่ะ ช่วงนี้อากาศเปลี่ยนแปลงบ่อย ยังไงก็รักษาสุขภาพกันด้วยนะคะ ...

วันนี้เราจะมาว่าด้วยการ Group By ใน Query ค่ะ สำหรับคำจำกัดความภาษาบ้านๆ ก็น่าจะเป็น "การรวมข้อมูลเป็น Record เดียวกัน โดยที่ข้อมูลใน Field ทุกๆ Field จะ้ต้องเหมือนกัน" ยกตัวอย่างข้อมูลที่สามารถ Group By ให้เป็น Record เดียวกันได้ ดังนี้
ตาราง test_table
field_a field_b field_c
123ทดสอบ1
123 ทดสอบ 2
123ทดสอบ2
123 ทดสอบ 2
123ทดสอบ3

หากต้องการสร้างตารางเพื่อทดสอบข้อมูลให้ใช้คำสั่ง
CREATE TABLE test_table(
field_a varchar(255) NOT NULL,
field_b varchar(255) NOT NULL,
field_c varchar(255) NOT NULL
);
หลังจากนั้น INSERT ข้อมูลจากคำสั่ง
INSERT INTO test_table(field_a,field_b,field_c) VALUES('123','ทดสอบ','1');
INSERT INTO test_table(field_a,field_b,field_c) VALUES('123','ทดสอบ','2');
INSERT INTO test_table(field_a,field_b,field_c) VALUES('123','ทดสอบ','2');
INSERT INTO test_table(field_a,field_b,field_c) VALUES('123','ทดสอบ','2');
INSERT INTO test_table(field_a,field_b,field_c) VALUES('123','ทดสอบ','3');

หลังจากที่รันคำสั่ง CREATE TABLE และ INSERT INTO แล้ว เราก็จะได้ข้อมูลดังตารางข้างต้น

จากข้อมูลข้างต้นของตาราง test_table ถ้าเราเขียนคำสั่ง SQL แล้วทำการ Group By แล้วจะได้อะไร ลองมาดูจากคำสั่ง 3 ชุด
ชุดที่ 1 : เลือกแสดงข้อมูลทุก Field
SELECT field_a, field_b, field_c
FROM
test_table
GROUP BY field_a, field_b, field_c;

จากคำสั่งข้างต้นเราทำ GROUP BY field_a, field_b, field_c นั่นคือ ในแต่ละ Record(แถวของข้อมูล) จะต้องมีค่าแต่ละ Field เท่ากัน ทุก Field ในตัวอย่าง
field_a มีค่า '123' เหมือนกันทุก Record
field_b มีค่า 'ทดสอบ' เหมือนกันทุก Record
แต่ field_c มีความแตกต่างกัน โดยจะมีค่า เป็น '1' ,'2' หรือ '3'
ดังนั้น Record ที่เหมือนกันทุก Field จะรวมเป็น Record เดียวกัน

ผลลัพธ์ที่ได้ คือ
field_afield_bfield_c
123ทดสอบ1
123ทดสอบ2
123ทดสอบ3

ชุดที่ 2 : เลือกแสดงบาง Field
SELECT field_a, field_b
FROM
test_table
GROUP BY field_a, field_b;

จากคำสั่งข้างต้นเราทำ GROUP BY field_a, field_b นั่นคือ ในแต่ละ Record จะต้องมีค่าแต่ละ Field เท่ากัน ทุก Field ที่ทำ GROUP BY ในตัวอย่าง
field_a มีค่า '123' เหมือนกันทุก Record 
field_b มีค่า 'ทดสอบ' เหมือนกันทุก Record

ผลลัพธ์ที่ได้ คือ
field_afield_b
123ทดสอบ

ชุดที่ 3
: เลือกแสดงข้อมูลบาง Field แต่ Group By ทุก Field
SELECT field_a, field_b
FROM
test_table
GROUP BY field_a, field_b, field_c;

จากคำสั่งข้างต้นเราทำ GROUP BY field_a, field_b, field_c นั่นคือ ในแต่ละ Record จะต้องมีค่าแต่ละ Field เท่ากัน ทุก Field ที่ทำการ Group By ในตัวอย่าง
field_a มีค่า '123' เหมือนกันทุก Record
field_b มีค่า 'ทดสอบ' เหมือนกันทุก Record
แต่ field_c มีความแตกต่างกัน โดยจะมีค่า เป็น '1' ,'2' หรือ '3'
ดังนั้น Record ที่เหมือนกันทุก Field จะรวมเป็น Record เดียวกัน

ผลลัพธ์ที่ได้ คือ
field_afield_b
123ทดสอบ
123ทดสอบ
123ทดสอบ

* จากคำสั่งชุดที่ 3 ถึงแม้การแสดงผลจะแสดงออกมาไม่ครบทุก Field แต่ข้อมูลที่เรานำมา Group By ก็สามารถ นำ Filed ที่ไม่แสดงผลมา Goup By ด้วยก็ได้ อันนี้ก็ขึ้นอยู่กับผลลัพธ์ที่เราต้องการ

สิ่งที่ควรจำสำหรับการทำ Group By
- เราจำเป็นต้องนำ Field ที่เรา Select(แสดงผล) มา Group By ทุก Field เสมอ
- เราสามารถนำ Field อื่นที่ไม่แสดงผลมาร่วม Group By ได้ (จากตัวอย่าง ชุดคำสั่งที่ 3)
- Field ที่เราเขียน Group By ทุก Field จะถูกนำมาคิดร่วมกันเพื่อเช็คข้อมูลกลุ่มเดียวกัน นั่นคือค่าของทุก Field ต้องมีค่าเท่ากันทั้งหมด จึงจะมองว่าเป็น Record เดียวกัน 

ทิ้งท้ายซักนิด
ในบางครั้งถ้าเราต้องการทำ Group By เท่ากับ Field ที่เรา Select เราอาจจะเขียนแทนด้วย Distinct ก็ได้ค่ะ
ตัวอย่าง
จากชุดคำสั่งที่ 1
SELECT field_a, field_b, field_c
FROM
test_table
GROUP BY field_a, field_b, field_c;
เราอาจจะเขียนแทนเป็น

SELECT DISTINCT field_a, field_b, field_c
FROM
test_table;

ซึ่งจะได้ผลลัพธ์ เหมือนกัน พิเศษหน่อยตรงที่ Distinct จะเรียงลำดับให้เราด้วยค่ะ แล้วค่อยมาว่ากันต่อ กับ Group By ขั้นต่อไป และ Distinct สามารถทำงานให้ประสิทธิภาพมากกว่านี้ค่ะ ...โปรดติดตามตอนต่อไป

วันจันทร์ที่ 13 กันยายน พ.ศ. 2553

การเขียน SQL แบบอ้างอิงตารางเดียวกันแต่คนละข้อมูล

สวัสดีค่ะ วันนี้แอบเข้าไปดูในสถิติของตนเอง พบว่ามีคนค้นหาทาง Google ด้วยค่ะ คริคริ ...แล้วก็แอบมาคลิก web ของเราด้วย แสดงว่าที่เราเขียนไปก็ไม่เสียเปล่า (มีความสุขนะเนี่ยะ)

แล้วก็พบว่าส่วนหนึ่งของคำที่ค้นหา คือ "postgresql อ้างอิงตารางเดียวสองครั้ง" 
ทำให้กลับมาคิดว่า เราน่าจะเอาความรู้พื้นฐานมาเขียนบ้าง เพราะว่า บางทีเอาตัวอย่างการใช้งานจริงมาเขียนจะยากไปสำหรับคนที่เข้ามาศึกษาใหม่ แล้วอาจจะไม่เกิดประโยชน์ก็ได้ ดังนั้นวันนี้ก็ขอเอาใจกับคนที่ Search จากข้อความข้างต้นนะคะ ไม่รู้ว่าจะเข้ามาอ่านอีกหรือเปล่า

สำหรับการเขียน Query หรือ เขียน SQL นั้นโดยส่วนใหญ่ภาษาที่ใช้ในแต่ละฐานข้อมูลก็จะเหมือนกัน แต่อาจจะต่างกันบางคำสั่ง หรือบาง Function ค่ะ ในที่นี้ขออ้างอิง PostgreSQL แล้วกันนะ (ก็นู๋ถนัดอันนี้นี่นา)

รูปแบบการเขียน Query พื้นฐาน
select fieldName
from tableName
where condition

ส่วนคำสั่งพื้นฐานอื่นๆ จะไม่ขอกล่าวถึงใน บทความเรื่องนี้ นะคะ เอาไว้จะหาโอกาสเขียนให้อีกครั้งละกันค่ะ

postgresql อ้างอิงตารางเดียวสองครั้ง 

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


select t_patient_id
,t_patient.patient_hn
,t_patient.patient_firstname
,t_patient.patient_lastname
,t_patient.patient_birthday
,t_patient.patient_house
,t_patient.patient_road
,t_patient.patient_moo
--เวลาอ้างถึงเพื่อแสดงผล หรือเพื่อนำไปใช้เป็นเงื่อนไขก็ต้องอ้างชื่อที่เราตั้งใหม่นะคะ
--หากเราอ้างถึง f_address แล้วละก็ฐานข้อมูลจะมองเป็นตารางที่ 4 ทันทีเลย ทีนี้ข้อมูลเราเบิ้ลแน่ๆค่ะ เพราะไม่มีการ join

,district.address_description
as district
,amphur.address_description
as amphur
,province.address_description
as province
from t_patient
inner join f_address as district --ตรงนี้ตั้งชื่อเล่นใหม่ค่ะเป็น district
    on t_patient.patient_tambon = district.f_address_id
 
inner join f_address as amphur --ตรงนี้ตั้งชื่อเล่นใหม่ค่ะเป็น amphur
    on t_patient.patient_amphur = amphur.f_address_id
 inner join f_address as province
--ตรงนี้ตั้งชื่อเล่นใหม่ค่ะเป็น province
    on t_patient.patient_changwat = province.f_address_id
where t_patient.patient_active = '1'
    and t_patient.patient_record_date_time like '2553%'
limit 100;



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

วันจันทร์ที่ 6 กันยายน พ.ศ. 2553

โรคเรื้อรังครั้งแรกหรือไม่ ....

ไหนๆก็ไหนๆ แล้วนำมาฝากกันเพิ่มเติมอีกนิดค่ะ เพราะว่าลืมนึกถึงไปว่าโรคเดียวกันแต่มีหลาย ICD10 ดังนั้น รอบก่อนที่เขียนตาม ICD10 น่ะ ไม่ค่อยเป็นประโชย์มากนัก


รอบนี้เลยขอแก้ตัวใหม่ค่ะ ดูตามโรคกันไป แต่ในที่นี้ขอยกโรคเรื้อรัง ละกันนะคะ เพราะฐานข้อมูล Hospital OS จัดเก็บ โรคเรื้อรังไว้ค่ะ เราสามารถทราบได้ว่า ICD10 ไหนเป็นโรคเรื่อรังอะไรจากข้อมูลตรงนี้ค่ะ

เอาคำสั่งนี้ไปรัน นะคะ (รันครั้งเดียวก็พอ) 

 CREATE OR REPLACE FUNCTION check_new_disease(varchar, varchar, varchar, varchar,varchar) RETURNS bool AS
'
declare
    patient_id varchar;
    visit_id varchar;
    visit_icd10 varchar;   
    visit_datetime varchar;
    disease varchar;
    newvisit boolean;
    check_visit record;
begin
    patient_id = $1;
    visit_id = $2;
    visit_icd10 = $3;
    visit_datetime = $4;
    disease = $5; 
    newvisit = true;
  
    if (patient_id <> '''' and visit_id <> '''' and visit_icd10 <> '''' and disease <> '''' and visit_datetime <> '''')
    then  
            for check_visit in (select t_visit.t_visit_id as t_visit_id
                                        from t_visit inner join t_diag_icd10
                                                on t_visit.t_visit_id = t_diag_icd10.diag_icd10_vn and t_diag_icd10.diag_icd10_active = ''1''
                                                inner join b_group_icd10
                                                on upper(substring(b_group_icd10.group_icd10_number,1,3)) = upper(substring(t_diag_icd10.diag_icd10_number,1,3))
                                                 inner join b_group_chronic
                                                on b_group_icd10.b_group_chronic_id = b_group_chronic.b_group_chronic_id
                                        where t_visit.t_visit_id <> visit_id
                                                and t_visit.t_patient_id = patient_id
                                                and b_group_chronic.b_group_chronic_id = disease
                                                and t_visit.f_visit_status_id <> ''4''
                                                and substring(t_visit.visit_financial_discharge_time,1,10) < substring(visit_datetime,1,10))        
            loop
                if check_visit.t_visit_id is not null
                then newvisit = false;
                else
                --กรณี Join กันไม่สำเร็จ
                end if;
            end loop;
    else      newvisit = true;       
    end if;
    return newvisit;
end;
'
LANGUAGE 'plpgsql';

ตัวอย่างการนำไปใช้ ดังนี้ค่ะ
SELECT t_visit.t_visit_id
,t_visit.t_patient_id
,t_diag_icd10.diag_icd10_number
,b_group_chronic.b_group_chronic_id
,b_group_chronic.group_chronic_number
,b_group_chronic.group_chronic_description_th
--เรียกใช้ตรงนี้
,check_new_disease(t_visit.t_patient_id,t_visit.t_visit_id,t_diag_icd10.diag_icd10_number,t_visit.visit_financial_discharge_time,b_group_chronic.b_group_chronic_id)
FROM t_visit inner join t_diag_icd10
on t_visit.t_visit_id = t_diag_icd10.diag_icd10_vn and t_diag_icd10.diag_icd10_active = '1'
inner join b_group_icd10
on upper(substring(b_group_icd10.group_icd10_number,1,3)) = upper(substring(t_diag_icd10.diag_icd10_number,1,3))
inner join b_group_chronic
on b_group_icd10.b_group_chronic_id = b_group_chronic.b_group_chronic_id
where t_visit.f_visit_status_id <> '4'
and substring(t_visit.visit_financial_discharge_time,1,10) = '2553-04-01';


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