DISTINCT ข้อมูลใน subquery เพื่อส่งให้กับ IN จำเป็นหรือไม่

มีข้อสงสัยจากโพสต์ว่า ถ้าเราต้องใช้คำสั่ง IN ร่วมกับ subquery เราควรกำหนด DISTINCT ให้กับผลลัพท์ของ subquery หรือไม่
SELECT * FROM table_name WHERE id IN (SELECT id FROM table_name2)

ก่อนอื่น อธิบายคำสั่ง IN กันก่อน
id IN (1, 2, 3)

คำสั่ง IN ถ้าแปลเป็นคำสั่งพื้นฐานจะได้ว่า
id = 1 OR id = 2 OR id = 3

และคำสั่ง NOT IN
id NOT IN (1, 2, 3)

ทำนองเดียวกัน คำสั่ง NOT IN เมื่อแปลเป็นคำสั่งพื้นฐานจะได้ว่า
!(id = 1 OR id = 2 OR id = 3)

มีบางคนอาจจะเข้าใจว่ามันเป็นรูปแบบนี้
id != 1 AND id != 2 AND id != 3

ซึ่งก็ทำงานได้ผลลัพท์เดียวกันนะครับ เพียงแต่แบบ OR จะทำงานเข้ากับรูปแบบของคำสั่งมากกว่า

ทีนี้มีคำถามว่า เราควรจำกัดผลลัพท์ของ subquery ให้เป็น DISTINCT (มีผลลัพท์ที่ไม่ซ้ำกันหรือไม่) ก่อนอื่น ผมจะอธิบายขั้นตอนการทำงานของ DISTINCT (รวมถึง GROUP BY) กันก่อนว่ามันทำอะไรถึงจะได้ผลลัพท์ที่ไม่ซ้ำกันออกมา
SELECT DISTINCT id FROM table_name2;
SELECT id FROM table_name2 GROUP BY id;

ทั้งสองคำสั่งด้านบน จะให้ผลลัพท์เป็นข้อมูลที่ไม่มี id ซ้ำกันเลยออกมา ซึ่งสามารถใช้กรองผลลัพท์ให้น้อยลงได้ โดยมีขั้นตอนการทำงานดังนี้
  1. เมื่อ query ได้ผลลัพท์ออกมาแล้ว (หลังจากทำตามคำสั่ง WHERE หรือ JOIN แล้ว) จะมาทำการเรียงลำดับข้อมูล ตามคอลัมน์ที่ DISTINCT หรือ GROUP BY ไว้
  2. หลังจากเรียงเสร็จแล้วถึงจะทำการสำเนาข้อมูลไปยังตารางชั่วคราว โดยคัดเอาเฉพาะข้อมูลที่ไม่ซ้ากับรายการก่อนหน้าไป
ผมได้แปลงคำสั่งด้านบนเป็น PHP ได้ประมาณนี (ลองทำความเข้าใจดูนะครับ)
// ข้อมูลต้นฉบับจากการ Query
$datas = array(..........);
// เรียงลำดับข้อมูลตาม id (ไม่ได้เขียนฟังก์ชั่นเรียงลำดับไว้นะครับ)
usort($datas, 'sortById');
// ตารางชั่วคราวเก็บผลลัพท์
$tmp = array();
// ตัวแปรสำหรับตรวจสอบข้อมูลก่อนหน้า
$old_id = null;
// วนลูปข้อมูลต้นฉบับทีละรายการ
foreach($datas as $item){
    // เปรียบเทียบข้อมูลกับข้อมูลก่อนหน้า ถ้าไม่ตรงกันแสดงว่าเป็นข้อมูลใหม่ เข้าไปทำใน if ถ้าเหมือนกันข้ามไปลูปใหม่
    if ($old_id != $item['id']) {
             // บันทึกข้อมูลเก็บไว้ตรวจสอบกับรายการถัดไป
             $old_id = $item['id'];
             // สำเนาข้อมูลไปยังตารางชั่วคราว
             $tmp[] = $item;
    }
}
// คืนค่าผลลัพท์จากตารางชั่วคราว
return $tmp;

จะเห็นว่าในการใช้คำสั่ง DISTINCT กับข้อมูลใน IN ก่อให้เกิดงานเพิ่มซึ่งเป็นงานที่ใช้เวลามากที่สุดซะด้วย (คือการเรียงลำดับ) ในขณะที่หากไม่มีการใช้ จะมีข้อมูลที่อาจจะซ้ำกันจำนวนมากไปตรวจสอบใน IN ยกตัวอย่างเช่น IN (1, 1, 1, 1, 1, 5) คำถามก็คือ คุ้มมั้ยที่จะทำการคัดเลือกข้อมูลที่ซ้ำกันออกก่อน เนื่องจากมีเพื่อนสมาชิกแย้งว่า หากต้องการข้อมูล 5 ต้องผ่าน 1 ซ้ำๆกันมาก่อนหน้า

คำตอบผมไม่ขอฟันธงนะครับ เพราะแน่นอนว่าย่อมมีคนเห็นต่าง แล้วก็ไม่ได้มีคำตอบที่ถูกต้องชัดเจน 100% ด้วย ผมขอให้ความเห็นส่วนตัวนะครับว่า ผมไม่เคย DISTNIC และ GROUP BY กับ subquery เลย (สำหรับกรณีนี้) เนื่องจาก การเรียงลำดับข้อมูลเป็นเรื่องที่เสียเวลามาก รวมถึงการคัดเลือกข้อมูลที่ซ้ำกันออก ก็ต้องทำกับข้อมูลทุกแถวอยู่แล้ว หากเราไม่ต้องทำขั้นตอนนี้ แต่เลือกให้ IN กระทำกับข้อมูลจนถึงลำดับสุดท้ายแทน เวลารวมการทำงานก็ยังคงน้อยกว่า เพราะไม่ต้องผ่านการเรียงลำดับ

สำหรับคนขี้สงสัย อ่านเพิ่มเติมนะครับ DISTINCT และ GROUP BY ต่างกันอย่างไร
0SHAREFacebookLINE it!