มาดูประเด็นของเมื่อวาน
SELECT vce.emp_id , vce.emp_name, da.date, da.stamp_inout, dl.leave_type, dl.leave_hours
FROM ((v_current_employee vce INNER JOIN d_attendance da ON (vce.emp_id = da.emp_id))
LEFT JOIN d_leave dl ON (vce.emp_id = dl.emp_id and da.date = dl.date))
WHERE (da.date between '2014-01-25' and '2014-02-08')
AND (dl.date between '2014-01-25' and '2014-02-08')
ประเด็นที่ Mr.X เขาอธิบายมีดังต่อไปนี้
– เขาใช้ d_attendance left join กับ d_leave ตาม key ที่ระบุ เพื่อจะได้ดูว่าวันนั้นถ้าคนคนนั้นไม่มีข้อมูล stamp_inout (ค่าว่าง) ก็จะได้ดูว่าคนนั้นขอลางานไว้หรือเปล่า ที่ทำ left join นั้นเพราะว่า ตารางข้อมูลทางฝั่งซ้ายคือ d_attendance นั้นมีข้อมูลแน่ๆ ไม่ว่าคนนั้นจะบันทึกการเข้าทำงานหรือไม่ แต่ฝั่งขวาคือ d_leave จะมีข้อมูลก็ต่อเมื่อมีการบันทึกวันลาไว้ก่อนแล้ว
– เขาใช้เงื่อนไข da_date ระหว่าง ‘2014-01-25’ และ ‘2014-02-08’ เพื่อให้ได้เวลาการทำงานตามช่วงเวลาที่กำหนด
– เขาใช้เงื่อนไข dl_date ระหว่าง ‘2014-01-25’ และ ‘2014-02-08’ เพื่อให้ได้เวลาของข้อมูลการลางานตามช่วงเวลาที่กำหนด
อธิบายมาแบบนี้ก็น่าจะถูกแล้วใช่ไหมครับ 🙂 แล้วทำไมคำตอบมันไม่ถูกอ่ะ?
นี่คืออีกตัวอย่างนึงที่ programmer ไม่ค่อยคำนึงถึงค่า Null value ครับ (ย้อนกลับไปอ่านอันนี้น่าจะเห็นภาพ https://mairai.wordpress.com/2014/01/22/is-distinct-from-postgresql/)
หลังจากการทำ left join แล้ว ข้อมูล d_attendance มันก็จะจับคู่กับ ข้อมูล d_leave ตาม emp_id และ date ถ้าไม่มีข้อมูลใน d_leave ที่ match กันได้ ผลลัพธ์ทางฝั่ง d_leave มันก็จะเป็น null ทั้ง record
ดังนั้น หลังจาที่ join เสร็จแล้วก็มาตัดเงื่อไขออกตาม WHERE clause
เงื่อนไข da.date between ‘2014-01-25’ and ‘2014-02-08’ มันก็ filter ได้แบบไม่มีปัญหา เพราะทุก record มันมีค่ามาให้เปรียบเทียบอยู่แล้ว
แต่ dl.date between ‘2014-01-25’ and ‘2014-02-08’ นี่สิ …. บาง record ก็เป็น null บาง record ก็มีค่า เห็นแล้วก็ร้องอ๋อแล้วใช่ไหมครับ มันก็ Filter เอาเฉพาะวันที่ที่มีค่าและมีค่าอยู่ระหว่าง ‘2014-01-25’ and ‘2014-02-08’ ไง แล้วมันก็ไม่เอาอันที่มีค่า null ด้วย ซึ่งนั่นเป็นสาเหตุให้ข้อมูลผลลัพธ์มันผิดปกติ
แล้วที่ถูกต้องทำยังไง ต้องเพิ่มเงื่อนไข OR (dl.date is null) เข้าไปเหรอ?
ใช่ครับ … แต่ถ้าลองพิจารณาดีๆ นะ
( dl.date between ‘2014-01-25’ and ‘2014-02-08) OR (dl.date is null) ก็คือข้อมูลที่เป็นไปได้ทุกตัวของ d_leave อยู่แล้ว (ลองนึกภาพแผนภาพเวนน์ เงื่อนไขนี้กำลังจะบอกว่าเอาอันที่มีค่าทั้งหมดหรือไม่มีค่า ซึ่งก็คือมันเอาทุกตัวนั่นแหล่ะ)
ดังนั้น เงื่อนไข นี้จะใส่หรือไม่ใส่ค่ามันเท่ากัน ดังนั้นถ้าตัดเงื่อนไขเหลือแค่เนี้ยก็น่าจะถูกแล้ว จริงมะ
SELECT vce.emp_id , vce.emp_name, da.date, da.stamp_inout, dl.leave_type, dl.leave_hours
FROM ((v_current_employee vce INNER JOIN d_attendance da ON (vce.emp_id = da.emp_id))
LEFT JOIN d_leave dl ON (vce.emp_id = dl.emp_id and da.date = dl.date))
WHERE (da.date between '2014-01-25' and '2014-02-08')