กรองจากผลลัพธ์ของฟังก์ชันวินโดว์
เหตุใดจึงต้องห่อฟังก์ชันวินโดว์ไว้ในคิวรีย่อยหรือ CTE เพื่อกรองจากผลลัพธ์นั้น
กรองจากผลลัพธ์ของฟังก์ชันวินโดว์ เป็นบทเรียน SQL Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน SQL Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส SQL Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดจึงไม่สามารถกรองฟังก์ชันหน้าต่างใน WHERE ได้
นี่คือจุดหลอกที่พบบ่อยในการสัมภาษณ์: การเขียน WHERE ROW_NUMBER() OVER (...) = 1 จะทำให้เกิดข้อผิดพลาด ฟังก์ชันหน้าต่าง ไม่อนุญาตให้ใช้ ใน WHERE, GROUP BY หรือ HAVING
เหตุผลอยู่ที่ลำดับการทำงานเชิงตรรกะ WHERE ทำงานเพื่อเลือกแถว ก่อนที่ฟังก์ชันหน้าต่างจะได้รับการประเมิน หน้าต่างยังไม่ได้รับการคำนวณด้วยซ้ำ จึงไม่สามารถนำมาอ้างอิงในตัวกรองได้
คำอธิบายลำดับการทำงาน
ฟังก์ชันหน้าต่างจะถูกคำนวณในขั้นตอนเฉพาะที่อยู่ หลังจาก FROM, WHERE, GROUP BY และ HAVING แต่ ก่อน ORDER BY และ LIMIT ขั้นสุดท้าย
ดังนั้น ในขณะที่ WHERE ทำงาน อันดับหรือลำดับแถวยังไม่มีอยู่ หากต้องการกรองด้วยค่านั้น คุณต้องปล่อยให้หน้าต่างคำนวณเสร็จก่อน แล้วจึงกรองคอลัมน์ที่สร้างขึ้นในชั้นคิวรี ภายนอก
รูปแบบการห่อด้วยคิวรีย่อย
วิธีแก้มาตรฐานคือ คำนวณฟังก์ชันหน้าต่างในคิวรีด้านใน (ตารางที่ได้จากคิวรี) ตั้งชื่อแทนผลลัพธ์ แล้วกรองชื่อแทนนั้นใน WHERE ของคิวรีด้านนอก
ตารางที่ได้จากคิวรี ต้องมีชื่อแทน (ในที่นี้คือ t) ผู้สัมภาษณ์มักสังเกตผู้สมัครที่ลืมใส่ ตอนนี้ rn เป็นคอลัมน์ทั่วไปที่คิวรีด้านนอกสามารถนำไปเปรียบเทียบได้
SELECT *
FROM (
SELECT
name, department, salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn
FROM employees
) t
WHERE rn = 1;รูปแบบ CTE (มักอ่านง่ายกว่า)
นิพจน์ตารางทั่วไป (CTE) ทำงานเดียวกันโดยมีโครงสร้างที่อ่านง่ายกว่า กำหนดการจัดอันดับในขั้นตอน WITH แล้วกรองในคิวรีหลัก
การทำงานเหมือนกับคิวรีย่อยทุกประการ แต่โดยทั่วไปผู้สัมภาษณ์มักชอบ CTE ในการเขียนโค้ดสด เพราะอ่านเจตนาได้จากบนลงล่าง
WITH ranked AS (
SELECT
name, department, salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn
FROM employees
)
SELECT name, department, salary
FROM ranked
WHERE rn = 1;ตัวอย่างแบบลงมือทำ: N อันดับแรกต่อกลุ่ม
โจทย์ฟังก์ชันหน้าต่างที่พบบ่อยที่สุดคือ "พนักงานที่ได้รับเงินเดือนสูงสุด 3 อันดับแรกต่อแผนก" จัดอันดับภายใน CTE แล้วเก็บเฉพาะ rn <= 3 ในคิวรีด้านนอก
เลือกฟังก์ชันจัดอันดับตามวิธีจัดการค่าที่เท่ากัน: ROW_NUMBER จำกัดไว้ที่ 3 แถวพอดีต่อแผนก หากต้องรวมผู้ที่ได้คะแนนเท่ากันตรงขอบเขต ให้เปลี่ยนเป็น RANK/DENSE_RANK
WITH ranked AS (
SELECT department, name, salary,
ROW_NUMBER() OVER (
PARTITION BY department ORDER BY salary DESC
) AS rn
FROM employees
)
SELECT department, name, salary
FROM ranked
WHERE rn <= 3
ORDER BY department, rn;ตัวอย่างแบบลงมือทำ: การกรองผลรวมสะสม
รูปแบบการห่อไม่ได้ใช้เฉพาะกับอันดับเท่านั้น ผลลัพธ์จากฟังก์ชันหน้าต่างใด ๆ — ผลรวมสะสม ค่าเฉลี่ยเคลื่อนที่ หรือผลต่างจาก LAG — ต้องกรองด้วยวิธีเดียวกัน
ในที่นี้ เราคำนวณยอดคงเหลือสะสม แล้วเก็บเฉพาะแถวที่ยอดดังกล่าวเกิน 1000 เป็นครั้งแรก ตัวกรองจะอยู่นอกชั้นหน้าต่าง
WITH balances AS (
SELECT
account_id, txn_date, amount,
SUM(amount) OVER (
PARTITION BY account_id ORDER BY txn_date
) AS running_balance
FROM transactions
)
SELECT *
FROM balances
WHERE running_balance > 1000;QUALIFY: ทางลัดในฐานข้อมูลบางระบบ
Snowflake, BigQuery, Teradata และ DuckDB มีส่วนคำสั่ง QUALIFY ที่กรองผลลัพธ์จากหน้าต่างได้โดยตรง จึงไม่ต้องใช้ตัวห่อ ส่วนคำสั่งนี้ทำงานหลังฟังก์ชันหน้าต่าง ซึ่งตรงกับตำแหน่งที่ต้องการพอดี
กล่าวถึง QUALIFY เพื่อแสดงให้เห็นว่าคุณมีความรู้รอบด้าน แต่ควรระบุด้วยว่าส่วนคำสั่งนี้ไม่ใช่มาตรฐาน SQL และไม่มีใน PostgreSQL, MySQL และ SQL Server ซึ่งยังต้องใช้คิวรีย่อยหรือ CTE ห่อหุ้มอยู่
-- Snowflake / BigQuery only:
SELECT department, name, salary
FROM employees
QUALIFY ROW_NUMBER() OVER (
PARTITION BY department ORDER BY salary DESC
) = 1;อย่าสับสนระหว่าง HAVING กับการกรองฟังก์ชันหน้าต่าง
ผู้สมัครบางคนพยายามใช้ HAVING เพื่อกรองอันดับ HAVING กรองกลุ่มหลังการรวมกลุ่มด้วย GROUP BY และยังทำงานก่อนฟังก์ชันหน้าต่าง จึงไม่สามารถอ้างอิงคอลัมน์จากหน้าต่างได้เช่นกัน
WHERE→ กรองแถวก่อนการจัดกลุ่มและก่อนหน้าต่างHAVING→ กรองกลุ่มที่รวมแล้ว แต่ยังทำงานก่อนหน้าต่าง- การกรองหน้าต่าง → ต้องใช้คิวรีภายนอก (หรือ
QUALIFY)
การรวมตัวกรองล่วงหน้ากับตัวกรองฟังก์ชันหน้าต่าง
บ่อยครั้งคุณจะกรองทั้งก่อนและหลังหน้าต่าง ให้ใช้ตัวกรองแถวทั่วไปใน WHERE ของคิวรีด้านใน (เพื่อให้หน้าต่างเห็นเฉพาะแถวที่เกี่ยวข้อง) แล้วจึงกรองผลลัพธ์จากหน้าต่างในคิวรีด้านนอก
ในตัวอย่างนี้ เราจำกัดข้อมูลให้เหลือพนักงานที่ยังปฏิบัติงานอยู่ก่อน แล้วจึงเลือกผู้มีเงินเดือนสูงสุดของแต่ละแผนกจากกลุ่มพนักงานเหล่านั้น การใส่ WHERE active ไว้ด้านในจะเปลี่ยนแถวที่ถูกนำมาจัดอันดับ
WITH ranked AS (
SELECT department, name, salary,
ROW_NUMBER() OVER (
PARTITION BY department ORDER BY salary DESC
) AS rn
FROM employees
WHERE is_active = true -- pre-filter before ranking
)
SELECT * FROM ranked
WHERE rn = 1; -- post-filter on the windowหมายเหตุด้านประสิทธิภาพ
ผู้สัมภาษณ์อาจถามว่าการใช้ตัวห่อทำให้ประสิทธิภาพลดลงหรือไม่ โดยทั่วไปแล้วไม่ลดลง เพราะตัวเพิ่มประสิทธิภาพมองคิวรีย่อยหรือ CTE เป็นส่วนหนึ่งของแผนการทำงานเดียว และคำนวณหน้าต่างเพียงครั้งเดียว การห่อคิวรีไม่ได้ทำให้เกิดการสแกนเพิ่มเติม
ข้อควรระวังคือ ในบางระบบ CTE อาจเป็นแนวกั้นการเพิ่มประสิทธิภาพ (มีการสร้างผลลัพธ์ไว้ในหน่วยความจำ) ดังนั้นสำหรับเส้นทางการทำงานที่มีการเรียกใช้มาก ตารางที่ได้จากคิวรีหรือ QUALIFY อาจวางแผนได้ดีกว่า หากเรื่องนี้สำคัญ ให้ตรวจสอบด้วย EXPLAIN
ข้อผิดพลาดที่พบบ่อย
รายการตรวจสอบสุดท้าย:
- อย่าวางฟังก์ชันหน้าต่างใน
WHERE/HAVINGเด็ดขาด เพราะจะเกิดข้อผิดพลาด - ตั้งชื่อแทนให้ตารางที่ได้จากคิวรีเสมอ คิวรีย่อยที่ไม่มีชื่อใน
FROMจะถูกปฏิเสธ - เลือกฟังก์ชันจัดอันดับให้ตรงกับวิธีจัดการค่าที่เท่ากันตามที่คำถามต้องการ
- ใช้
QUALIFYเฉพาะในระบบที่รองรับ หากไม่รองรับ ให้ใช้ CTE หรือคิวรีย่อยห่อหุ้มแทน
ตรวจสอบความเข้าใจอย่างรวดเร็ว
เหตุใดการกรองฟังก์ชันหน้าต่างจึงต้องใช้คิวรีห่อหุ้ม
สรุป: การกรองผลลัพธ์จากหน้าต่าง
คุณได้เรียนรู้กระบวนการจัดอันดับด้วยฟังก์ชันหน้าต่างครบถ้วนแล้ว:
- ฟังก์ชันหน้าต่างทำงานหลัง
WHERE/GROUP BY/HAVINGดังนั้นคุณจึงกรองด้วยฟังก์ชันเหล่านี้โดยตรงไม่ได้ - ห่อฟังก์ชันหน้าต่างไว้ในคิวรีย่อยหรือCTE (ต้องตั้งชื่อแทนเสมอ) แล้วกรองผลลัพธ์ในคิวรีด้านนอก
- วิธีนี้ใช้กับการเลือก N อันดับแรกต่อกลุ่ม แถวล่าสุดต่อคีย์ และเกณฑ์ของผลรวมสะสม
QUALIFYเป็นทางลัดที่ไม่ใช่มาตรฐานและมีประโยชน์เฉพาะใน Snowflake/BigQuery
ตอนนี้คุณมีชุดเครื่องมือด้านการจัดอันดับครบถ้วนสำหรับหัวข้อที่ผู้สัมภาษณ์มักทดสอบแล้ว
คำถามที่พบบ่อย
บทเรียน “กรองจากผลลัพธ์ของฟังก์ชันวินโดว์” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “กรองจากผลลัพธ์ของฟังก์ชันวินโดว์” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส SQL Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส SQL Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “กรองจากผลลัพธ์ของฟังก์ชันวินโดว์”
เหตุใดจึงต้องห่อฟังก์ชันวินโดว์ไว้ในคิวรีย่อยหรือ CTE เพื่อกรองจากผลลัพธ์นั้น คุณปฏิบัติ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- OVER, PARTITION BY และ ORDER BY
- ROW_NUMBER สำหรับลำดับที่ไม่ซ้ำกัน
- RANK เทียบกับ DENSE_RANK เมื่อค่าซ้ำกัน
- กรองจากผลลัพธ์ของฟังก์ชันวินโดว์