0Pricing
SQL Interview Prep · บทเรียน

การแตกแขนงของการเชื่อมตารางและการเพิ่มจำนวนแถว

เหตุใดการเชื่อมตารางจึงส่งคืนแถวมากกว่าตารางใดตารางหนึ่งได้ และผู้สัมภาษณ์ทดสอบเรื่องนี้อย่างไร

การแตกแขนงของการเชื่อมตารางและการเพิ่มจำนวนแถว เป็นบทเรียน SQL Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน SQL Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส SQL 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) และปลดล็อคส่วนที่เหลือของคอร์ส SQL Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส SQL Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การแตกแขนงของการเชื่อมตารางและการเพิ่มจำนวนแถว”

เหตุใดการเชื่อมตารางจึงส่งคืนแถวมากกว่าตารางใดตารางหนึ่งได้ และผู้สัมภาษณ์ทดสอบเรื่องนี้อย่างไร คุณปฏิบัติ SQL Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน SQL Interview Prep หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน SQL Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “การแตกแขนงของการเชื่อมตารางและการเพิ่มจำนวนแถว” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน SQL Interview Prep นี้ได้ไหม

ได้ บทเรียน SQL Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. วิธีที่ INNER JOIN จับคู่แถว
  2. ON เทียบกับ WHERE ในการเชื่อมตาราง
  3. การแตกแขนงของการเชื่อมตารางและการเพิ่มจำนวนแถว
  4. เชื่อมตารางสามตารางขึ้นไป
← กลับไปที่ SQL Interview Prep