ผู้มีรายได้สูงสุดประจำแผนก
ผสานการแบ่งพาร์ทิชันกับการจัดอันดับสำหรับโจทย์เงินเดือน N อันดับแรกในแต่ละกลุ่ม
ผู้มีรายได้สูงสุดประจำแผนก เป็นบทเรียน Coding Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Coding Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
จากการจัดอันดับทั้งตารางสู่การจัดอันดับรายกลุ่ม
คำถามต่อยอดถัดไปคือ "ค้นหาพนักงานที่ได้เงินเดือนสูงสุดในแต่ละแผนก" นี่เป็นการผสานการจัดอันดับเข้ากับการจัดกลุ่ม และเป็นคำถามระดับกลางที่มักใช้ประเมินผู้สมัคร
สมมติว่ามีตาราง employee ซึ่งมี id, name, department_id และ salary เราต้องการพนักงานที่มีเงินเดือนสูงสุดหนึ่งคนต่อแผนก หรือมากกว่าหนึ่งคนหากเงินเดือนเท่ากัน ไม่ใช่เพียงค่าสูงสุดของทั้งตาราง
เครื่องมือใหม่ที่สำคัญคือ PARTITION BY ซึ่งเริ่มการจัดอันดับใหม่ภายในแต่ละแผนก
CREATE TABLE employee (
id INT PRIMARY KEY,
name VARCHAR(100),
department_id INT,
salary INT
);PARTITION BY เริ่มการจัดอันดับใหม่
การเพิ่ม PARTITION BY department_id ให้กับฟังก์ชันหน้าต่างจะบอกฐานข้อมูลให้คำนวณการจัดอันดับแยกกันภายในแต่ละแผนก
ทุกแผนกจะเริ่มอันดับ 1 ของตนเอง ดังนั้นพนักงานที่ได้เงินเดือนสูงสุดในแผนก 1 และพนักงานที่ได้เงินเดือนสูงสุดในแผนก 5 ต่างก็ได้อันดับ 1 หากไม่แบ่งกลุ่ม จะมีเพียงค่าสูงสุดของทั้งตารางเท่านั้นที่ได้อันดับ 1
SELECT name, department_id, salary,
DENSE_RANK() OVER (
PARTITION BY department_id
ORDER BY salary DESC
) AS rnk
FROM employee;การกรองให้เหลืออันดับ 1
หากต้องการเก็บไว้เฉพาะพนักงานที่มีเงินเดือนสูงสุด ให้ครอบคิวรีที่จัดอันดับแล้วกรองหาอันดับ 1 เช่นเดิม ฟังก์ชันหน้าต่างต้องคำนวณในคิวรีย่อยหรือ CTE ก่อน จึงจะสามารถกรองด้วยฟังก์ชันนั้นได้
การใช้ DENSE_RANK หรือ RANK ในกรณีนี้หมายความว่า หากพนักงานสองคนมีเงินเดือนสูงสุดเท่ากันในแผนกหนึ่ง ระบบจะส่งคืนทั้งสองคน ซึ่งโดยทั่วไปเป็นการตีความที่ถูกต้องของคำว่า "พนักงานที่มีเงินเดือนสูงสุด"
SELECT name, department_id, salary
FROM (
SELECT name, department_id, salary,
DENSE_RANK() OVER (
PARTITION BY department_id
ORDER BY salary DESC
) AS rnk
FROM employee
) t
WHERE rnk = 1;ROW_NUMBER เมื่อคุณต้องการเพียงหนึ่งแถว
บางครั้งผู้สัมภาษณ์ต้องการแถว เพียงหนึ่งแถวเท่านั้น ต่อแผนก แม้ว่าจะมีเงินเดือนเท่ากันหลายคน ในกรณีนี้ให้ใช้ ROW_NUMBER และเพิ่มตัวตัดสินกรณีเสมอที่กำหนดผลได้แน่นอน เช่น รหัสที่ต่ำที่สุด
หากไม่มีตัวตัดสินกรณีเสมอ แถวที่เงินเดือนเท่ากันจะถูกตัดสินแบบสุ่ม และผลลัพธ์จะไม่แน่นอน การเพิ่ม , id ASC ทำให้เลือกซ้ำได้ผลเดิม
SELECT name, department_id, salary
FROM (
SELECT name, department_id, salary,
ROW_NUMBER() OVER (
PARTITION BY department_id
ORDER BY salary DESC, id ASC
) AS rn
FROM employee
) t
WHERE rn = 1;DENSE_RANK เทียบกับ ROW_NUMBER และ RANK ในกรณีนี้
ให้เลือกตามถ้อยคำของโจทย์อย่างแม่นยำ:
- DENSE_RANK = 1: พนักงานทั้งหมดที่มีเงินเดือนสูงสุดเท่ากันในแต่ละแผนก
- RANK = 1: ให้ผลเหมือนกับ DENSE_RANK สำหรับอันดับสูงสุด ช่องว่างของอันดับจะมีผลเฉพาะอันดับถัดจากอันดับ 1 ลงไป
- ROW_NUMBER = 1: พนักงานเพียงหนึ่งคนต่อแผนก โดยใช้ ORDER BY ของคุณตัดสินกรณีเสมอ
การบอกว่าเลือกแบบใดและเพราะเหตุใดคือส่วนที่ผู้สัมภาษณ์ใช้ให้คะแนน
แนวทางคำสั่งย่อยแบบสัมพันธ์ก่อนมีฟังก์ชันหน้าต่าง
ก่อนจะมีฟังก์ชันหน้าต่าง วิธีมาตรฐานคือใช้คำสั่งย่อยแบบสัมพันธ์ โดยเก็บแถวไว้เฉพาะเมื่อไม่มีใครในแผนกเดียวกันได้เงินเดือนสูงกว่า
วิธีนี้คืนผู้มีเงินเดือนสูงสุดที่เสมอกันทั้งหมดได้โดยธรรมชาติ ใช้ได้กับระบบต่าง ๆ แต่ก็อาจช้าได้ เพราะจะคำนวณ MAX ด้านในสำหรับแต่ละแถวด้านนอก เว้นแต่ตัวเพิ่มประสิทธิภาพจะเขียนคำสั่งใหม่
SELECT e.name, e.department_id, e.salary
FROM employee e
WHERE e.salary = (
SELECT MAX(e2.salary)
FROM employee e2
WHERE e2.department_id = e.department_id
);แนวทางการเชื่อมตารางด้วย GROUP BY
อีกรูปแบบหนึ่งที่ใช้ได้กับระบบต่าง ๆ คือคำนวณเงินเดือนสูงสุดต่อแผนกด้วย GROUP BY แล้วเชื่อมกลับเพื่อดึงพนักงานที่ตรงกัน
วิธีนี้มีประสิทธิภาพและเข้าใจง่าย การเชื่อมตารางจะนำพนักงานทุกคนที่มีเงินเดือนเท่ากับค่าสูงสุดของแผนกกลับมา ดังนั้นจึงยังคงผู้ที่เสมอกันไว้
SELECT e.name, e.department_id, e.salary
FROM employee e
JOIN (
SELECT department_id, MAX(salary) AS max_sal
FROM employee
GROUP BY department_id
) m
ON e.department_id = m.department_id
AND e.salary = m.max_sal;N อันดับสูงสุดต่อแผนก
รูปแบบนี้ขยายไปสู่โจทย์ "ผู้มีเงินเดือนสูงสุด 3 อันดับแรกต่อแผนก" ได้โดยไม่ต้องมีแนวคิดใหม่ เพียงเปลี่ยนตัวกรองให้เป็นช่วง
เมื่อใช้ DENSE_RANK ค่า rnk <= 3 จะคืนระดับเงินเดือนแบบ DISTINCT สามอันดับแรก ซึ่งอาจมีมากกว่าสามแถวหากมีผู้ที่เสมอกัน ส่วน ROW_NUMBER กับค่า rn <= 3 จะคืนสามแถวพอดีต่อแผนก
SELECT name, department_id, salary
FROM (
SELECT name, department_id, salary,
DENSE_RANK() OVER (
PARTITION BY department_id
ORDER BY salary DESC
) AS rnk
FROM employee
) t
WHERE rnk <= 3;ตัวอย่างที่ทำให้ดู
แผนก 1: Ana 120, Bob 120, Cara 90 แผนก 2: Dan 200, Eve 150
- DENSE_RANK = 1: Ana (120), Bob (120) จากแผนก 1 และ Dan (200) จากแผนก 2 รวมสามแถว
- ROW_NUMBER = 1 พร้อมใช้รหัสเป็นตัวตัดสินกรณีเสมอ: เลือก Ana หรือ Bob คนใดคนหนึ่ง โดยเลือกคนที่มีรหัสต่ำกว่า และเลือก Dan รวมสองแถว
ข้อมูลเดียวกันอาจให้จำนวนแถวต่างกันตามฟังก์ชันที่ใช้ ให้เลือกให้ตรงกับคำถาม
รวมแผนกและเชื่อมชื่อ
ผู้สัมภาษณ์มักเพิ่มตาราง department แล้วขอชื่อแผนกด้วย เพียงเชื่อมตารางนี้หลังจากจัดอันดับแล้ว
ให้จัดอันดับบนตาราง employee แล้วค่อยเชื่อมตารางอ้างอิงในตอนท้าย เพื่อให้การแบ่งกลุ่มยังเกิดขึ้นในระดับที่ถูกต้อง
SELECT d.name AS department, t.name AS employee, t.salary
FROM (
SELECT name, department_id, salary,
DENSE_RANK() OVER (
PARTITION BY department_id ORDER BY salary DESC
) AS rnk
FROM employee
) t
JOIN department d ON d.id = t.department_id
WHERE t.rnk = 1;ข้อผิดพลาดที่ควรหลีกเลี่ยง
ข้อผิดพลาดที่พบบ่อยในการจัดอันดับแยกตามกลุ่ม:
- ลืม
PARTITION BYแล้วจัดอันดับรวมทั้งบริษัท ทำให้คืนเฉพาะผู้มีเงินเดือนสูงสุดของบริษัท - ใช้
ROW_NUMBERเมื่อโจทย์สื่อว่าต้องแสดงผู้ที่เสมอกันทั้งหมด ทำให้ผู้มีเงินเดือนสูงสุดที่เสมอกันถูกตัดออกโดยไม่แจ้ง - พยายามใส่ฟังก์ชันหน้าต่างไว้ใน
WHEREโดยตรง แทนที่จะครอบไว้ - เชื่อมตารางแผนกก่อนจัดอันดับ ทำให้ระดับความละเอียดของการแบ่งกลุ่มเปลี่ยนไปโดยไม่ตั้งใจ
ตรวจสอบด่วน
เลือกฟังก์ชันจัดอันดับที่เหมาะกับข้อกำหนด
สรุป
การหาผู้มีเงินเดือนสูงสุดต่อแผนกคือรูปแบบการจัดอันดับทั่วทั้งข้อมูล โดยเพิ่ม PARTITION BY department_id:
- DENSE_RANK = 1 คืนผู้มีเงินเดือนสูงสุดที่เสมอกันทั้งหมดในแต่ละแผนก
- ROW_NUMBER = 1 เมื่อมีตัวตัดสินกรณีเสมอ จะคืนเพียงหนึ่งคนต่อแผนก
- ทางเลือกที่ใช้ได้กับระบบต่าง ๆ ได้แก่คำสั่งย่อยแบบสัมพันธ์ที่หา
MAXต่อแผนก หรือค่าMAXจากGROUP BYแล้วเชื่อมกลับกับตาราง
หากต้องการ N อันดับสูงสุด ให้เปลี่ยน = 1 เป็น <= N และบอกให้ชัดว่าจัดการกรณีเสมออย่างไร
คำถามที่พบบ่อย
บทเรียน “ผู้มีรายได้สูงสุดประจำแผนก” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ผู้มีรายได้สูงสุดประจำแผนก” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Coding Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ผู้มีรายได้สูงสุดประจำแผนก”
ผสานการแบ่งพาร์ทิชันกับการจัดอันดับสำหรับโจทย์เงินเดือน N อันดับแรกในแต่ละกลุ่ม คุณปฏิบัติ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- เงินเดือนสูงสุดอันดับสอง ห้าวิธี
- ค่าสูงสุดอันดับที่ n ด้วย DENSE_RANK
- ผู้มีรายได้สูงสุดประจำแผนก
- ส่งคืน NULL เมื่อไม่มีค่าอันดับที่ n