การแตกแขนงของการเชื่อมตารางและการเพิ่มจำนวนแถว
เหตุใดการเชื่อมตารางจึงส่งคืนแถวมากกว่าตารางใดตารางหนึ่งได้ และผู้สัมภาษณ์ทดสอบเรื่องนี้อย่างไร
การแตกแขนงของการเชื่อมตารางและการเพิ่มจำนวนแถว เป็นบทเรียน Coding Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Coding Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
เมื่อการเชื่อมคืนแถวมามากเกินไป
หนึ่งในคำถามสัมภาษณ์ที่เปิดเผยความเข้าใจได้มากที่สุดฟังดูเหมือนเป็นคำถามง่าย ๆ: “การเชื่อมสามารถคืนแถวมากกว่าตารางที่มีจำนวนแถวมากกว่าได้หรือไม่” คำตอบคือได้ และปรากฏการณ์นี้เรียกว่าการแตกแขนงของแถว หรือการคูณแถว
ผู้สมัครที่ตอบว่า “การเชื่อมก็แค่รวมตารางเข้าด้วยกัน” จะพลาดประเด็นนี้ ส่วนผู้สมัครที่คาดการณ์จำนวนแถวที่แน่นอนได้จะมีโอกาสได้รับคัดเลือก บทเรียนนี้จะฝึกทักษะการคาดการณ์ดังกล่าว
สาเหตุ: การจับคู่แบบหนึ่งต่อหลาย
การแตกแขนงของแถวเกิดขึ้นเมื่อแถวหนึ่งฝั่งซ้ายตรงกับหลายแถวฝั่งขวา แต่ละคู่ที่ตรงกันจะสร้างแถวผลลัพธ์แยกกัน
ในกรณีตารางลูกค้าและคำสั่งซื้อ Ada ซึ่งเป็นลูกค้าหนึ่งคนมีคำสั่งซื้อสองรายการ การเชื่อมจะสร้างหนึ่งแถวต่อคำสั่งซื้อ ดังนั้นข้อมูลของ Ada จึงซ้ำกัน ข้อมูลลูกค้าจะเหมือนเดิม ต่างกันเฉพาะข้อมูลคำสั่งซื้อ
SELECT c.name, o.amount
FROM customers c
JOIN orders o ON o.customer_id = c.id;
-- Ada appears twice (she has 2 orders)
-- name | amount
-- Ada | 50
-- Ada | 20
-- Bob | 99การนับจำนวนแถวผลลัพธ์
จำนวนแถวผลลัพธ์เท่ากับผลรวมจำนวนคู่ที่ตรงกันของแต่ละแถวฝั่งซ้าย ไม่ใช่จำนวนลูกค้า
- Ada -> คำสั่งซื้อ 2 รายการ -> 2 แถว
- Bob -> คำสั่งซื้อ 1 รายการ -> 1 แถว
- Cleo -> คำสั่งซื้อ 0 รายการ -> 0 แถว (ถูกตัดออกโดย INNER JOIN)
รวม = 3 แถว แม้ว่าตารางลูกค้าจะมี 3 แถวเช่นกัน หากเปลี่ยนให้ Ada มีคำสั่งซื้อ 10 รายการ ผลลัพธ์จะเพิ่มเป็น 11 แถว
การจับคู่แบบหลายต่อหลายทำให้จำนวนพุ่งขึ้น
การแตกแขนงของแถวจะทวีคูณเมื่อทั้งสองฝั่งมีหลายคู่ที่ตรงกับคีย์เดียวกัน หากคีย์ K ปรากฏ 3 ครั้งทางฝั่งซ้ายและ 4 ครั้งทางฝั่งขวา การเชื่อมจะสร้าง 3 x 4 = 12 แถวสำหรับคีย์นั้น
นี่คือวิธีที่การเชื่อมซึ่งดูเหมือนเล็กน้อยขยายตัวจนกลายเป็นหลายล้านแถว ผู้สัมภาษณ์ชอบใส่คีย์ซ้ำไว้ทั้งสองฝั่งเพื่อดูว่าคุณสังเกตเห็นการคูณหรือไม่
-- left has 3 rows with tag 'A', right has 4 rows with tag 'A'
SELECT l.id, r.id
FROM left_t l
JOIN right_t r ON r.tag = l.tag;
-- tag 'A' alone yields 3 * 4 = 12 output rowsกับดักของการรวมข้อมูล
นี่คือข้อผิดพลาดที่ผู้สัมภาษณ์มักวางไว้มากที่สุด คุณเชื่อมคำสั่งซื้อกับรายการสินค้าในคำสั่งซื้อเพื่อดูรายละเอียดสินค้า แล้วใช้ SUM กับจำนวนเงินของคำสั่งซื้อ เนื่องจากแต่ละคำสั่งซื้อแตกแขนงเป็นแถวรายการสินค้าหลายแถว จำนวนเงินของคำสั่งซื้อจึงถูกนับหนึ่งครั้งต่อรายการสินค้า
ผลรวมจึงสูงเกินจริงอย่างมาก คิวรีดูถูกต้องและยังทำงานได้ด้วย ซึ่งเป็นเหตุผลที่ทำให้ข้อผิดพลาดนี้อันตราย
-- BUG: order.amount duplicated across items
SELECT SUM(o.amount) AS total
FROM orders o
JOIN order_items i ON i.order_id = o.id;
-- a 3-item order counts o.amount 3 timesมองเห็นค่าที่สูงเกินจริง
สมมติว่าคำสั่งซื้อหนึ่งรายการมีจำนวนเงิน 100 และมีรายการสินค้า 3 รายการ การเชื่อมจะสร้าง 3 แถว โดยแต่ละแถวมีจำนวนเงิน 100 อยู่ด้วย SUM(o.amount) จึงคืนค่า 300 ไม่ใช่ 100
วิธีแก้คือรวมข้อมูลตามระดับข้อมูลที่ถูกต้อง: รวมรายการสินค้า หรือรวมคำสั่งซื้อที่ไม่ซ้ำกันแยกต่างหาก อย่าใช้ SUM กับค่าของตารางแม่ข้ามการเชื่อมกับตารางลูกที่แตกแขนง
o.id | o.amount | i.id
7 | 100 | 71
7 | 100 | 72
7 | 100 | 73
-- SUM(o.amount) = 300 (WRONG, should be 100)วิธีแก้ที่ 1: รวมข้อมูลตารางลูกก่อน
วิธีแก้ที่สะอาดที่สุดคือรวมข้อมูลฝั่งที่มีหลายแถวล่วงหน้าในคิวรีย่อยหรือ CTE เพื่อให้แต่ละตารางแม่ตรงกับแถวสรุปเพียงหนึ่งแถว ไม่มีการแตกแขนงและไม่มีค่าที่สูงเกินจริง
ในที่นี้เรายุบรายการสินค้าให้เหลือหนึ่งแถวต่อคำสั่งซื้อก่อนทำการเชื่อม ดังนั้นจำนวนเงินของตารางแม่จะไม่ถูกทำซ้ำ
SELECT o.id, o.amount, i.item_count
FROM orders o
JOIN (
SELECT order_id, COUNT(*) AS item_count
FROM order_items
GROUP BY order_id
) i ON i.order_id = o.id;วิธีแก้ที่ 2: COUNT(DISTINCT) และผลรวมแบบมีเงื่อนไข
หากจำเป็นต้องรวมข้อมูลหลังการเชื่อมที่ทำให้เกิดการแตกแขนง ให้นับหรือรวมข้อมูลตามระดับข้อมูลที่ถูกต้อง ใช้ COUNT(DISTINCT o.id) เพื่อนับจำนวนคำสั่งซื้อแทนการนับแถวรายการสินค้า
หมายเหตุ: SUM(DISTINCT o.amount) ไม่ใช่วิธีแก้ที่ปลอดภัย เพราะคำสั่งซื้อที่แตกต่างกันสองรายการอาจมีจำนวนเงินเท่ากันโดยถูกต้อง และจะถูกนับรวมเป็นรายการเดียว การรวมข้อมูลล่วงหน้าจึงเชื่อถือได้มากกว่า
SELECT COUNT(DISTINCT o.id) AS num_orders,
COUNT(i.id) AS num_items
FROM orders o
JOIN order_items i ON i.order_id = o.id;ตรวจจับการแตกแขนงก่อนจะสร้างปัญหา
วิธีตรวจสอบอย่างรวดเร็วที่ผู้สัมภาษณ์ชอบใช้คือ ตรวจสอบว่าคีย์การเชื่อมไม่ซ้ำกันในฝั่งที่คุณคาดว่าจะเป็น “หนึ่ง” หรือไม่ หากจำนวนคีย์แบบ DISTINCT น้อยกว่าจำนวนแถว แสดงว่าฝั่งนั้นมีค่าซ้ำและจะทำให้แถวแตกแขนง
-- if this returns rows, order_id is NOT unique in order_items
SELECT order_id, COUNT(*) AS n
FROM order_items
GROUP BY order_id
HAVING COUNT(*) > 1;ตรวจสอบระดับข้อมูลด้วยการนับ
ก่อนเชื่อถือผลรวมใด ๆ จากผลลัพธ์ที่เชื่อมกัน ให้ตรวจสอบจำนวนแถวเพื่อดูความสมเหตุสมผลก่อน วิธีที่รวดเร็วคือเปรียบเทียบจำนวนนับของผลลัพธ์ที่เชื่อมกันกับจำนวนนับของตารางที่คุณคาดว่าจะเป็นระดับข้อมูล
หาก COUNT(*) ของการเชื่อมมีค่ามากกว่า COUNT(*) ของคำสั่งซื้อ แสดงว่าการเชื่อมทำให้แถวแตกแขนง และการรวมข้อมูลต่อคำสั่งซื้ออาจให้ผลผิดพลาด การตรวจสอบเพียงบรรทัดเดียวนี้ช่วยคำตอบในการสัมภาษณ์มาแล้วหลายครั้ง
-- joined rows should equal order count if no fan-out
SELECT COUNT(*) AS joined_rows
FROM orders o
JOIN order_items i ON i.order_id = o.id;
SELECT COUNT(*) AS order_rows FROM orders;
-- joined_rows > order_rows => fan-out presentการแตกแขนงไม่ได้เป็นข้อผิดพลาดเสมอไป
บางครั้งคุณต้องการหนึ่งแถวต่อรายการลูก การแสดงรายการสินค้าทุกรายการพร้อมข้อมูลส่วนหัวของคำสั่งซื้อเป็นการแตกแขนงที่ถูกต้อง ทักษะสำคัญคือการรู้จักระดับข้อมูลเป้าหมายของคุณ: เอนทิตีหนึ่งรายการควรสร้างกี่แถว
ระบุระดับข้อมูลก่อนเขียนคิวรี “ฉันต้องการหนึ่งแถวต่อรายการสินค้าในคำสั่งซื้อ” เทียบกับ “หนึ่งแถวต่อคำสั่งซื้อ” ซึ่งจะเป็นตัวตัดสินว่าการแตกแขนงเป็นคุณลักษณะหรือข้อผิดพลาด
ตรวจสอบอย่างรวดเร็ว
คาดการณ์ผลลัพธ์ของการเชื่อมแบบหนึ่งต่อหลาย
สรุป: การแตกแขนงและการคูณแถว
สิ่งที่ควรจำ:
- การเชื่อมสร้างหนึ่งแถวต่อคู่ที่ตรงกัน ดังนั้นการจับคู่แบบหนึ่งต่อหลายจึงทำให้ข้อมูลของฝั่งที่มีหนึ่งรายการซ้ำกัน
- คีย์แบบหลายต่อหลายจะทำให้จำนวนแถวคูณกัน: 3 x 4 = 12 แถวสำหรับคีย์นั้น
- การรวมค่าของตารางแม่ข้ามการเชื่อมที่ทำให้เกิดการแตกแขนงจะทำให้ผลรวมและค่าการนับสูงเกินจริง
- แก้ไขโดยรวมข้อมูลตารางลูกล่วงหน้า หรือนับ/รวมข้อมูลตามระดับข้อมูลที่ถูกต้อง เช่น
COUNT(DISTINCT) - ระบุระดับข้อมูลที่ต้องการก่อนเสมอ การแตกแขนงจะเป็นข้อผิดพลาดก็ต่อเมื่อไม่สอดคล้องกับระดับข้อมูลนั้น
คำถามที่พบบ่อย
บทเรียน “การแตกแขนงของการเชื่อมตารางและการเพิ่มจำนวนแถว” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การแตกแขนงของการเชื่อมตารางและการเพิ่มจำนวนแถว” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Coding Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การแตกแขนงของการเชื่อมตารางและการเพิ่มจำนวนแถว”
เหตุใดการเชื่อมตารางจึงส่งคืนแถวมากกว่าตารางใดตารางหนึ่งได้ และผู้สัมภาษณ์ทดสอบเรื่องนี้อย่างไร คุณปฏิบัติ Coding Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Coding Interview Prep หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Coding Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “การแตกแขนงของการเชื่อมตารางและการเพิ่มจำนวนแถว” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Coding Interview Prep นี้ได้ไหม
ได้ บทเรียน Coding Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- วิธีที่ INNER JOIN จับคู่แถว
- ON เทียบกับ WHERE ในการเชื่อมตาราง
- การแตกแขนงของการเชื่อมตารางและการเพิ่มจำนวนแถว
- เชื่อมตารางสามตารางขึ้นไป