เขียนคิวรีย่อยที่สัมพันธ์กับคิวรีภายนอกใหม่เป็นการเชื่อมตาราง
เปลี่ยนตรรกะที่สัมพันธ์กับคิวรีภายนอกให้เป็นการเชื่อมตารางหรือฟังก์ชันวินโดว์เพื่อเพิ่มประสิทธิภาพ
เขียนคิวรีย่อยที่สัมพันธ์กับคิวรีภายนอกใหม่เป็นการเชื่อมตาราง เป็นบทเรียน SQL Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน SQL Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส SQL Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดจึงต้องเขียนใหม่
ซับคิวรีแบบสัมพันธ์อ่านเข้าใจง่าย แต่ทำงานช้าได้: คิวรีด้านในอาจทำงานหนึ่งครั้งต่อแถวด้านนอก ผู้สัมภาษณ์มักขอให้คุณ เขียนใหม่เป็นการเชื่อมตารางหรือฟังก์ชันหน้าต่าง เพื่อปรับปรุงประสิทธิภาพ
เป้าหมายคือให้ได้ผลลัพธ์เดิมด้วยการประมวลผลข้อมูลเพียงรอบเดียว แทนการตรวจสอบด้านในซ้ำ ๆ
การรู้จักรูปแบบการเขียนใหม่สองหรือสามรูปแบบ รวมถึงรู้ว่าแต่ละรูปแบบยังคงความถูกต้องได้เมื่อใด เป็นทักษะสำคัญของผู้พัฒนาระดับกลาง
รูปแบบที่ 1: EXISTS เป็น INNER JOIN
EXISTS แบบสัมพันธ์ที่ตรวจสอบว่ามีรายการตรงกันอย่างน้อยหนึ่งรายการ มักเขียนใหม่เป็น INNER JOIN ได้
แต่ต้องระวังว่า การเชื่อมตารางอาจสร้าง แถวด้านนอกซ้ำกัน หากมีหลายแถวด้านในที่ตรงกัน ให้เพิ่ม DISTINCT หรือใช้การรวมค่าเพื่อให้กลับมาเหลือหนึ่งแถวต่อคีย์ด้านนอก
-- Correlated EXISTS
SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id);
-- Join rewrite (DISTINCT avoids dupes from fan-out)
SELECT DISTINCT c.customer_id, c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;ข้อผิดพลาดจากการแตกแถว
ข้อผิดพลาดที่พบบ่อยที่สุดในการเขียนใหม่คือการลืมคำนึงถึงการแตกแถว EXISTS คืนค่าลูกค้าแต่ละรายเพียงครั้งเดียว ไม่ว่าลูกค้าจะมีคำสั่งซื้อกี่รายการก็ตาม แต่การเชื่อมตารางแบบตรงไปตรงมาจะคืนค่าหนึ่งแถวต่อคำสั่งซื้อ ทำให้จำนวนที่นับสูงเกินจริง
หากขั้นตอนถัดไปใช้ COUNT(*) หรือ SUM(amount) กับผลลัพธ์ที่เชื่อมแล้วโดยจัดกลุ่มไม่รอบคอบ ตัวเลขจะไม่ถูกต้อง
ควรถามตัวเองเสมอว่า การเชื่อมตารางอาจทำให้จำนวนแถวเพิ่มขึ้นหรือไม่ หากใช่ ให้ใช้ DISTINCT หรือ GROUP BY เพื่อรวมกลับมาเป็นจำนวนแถวที่ถูกต้อง
รูปแบบที่ 2: NOT EXISTS เป็น LEFT JOIN / IS NULL
การเขียนใหม่เพื่อค้นหาแถวที่ไม่ตรงกันเป็นรูปแบบที่มักถูกถามในการสัมภาษณ์ โดย NOT EXISTS แบบสัมพันธ์จะกลายเป็น LEFT JOIN ที่ตรวจสอบให้ฝั่งขวาเป็น NULL
แถวด้านนอกที่ไม่พบรายการตรงกันจะมีค่า NULL ทางฝั่งขวา การกรองด้วยค่านั้นจะเก็บไว้พอดีกับแถวที่ไม่มีรายการตรงกัน
-- Correlated NOT EXISTS
SELECT c.customer_id FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id);
-- LEFT JOIN / IS NULL rewrite
SELECT c.customer_id
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
WHERE o.customer_id IS NULL;เลือกคอลัมน์ที่ไม่ใช่ NULL สำหรับตรวจสอบ
ในการเขียนใหม่ด้วย LEFT JOIN / IS NULL ให้ตรวจสอบคอลัมน์ฝั่งขวาที่ ไม่มีทางเป็น NULL เมื่อมีรายการที่ตรงกันจริง โดยควรเป็นคีย์ที่ใช้เชื่อมตารางหรือคีย์หลัก
หากตรวจสอบคอลัมน์ที่อนุญาตให้เป็น NULL คุณจะแยกไม่ออกระหว่างการไม่ตรงกันจริง ๆ (ไม่มีแถว) กับแถวที่ตรงกันแต่มีค่า NULL ในคอลัมน์นั้น ข้อผิดพลาดนี้จะคืนค่าแถวที่ไม่ถูกต้อง
การใช้คีย์ที่ใช้เชื่อมตาราง (ในที่นี้คือ o.customer_id) หรือ o.order_id รับรองได้ว่า NULL หมายถึง "ไม่มีแถวที่ตรงกัน"
รูปแบบที่ 3: การรวมค่าเดี่ยวเป็น JOIN + GROUP BY
การรวมค่าแบบสัมพันธ์ใน SELECT สามารถเขียนใหม่เป็นการเชื่อมกับซับคิวรีที่จัดกลุ่ม (ตารางที่สร้างจากคิวรี) ได้
คำนวณค่ารวมของแต่ละกลุ่มเพียงครั้งเดียว แล้วเชื่อมกลับเข้ากับแถวรายละเอียด คิวรีด้านในจึงทำงานเพียงครั้งเดียวแทนที่จะทำงานซ้ำทุกแถว
-- Correlated scalar aggregate
SELECT e1.name,
(SELECT MAX(e2.salary) FROM employees e2
WHERE e2.dept_id = e1.dept_id) AS dept_max
FROM employees e1;
-- Join + GROUP BY rewrite
SELECT e.name, m.dept_max
FROM employees e
JOIN (SELECT dept_id, MAX(salary) AS dept_max
FROM employees GROUP BY dept_id) m
ON m.dept_id = e.dept_id;รูปแบบที่ 4: การเขียนใหม่ด้วยฟังก์ชันหน้าต่าง
บ่อยครั้ง การเขียนใหม่ที่สะอาดที่สุดคือการใช้ฟังก์ชันหน้าต่าง MAX(salary) OVER (PARTITION BY dept_id) จะแทนการรวมค่าแบบสัมพันธ์ทั้งหมดโดยไม่ต้องใช้การเชื่อมตาราง
วิธีนี้คำนวณค่าของกลุ่มด้วยการประมวลผลเพียงรอบเดียวและยังคงแถวรายละเอียดทุกแถวไว้ โดยทั่วไปนี่คือคำตอบที่ผู้สัมภาษณ์ต้องการเห็นมากที่สุดสำหรับคิวรีวิเคราะห์ข้อมูล
SELECT name,
dept_id,
salary,
MAX(salary) OVER (PARTITION BY dept_id) AS dept_max
FROM employees;การเขียนใหม่เพื่อหา N อันดับสูงสุดต่อกลุ่ม
ซับคิวรีแบบสัมพันธ์ที่เลือกแถวอันดับสูงสุดของแต่ละกลุ่ม (salary = MAX per dept) สามารถเขียนใหม่ได้อย่างเป็นระเบียบด้วย ROW_NUMBER
แบ่งด้วย PARTITION ตามกลุ่ม เรียงตามตัวชี้วัด แล้วเก็บแถวที่มีอันดับ 1 หากต้องการเก็บแถวอันดับสูงสุดทั้งหมดที่มีค่าเท่ากัน ให้ใช้ RANK แทน
SELECT name, dept_id, salary
FROM (
SELECT name, dept_id, salary,
ROW_NUMBER() OVER (PARTITION BY dept_id
ORDER BY salary DESC) AS rn
FROM employees
) t
WHERE rn = 1;เมื่อใดที่ไม่ควรเขียนใหม่
การเขียนใหม่ไม่ได้ให้ผลดีเสมอไป ควรใช้ซับคิวรีแบบสัมพันธ์ต่อไปเมื่อ:
- ชุดข้อมูลด้านนอกมีขนาดเล็ก ต้นทุนต่อแถวจึงต่ำจนแทบไม่มีนัยสำคัญ
- คอลัมน์ที่ใช้สัมพันธ์มีดัชนีที่ดี และตัวปรับประสิทธิภาพคิวรีแปลงให้เป็นการเชื่อมแบบกึ่งที่มีประสิทธิภาพอยู่แล้ว
- ความอ่านง่ายสำคัญกว่าการปรับแต่งประสิทธิภาพเล็ก ๆ น้อย ๆ ในโค้ดที่ต้องดูแลรักษา
ตัวปรับประสิทธิภาพคิวรีสมัยใหม่มักแปลง EXISTS เป็นการเชื่อมแบบกึ่งโดยอัตโนมัติ ควรกล่าวว่าคุณจะ ตรวจวัดด้วย EXPLAIN ก่อนสรุปว่าการเขียนใหม่ช่วยเพิ่มประสิทธิภาพ
การตรวจสอบความเทียบเท่า
หลังจากเขียนใหม่ทุกครั้ง ให้ยืนยันว่าได้ แถวชุดเดียวกันและมีจำนวนแถวเท่ากัน กับรูปแบบเดิม
- ตรวจสอบว่าจำนวนแถวตรงกัน
- ตรวจสอบว่าไม่มีแถวซ้ำเกิดจากการแตกแถวของการเชื่อมตาราง
- ตรวจสอบว่ากรณีขอบเกี่ยวกับ NULL และกลุ่มว่างยังทำงานอย่างถูกต้อง
วิธีที่รวดเร็วคือเรียกใช้ทั้งสองรูปแบบแล้วใช้ EXCEPT เปรียบเทียบกันทั้งสองทิศทาง หากผลลัพธ์ว่าง แสดงว่าทั้งสองรูปแบบให้ผลตรงกัน ผู้สัมภาษณ์ให้คุณค่ากับการตรวจสอบแทนการคาดเดา
SELECT customer_id FROM query_a
EXCEPT
SELECT customer_id FROM query_b;
-- and the reverse; both empty => equivalentการเขียนใหม่จาก IN เป็น JOIN
ซับคิวรี IN ที่ไม่สัมพันธ์กับคิวรีด้านนอกมักเขียนใหม่เป็นการเชื่อมตารางได้เช่นกัน แต่ยังต้องระวังการแตกแถวแบบเดิม IN จะตัดสมาชิกที่ซ้ำกันออก แต่การเชื่อมตารางจะไม่ทำเช่นนั้น
หากรายการด้านในมีคีย์ซ้ำกัน การเชื่อมตารางจะทำให้แถวด้านนอกซ้ำ ใช้ DISTINCT กับฝั่งด้านในหรือกับผลลัพธ์สุดท้ายเพื่อให้ได้ความหมายแบบเดียวกับ IN
-- IN subquery
SELECT c.name FROM customers c
WHERE c.customer_id IN (SELECT o.customer_id FROM orders o);
-- Join rewrite, de-duplicated to match IN
SELECT DISTINCT c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;ตรวจสอบด่วน
เลือกรูปแบบการเชื่อมตารางที่ถูกต้องสำหรับการเชื่อมเพื่อค้นหาแถวที่ไม่ตรงกันด้วย NOT EXISTS แบบสัมพันธ์
สรุป: การเขียนซับคิวรีแบบสัมพันธ์ใหม่เป็นการเชื่อมตาราง
ประเด็นสำคัญ:
EXISTS→INNER JOIN(เพิ่ม DISTINCT เพื่อป้องกันแถวซ้ำจากการแตกแถว)NOT EXISTS→LEFT JOIN ... WHERE key IS NULL(ตรวจสอบคอลัมน์ที่ไม่อนุญาตให้เป็น NULL)- การรวมค่าเดี่ยวแบบสัมพันธ์ → ใช้
JOINกับตารางที่สร้างจากคิวรีและจัดกลุ่ม หรือที่ดีกว่าคือใช้ ฟังก์ชันหน้าต่าง - อันดับสูงสุดต่อกลุ่ม → ใช้
ROW_NUMBER(หรือRANKเมื่อมีค่าเท่ากัน) - ตรวจสอบความเทียบเท่าและใช้
EXPLAINก่อนสรุปว่าการเขียนใหม่เร็วกว่า
การรู้จักทั้งสองรูปแบบและกับดักการแตกแถวคือสิ่งที่การสัมภาษณ์ระดับกลางมักใช้ตรวจสอบ
คำถามที่พบบ่อย
บทเรียน “เขียนคิวรีย่อยที่สัมพันธ์กับคิวรีภายนอกใหม่เป็นการเชื่อมตาราง” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “เขียนคิวรีย่อยที่สัมพันธ์กับคิวรีภายนอกใหม่เป็นการเชื่อมตาราง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ 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 ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “เขียนคิวรีย่อยที่สัมพันธ์กับคิวรีภายนอกใหม่เป็นการเชื่อมตาราง” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน SQL Interview Prep นี้ได้ไหม
ได้ บทเรียน SQL Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- กายวิภาคของคิวรีย่อยที่สัมพันธ์กับคิวรีภายนอก
- ฟังก์ชันรวมแยกตามกลุ่มโดยไม่มี GROUP BY
- EXISTS และ NOT EXISTS ที่สัมพันธ์กับคิวรีภายนอก
- เขียนคิวรีย่อยที่สัมพันธ์กับคิวรีภายนอกใหม่เป็นการเชื่อมตาราง