แถวต่อเนื่อง N แถวที่ตรงตามเงื่อนไข
รูปแบบช่วงแบบคลาสสิก เช่น ยอดขายเกิน X เป็นเวลา 3 วันติดต่อกัน
แถวต่อเนื่อง N แถวที่ตรงตามเงื่อนไข เป็นบทเรียน SQL Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน SQL Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส SQL Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
โจทย์คลาสสิกของ LeetCode
นี่เป็นหนึ่งในโจทย์สัมภาษณ์ SQL ที่ถูกถามบ่อยที่สุด: "ค้นหาวันที่ทั้งหมดที่มี สามวันต่อเนื่องเป็นอย่างน้อย ซึ่งยอดขายสูงกว่าค่าขีดจำกัด" หรือโจทย์ยอดนิยมของ LeetCode ที่ให้ "รายงานสนามกีฬาที่มีแถวผู้เข้าชมอย่างน้อย 3 แถวต่อเนื่อง โดยแต่ละแถวมีจำนวนผู้เข้าชมมากกว่า 100 คน"
รูปแบบของโจทย์เหมือนกันเสมอ: แถวหนึ่งจะผ่านเกณฑ์ก็ต่อเมื่ออยู่ภายในช่วงของแถวที่ผ่านเกณฑ์ต่อเนื่องกัน N แถว บทเรียนนี้จะแสดงวิธีแก้ที่กระชับสองวิธี พร้อมกับกับดักที่ทำให้ผู้สมัครส่วนใหญ่ตอบผิด
ข้อมูลตัวอย่าง
เราจะใช้ตาราง sales รายวัน โดยมีเงื่อนไขเป็น amount > 100 เราต้องคืนค่าทุกวันที่อยู่ในช่วงของ วันตามปฏิทินที่ต่อเนื่องกันตั้งแต่ 3 วันขึ้นไป และทุกวันต้องตรงตามเงื่อนไข
sale_date— หนึ่งแถวต่อวันamount— ยอดขายรวมของวันนั้น
จุดละเอียดสำคัญคือ แถวต้องต่อเนื่องกันตาม ลำดับ และสำหรับรูปแบบที่อิงวันที่ แถวนั้นต้องต่อเนื่องกันตาม ปฏิทินด้วย
SELECT * FROM sales ORDER BY sale_date;
-- sale_date | amount
-- 2024-03-01 | 120
-- 2024-03-02 | 150
-- 2024-03-03 | 130
-- 2024-03-04 | 90
-- 2024-03-05 | 200แนวทางที่ 1: กรองแล้วจัดกลุ่มช่วงต่อเนื่อง
แนวทางที่เชื่อถือได้คือ เก็บเฉพาะแถวที่ตรงตามเงื่อนไขก่อน จากนั้นจัดกลุ่มแถวที่เหลือเป็นกลุ่มช่วงต่อเนื่อง แล้วเก็บเฉพาะกลุ่มที่มีความยาวอย่างน้อย N
ขั้นแรกคือการกรองด้วย WHERE ขั้นที่สองนำจุดยึดสำหรับปัญหาช่องว่างและกลุ่มช่วงต่อเนื่องกลับมาใช้ เนื่องจากเรากรองก่อน กลุ่มช่วงต่อเนื่องในที่นี้จึงหมายถึง "ช่วงของวันที่ต่อเนื่องกันและตรงตามเงื่อนไข"
WITH qualifying AS (
SELECT sale_date
FROM sales
WHERE amount > 100
)
SELECT * FROM qualifying ORDER BY sale_date;การกำหนดจุดยึดให้ช่วงที่ตรงตามเงื่อนไข
กำหนดหมายเลขให้แถวที่ตรงตามเงื่อนไขตามวันที่ แล้วลบออกเพื่อหาจุดยึด แถวที่ต่อเนื่องกันตามปฏิทิน AND ตรงตามเงื่อนไขทั้งหมดจะมีจุดยึดเดียวกัน ส่วนวันที่ไม่ตรงตามเงื่อนไขจะถูกนำออก ทำให้ช่วงต่อเนื่องขาดตรงตำแหน่งที่ควรขาดพอดี
WITH qualifying AS (
SELECT sale_date
FROM sales
WHERE amount > 100
),
numbered AS (
SELECT sale_date,
ROW_NUMBER() OVER (ORDER BY sale_date) AS rn
FROM qualifying
)
SELECT sale_date, sale_date - rn AS grp
FROM numbered;การเก็บกลุ่มที่ยาวเพียงพอ
จัดกลุ่มตามจุดยึด นับจำนวนแถว แล้วเก็บเฉพาะกลุ่มที่มี COUNT(*) >= 3 หากผู้สัมภาษณ์ต้องการวันที่ที่ตรงตามเงื่อนไขแต่ละวันกลับมา ให้เชื่อมจุดยึดที่เก็บไว้เข้ากับแถวที่มีหมายเลข
WITH qualifying AS (
SELECT sale_date FROM sales WHERE amount > 100
),
numbered AS (
SELECT sale_date,
ROW_NUMBER() OVER (ORDER BY sale_date) AS rn
FROM qualifying
),
islands AS (
SELECT sale_date - rn AS grp, COUNT(*) AS len
FROM numbered
GROUP BY sale_date - rn
HAVING COUNT(*) >= 3
)
SELECT n.sale_date
FROM numbered n
JOIN islands i ON n.sale_date - n.rn = i.grp
ORDER BY n.sale_date;แนวทางที่ 2: หน้าต่าง COUNT แบบเลื่อน
แนวทางที่กระชับกว่าเมื่อ N มีค่าต่ำและกำหนดตายตัว คือใช้กรอบหน้าต่างเพื่อนับว่ามีแถวรอบข้างกี่แถวที่ตรงตามเงื่อนไข หากหน้าต่างใดที่มี N แถวต่อเนื่องและรวมแถวนี้มีทุกแถวตรงตามเงื่อนไข แถวนั้นก็อยู่ในผลลัพธ์
ขั้นแรกให้เพิ่มเครื่องหมายบูลีน จากนั้นหาผลรวมของเครื่องหมายนั้นบนกรอบหน้าต่างแบบเลื่อน
SELECT sale_date, amount,
CASE WHEN amount > 100 THEN 1 ELSE 0 END AS ok
FROM sales;การหาผลรวมบนกรอบสามแบบ
สำหรับช่วงต่อเนื่องที่มี 3 แถวพอดี แถวที่ตรงตามเงื่อนไขจะอยู่ในผลลัพธ์ หากผลรวมของหน้าต่าง 3 แถวที่สิ้นสุดตรงแถวนี้ อยู่กึ่งกลางที่แถวนี้ หรือเริ่มต้นที่แถวนี้ มีค่าเป็น 3 ให้คำนวณผลรวมแบบเลื่อนทั้งสามแบบ แล้วตรวจสอบว่ามีแบบใดมีค่าเท่ากับ 3 หรือไม่
นี่คือเทคนิคที่อยู่เบื้องหลังคำตอบของ LeetCode 601 (การจราจรมนุษย์ในสนามกีฬา)
WITH flagged AS (
SELECT sale_date, amount,
CASE WHEN amount > 100 THEN 1 ELSE 0 END AS ok
FROM sales
),
w AS (
SELECT *,
SUM(ok) OVER (ORDER BY sale_date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS s_end,
SUM(ok) OVER (ORDER BY sale_date
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS s_mid,
SUM(ok) OVER (ORDER BY sale_date
ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) AS s_start
FROM flagged
)
SELECT sale_date, amount
FROM w
WHERE ok = 1 AND (s_end = 3 OR s_mid = 3 OR s_start = 3);กับดักเรื่องช่องว่างของปฏิทิน
แนวทางผลรวมบนหน้าต่างใช้ ROWS ซึ่งนับแถวผลลัพธ์ที่อยู่ติดกัน ไม่ใช่วันที่ตามปฏิทินที่อยู่ติดกัน หากกรองวันที่ไม่ตรงตามเงื่อนไขออกไปแล้ว แถวสองแถวอาจอยู่ติดกันในผลลัพธ์ แต่ไม่ใช่วันที่ต่อเนื่องกันตามปฏิทิน
บทเรียน: ใช้หน้าต่างแบบเลื่อนกับชุดข้อมูลรายวันทั้งหมด (อย่ากรองล่วงหน้า) หรือใช้วิธีจุดยึดวันที่ซึ่งคำนึงถึงช่องว่างของปฏิทินโดยธรรมชาติ ควรอธิบายข้อแลกเปลี่ยนนี้ในการสัมภาษณ์
การทำให้ใช้ได้กับ N ใด ๆ
แนวทางที่ 1 (กรองแล้วจัดกลุ่มช่วงต่อเนื่อง) ทำให้ใช้ได้ทั่วไปอย่างง่ายดาย เพียงเปลี่ยน HAVING COUNT(*) >= N นี่คือข้อได้เปรียบสำคัญเหนือผลรวมจากหน้าต่างหลายชุด ซึ่งต้องเพิ่มกรอบหน้าต่างเมื่อ N มีค่ามากขึ้น
สำหรับ N ที่รับเป็นพารามิเตอร์หรือมีค่ามาก ควรเลือกวิธีกลุ่มช่วงต่อเนื่อง เพราะเปลี่ยนเพียงค่าขีดจำกัดเดียว แทนที่จะต้องเขียนหน้าต่างด้วยตนเองจำนวน N−1 ชุด
-- only the threshold changes for N = 5
HAVING COUNT(*) >= 5การเลือกแนวทาง
แนวทางตัดสินใจสั้น ๆ สำหรับอธิบายด้วยวาจา:
- กรองแล้วจัดกลุ่มช่วงต่อเนื่อง: คำนึงถึงช่องว่างของปฏิทิน ใช้ได้กับ N ใด ๆ และคืนค่าช่วงต่อเนื่องทั้งหมด — เป็นค่าเริ่มต้นที่ปลอดภัย
- ผลรวมของหน้าต่างแบบเลื่อน: สวยงามสำหรับ N ขนาดเล็กที่กำหนดตายตัวบนชุดข้อมูลรายวันที่หนาแน่น แต่ต้องระวังกับดักระหว่าง ROWS กับปฏิทิน
การกล่าวถึงทั้งสองวิธีแล้วให้เหตุผลประกอบว่าเลือกวิธีใด คือสิ่งที่ผู้สัมภาษณ์ระดับกลางถึงอาวุโสให้คุณค่าอย่างแท้จริง
คำตอบฉบับเต็ม
คำตอบที่ใช้ได้กับระบบต่าง ๆ และ N ใด ๆ ซึ่งคำนึงถึงความต่อเนื่องตามปฏิทินและคืนค่าวันที่ที่ตรงตามเงื่อนไข:
WITH qualifying AS (
SELECT sale_date FROM sales WHERE amount > 100
),
numbered AS (
SELECT sale_date,
ROW_NUMBER() OVER (ORDER BY sale_date) AS rn
FROM qualifying
),
islands AS (
SELECT sale_date - rn AS grp, COUNT(*) AS len
FROM numbered
GROUP BY sale_date - rn
HAVING COUNT(*) >= 3
)
SELECT n.sale_date
FROM numbered n
JOIN islands i ON n.sale_date - n.rn = i.grp
ORDER BY n.sale_date;ตรวจสอบอย่างรวดเร็ว
ค้นหาจุดผิดพลาดเล็กน้อยที่ซ่อนอยู่
สรุป
สำหรับ N แถวต่อเนื่องที่ตรงตามเงื่อนไข:
- กรองแล้วจัดกลุ่มช่วงต่อเนื่อง: เก็บแถวที่ตรงตามเงื่อนไข ใช้จุดยึดด้วย
date - ROW_NUMBER()จัดกลุ่ม และใช้HAVING COUNT(*) >= Nวิธีนี้ใช้ได้กับ N ใด ๆ และคำนึงถึงช่องว่างของปฏิทิน - ผลรวมของหน้าต่างแบบเลื่อน: ทำเครื่องหมายแถว แล้วหาผลรวมบนกรอบหน้าต่าง N แถวที่กำหนดตายตัว วิธีนี้กระชับ แต่ต้องระวังความแตกต่างระหว่าง ROWS กับปฏิทินเมื่อใช้กับข้อมูลที่กรองล่วงหน้า
ถัดไป: การคำนวณช่วงต่อเนื่องที่กำลังใช้งานของผู้ใช้ ณ วันนี้
คำถามที่พบบ่อย
บทเรียน “แถวต่อเนื่อง N แถวที่ตรงตามเงื่อนไข” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “แถวต่อเนื่อง N แถวที่ตรงตามเงื่อนไข” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส SQL Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส SQL Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “แถวต่อเนื่อง N แถวที่ตรงตามเงื่อนไข”
รูปแบบช่วงแบบคลาสสิก เช่น ยอดขายเกิน X เป็นเวลา 3 วันติดต่อกัน คุณปฏิบัติ SQL Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน SQL Interview Prep หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน SQL Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “แถวต่อเนื่อง N แถวที่ตรงตามเงื่อนไข” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน SQL Interview Prep นี้ได้ไหม
ได้ บทเรียน SQL Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- ตรวจจับวันที่ในปฏิทินที่ต่อเนื่องกัน
- ช่วงต่อเนื่องที่ยาวที่สุดของผู้ใช้แต่ละคน
- แถวต่อเนื่อง N แถวที่ตรงตามเงื่อนไข
- ช่วงต่อเนื่องที่ยังดำเนินอยู่จนถึงวันนี้