Posted by: siamnobita | 03/30/2009

Knowing The Question

มีคนใกล้ตัวบางคน (ที่ผมบังคับให้เข้ามาอ่าน) บอกว่า ประโยคนี้ในหัวข้อก่อน อ่านแล้วไม่เข้าใจ

นอกจากนั้นข้อแตกต่างเล็ก ๆ น้อย ๆ ระหว่าง query 2 query จะเกิดขึ้นกับความเป็นไปได้ของข้อมูลบางชุดเท่านั้น เราต้องเข้าใจถึงลักษณะของข้อมูลที่เรามีอยู่ด้วย

นั่นสิ ผมอ่านที่ตัวเองเขียนอีกรอบ ก็เห็นด้วยกับเขาเหมือนกัน เพราะฉะนั้นคราวนี้ผมจะลองขยายความเพิ่มเติม พร้อมกับยกตัวอย่างที่หลายคนน่าจะเคยเจอกันมาบ้าง ให้ดูด้วย

จริง ๆ ครั้งที่แล้ว ผมตั้งใจจะบอกว่า ในวิธีการเขียนของ SQL ที่หลากหลายนั้น บาง syntax ให้ผลลัพธ์ที่คล้ายคลึงกันมาก จนบางครั้ง คนบางคนเข้าใจผิดคิดว่าผลลัพธ์ที่ได้เหมือนกันเปี๊ยบเลยก็มี ทั้งนี้เป็นเพราะความแตกต่างที่จะเกิดขึ้นนั้น จะเกิดในกรณีที่ข้อมูลมีลักษณะเฉพาะบางประการเท่านั้น ผลที่ตามมาคือ oracle เลือกใช้วิธีการที่แตกต่างกันอย่างสิ้นเชิงในการหาผลลัพธ์ออกมาให้กับเรา และมีผลกับความเร็วโดยตรง

อย่างไรก็ดี เราในฐานะที่เป็นเจ้าของข้อมูล เราอาจจะรู้และรับรองได้ว่าข้อมูลในลักษณะนั้น ๆ ไม่มีทางเกิดขึ้นได้ในข้อมูลจริง (พูดง่าย ๆ บางครั้ง oracle ก็คิดมากและฉลาดเกินไป) เราจึงต้องรับผิดชอบโดยการเลือกวิธีการเขียนที่เหมาะสมกับข้อมูลของเราจริง ๆ หรือบอกให้ oracle รับรู้ข้อเท็จจริงเดียวกับเราโดยผ่าน statistics หรือ integrity constraints (โดยเฉพาะเรื่อง constraints นี้ ผมคงใช้เขียนได้อีกหลายวันทีเดียว)

ผมขอยกตัวอย่างง่าย ๆ ให้พิจารณาสักตัวอย่างสองตัวอย่าง ตัวอย่างแรกเป็นการใช้ not in, not exists และ minus

select t1_id
from t1
where t1_id not in ( select t2_id from t2 ) ;

select t1_id
from t1
where not exists
( select null from t2 where t2_id = t1.t1_id ) ;

select t1_id
from t1
minus
select t2_id
from t2 ;

สาม statement นี้ ดูเผิน ๆ จะเหมือนว่าได้คำตอบเดียวกัน แต่จริง ๆ แล้วไม่ใช่ ผมสามารถแสดงให้ดูด้วยชุดข้อมูลดังนี้

insert into t1 (t1_id) values (1) ;
insert into t1 (t1_id) values (1) ;
insert into t1 (t1_id) values (2) ;
insert into t2 (t2_id) values (2) ;
insert into t2 (t2_id) values (null) ; 

ข้อมูลบรรทัดที่สอง จะทำให้ minus ให้ผลลัพธ์ที่แตกต่าง เพราะ t1_id มีค่าซ้ำ ขณะที่ผลลัพธ์จาก set operators ทุกตัวยกเว้น union all จะถูกกำจัดค่าซ้ำก่อนเสมอ (select distinct … ประมาณนั้น) จึงได้ผลลัพธ์เพียง 1 แถว ส่วน not in กับ not exists ซ้ำก็ซ้ำ ไม่สนใจ แสดงทั้งหมด ส่วนข้อมูลบรรทัดที่ห้า จะทำให้ not in ให้ผลลัพธ์ที่แตกต่าง เพราะถ้าใช้ not in แล้ว ค่าที่ return จาก subquery ต้องไม่มีค่า null มิฉะนั้น ผลลัพธ์จะเป็น 0 แถวเสมอ ดังนั้นผลลัพธ์จากสาม query เมื่อกระทำกับข้อมูลชุดนี้แล้ว จะได้ไม่เท่ากันเลย

อย่างไรก็ดี ถ้าทั้งคอลัมน์ t1_id และ t2_id เป็น primary key ของตาราง t1 และ t2 ตามลำดับ จะเห็นได้ว่าข้อมูลชุดที่เรายกมาไม่มีทางเป็นจริงไปได้ เพราะ primary key จะต้องไม่ซ้ำและมีค่าเสมอ เป็น null ไม่ได้

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

ตัวอย่างที่สอง หลายคนอาจจะเคยใช้อยู่บ่อย ๆ และสงสัยว่าแบบไหนดีกว่ากัน

select *
from t1
where :p_id is null or id = :p_id  ;

select *
from t1
where id = nvl(:p_id, id) ;

สอง query มักถูกใช้เมื่อเราต้องการสร้างรายงาน ที่ผู้เรียกดูรายงานสามารถที่จะระบุ หรือไม่ระบุค่าพารามิเตอร์ก็ได้ ถ้าไม่ระบุก็ให้แสดงข้อมูลทั้งหมด คุ้น ๆ บ้างไหมครับ รู้ไหมครับว่า สอง query นี้แตกต่างกันตรงไหน

ค่า null อีกแล้วครับท่าน กติกาของ SQL ข้อหนึ่ง null ไม่เคยเท่ากับอะไรเลย แม้แต่ตัวมันเอง เงื่อนไข (null = null) จะให้ค่าเป็น null ไม่ใช่ true

ดังนั้นหากเราไม่ระบุค่าพารามิเตอร์ และในข้อมูลของเรา มีแถวที่ id มีค่าเป็น null รวมอยู่ด้วย query แรกจะแสดงผลทุกแถวจริง ๆ รวมทั้งแถวที่ id เป็น null ขณะที่ query ที่สองจะแสดงผลแค่แถวที่ id มีค่าเท่านั้น แถวที่ id เป็น null จะไม่แสดงออกมา

เป็นอย่างไรบ้างครับ ตัวอย่างที่ยกมาพอจะทำให้เห็นภาพไหมครับ ว่าเราต้องรู้อะไรบ้างก่อนจะทำการ tune query นอกจากนี้แล้วยังมีอีกหลายกรณี แต่วันนี้ผมยังนึกไม่ออก เอาไว้วันไหนนึกได้ แล้วจะกลับมาเล่าสู่กันฟังอีกรอบนะครับ

ด้วยความเคารพอย่างสูง


ใส่ความเห็น

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / เปลี่ยนแปลง )

Twitter picture

You are commenting using your Twitter account. Log Out / เปลี่ยนแปลง )

Facebook photo

You are commenting using your Facebook account. Log Out / เปลี่ยนแปลง )

Google+ photo

You are commenting using your Google+ account. Log Out / เปลี่ยนแปลง )

Connecting to %s

หมวดหมู่

%d bloggers like this: