เทคนิคผลต่างของหมายเลขแถว
ลบ ROW_NUMBER ออกจากลำดับ เพื่อจัดกลุ่มค่าต่อเนื่องให้เป็นกลุ่มต่อเนื่อง
เทคนิคผลต่างของหมายเลขแถว เป็นบทเรียน Coding Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Coding Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
คีย์กลุ่มต่อเนื่องที่เรียบง่ายที่สุด
เทคนิคผลต่างของหมายเลขแถวเป็นวิธีที่ผู้สัมภาษณ์อยากเห็นมากที่สุดสำหรับกลุ่มต่อเนื่องของ จำนวนเต็มหรือวันที่ที่ต่อเนื่องกัน วิธีนี้สร้างคีย์กลุ่มด้วยการลบเพียงครั้งเดียว โดยไม่ต้องใช้ LAG หรือผลรวมสะสม
แนวคิดทั้งหมดคือ ลบ ROW_NUMBER ออกจากค่าของข้อมูลเอง สำหรับลำดับค่าที่ต่อเนื่องกัน ทุกขั้นค่าของข้อมูลและหมายเลขแถวจะเพิ่มขึ้นทีละ 1 พอดี ดังนั้น ผลต่างจึงคงที่ตลอดทั้งลำดับ ค่าคงที่นั้นคือคีย์ของกลุ่มต่อเนื่อง
เหตุใดผลต่างจึงคงที่
ลองพิจารณาแถวสองแถวที่อยู่ติดกันในลำดับต่อเนื่อง เมื่อเลื่อนจากแถวหนึ่งไปยังแถวถัดไป ค่าของข้อมูลจะเพิ่มขึ้น 1 และ หมายเลขแถวก็เพิ่มขึ้น 1 เช่นกัน เมื่อนำมาลบกัน ค่า +1 ทั้งสองจะหักล้างกัน ดังนั้น value - row_number จึงไม่เปลี่ยนแปลง
แต่ทันทีที่เกิดช่องว่าง ค่าของข้อมูลจะเพิ่มขึ้นมากกว่า 1 ขณะที่หมายเลขแถวยังคงเพิ่มขึ้นเพียง 1 ผลต่างจึงเปลี่ยนไปเป็นค่าคงที่ชุดใหม่ การเปลี่ยนแปลงนั้นคือสิ่งที่แบ่งกลุ่มต่อเนื่องหนึ่งกลุ่มออกจากกลุ่มถัดไปได้พอดี
ดูจากชุดข้อมูลของเรา
ลองนึกถึงวันที่เข้าสู่ระบบ 1, 2, 3, 7, 8, 10 แล้วจัดหมายเลขแถวกับผลต่างไว้ข้างกัน:
- วันที่ 1, ลำดับแถว 1, ผลต่าง 0
- วันที่ 2, ลำดับแถว 2, ผลต่าง 0
- วันที่ 3, ลำดับแถว 3, ผลต่าง 0
- วันที่ 7, ลำดับแถว 4, ผลต่าง 3
- วันที่ 8, ลำดับแถว 5, ผลต่าง 3
- วันที่ 10, ลำดับแถว 6, ผลต่าง 4
ผลต่าง (0,0,0,3,3,4) แบ่งแถวออกเป็นกลุ่มต่อเนื่องสามกลุ่มได้อย่างพอดี ผลต่างเดียวกันหมายถึงกลุ่มเดียวกัน
SELECT
day_no,
ROW_NUMBER() OVER (ORDER BY day_no) AS rn,
day_no - ROW_NUMBER() OVER (ORDER BY day_no) AS grp
FROM logins
ORDER BY day_no;รวมข้อมูลให้เป็นกลุ่มต่อเนื่อง
เมื่อใช้ผลต่างเป็นคีย์กลุ่ม แบบสอบถามสุดท้ายก็เป็นการรวมข้อมูลตามรูปแบบมาตรฐาน ครอบผลต่างไว้ใน CTE แล้วใช้ GROUP BY กับผลต่างนั้น:
ผลลัพธ์เป็นกลุ่มต่อเนื่องสามกลุ่มเดิม แต่ SQL สั้นและชัดเจนกว่าแบบที่ใช้ LAG ร่วมกับผลรวมสะสม สำหรับลำดับจำนวนเต็มหรือลำดับที่เพิ่มขึ้นเป็นช่วงเท่ากัน นี่คือคำตอบแรกที่ควรเลือก
WITH keyed AS (
SELECT
day_no,
day_no - ROW_NUMBER() OVER (ORDER BY day_no) AS grp
FROM logins
)
SELECT
MIN(day_no) AS start_day,
MAX(day_no) AS end_day,
COUNT(*) AS length
FROM keyed
GROUP BY grp
ORDER BY start_day;ข้อควรระวัง: ค่าต้องเพิ่มทีละหนึ่ง
เทคนิคการหาผลต่างแบบตรงไปตรงมาสมมติว่าลำดับเพิ่มขึ้น พอดี 1 ในแต่ละขั้น สมมตินี้เป็นจริงสำหรับจำนวนเต็มที่ไม่มีช่องว่างและวันที่ตามปฏิทินที่เรียงต่อเนื่องกัน แต่จะใช้ไม่ได้หากค่าเพิ่มทีละจำนวนคงที่อื่น หรือมีค่าซ้ำ
- แม้แต่ค่าเลขคู่ 2,4,6,8 ก็จะดูเหมือนมีช่องว่างเมื่อใช้การลบค่าออกจากหมายเลขแถว
- ค่าซ้ำทำให้การจัดแนวคลาดเคลื่อน เพราะหมายเลขแถวยังคงเพิ่มขึ้นในขณะที่ค่าไม่เพิ่ม
การเข้าใจข้อจำกัดนี้และวิธีแก้ไข คือสิ่งที่แยกการจำเทคนิคจากความเข้าใจอย่างแท้จริง
แก้ไขลำดับที่เพิ่มด้วยขั้นคงที่
หากค่าเพิ่มทีละค่าคงที่ k แทนที่จะเป็น 1 ให้ปรับมาตรฐานก่อน โดยหารค่าด้วย k (หรือใช้ value / k สำหรับจำนวนเต็ม) เพื่อให้แต่ละขั้นกลับมาเป็น 1 แล้วจึงลบหมายเลขแถว
ตัวอย่างเช่น สำหรับจำนวนคู่ที่เพิ่มทีละ 2 ให้ใช้ day_no / 2 - ROW_NUMBER() ค่าที่ปรับมาตรฐานแล้วจะเพิ่มขึ้นทีละ 1 ในแต่ละรายการที่ต่อเนื่องกัน ทำให้คุณสมบัติผลต่างคงที่กลับมาอีกครั้ง
SELECT
val,
(val / 2) - ROW_NUMBER() OVER (ORDER BY val) AS grp
FROM even_series
ORDER BY val;นำไปใช้กับวันที่
วันที่เป็นกรณีใช้งานจริงที่พบได้บ่อยที่สุด วันที่ตามปฏิทินไม่สามารถนำไปลบจากหมายเลขแถวได้โดยตรง จึงต้องแปลงวันที่เป็นจำนวนวันก่อน ในโพสต์เกรส ให้ลบวันที่อ้างอิงคงที่เพื่อให้ได้จำนวนวันเป็นจำนวนเต็ม แล้วจึงใช้เทคนิคเดิม
เนื่องจากวันที่ตามปฏิทินที่ต่อเนื่องกันต่างกัน 1 วัน ผลต่างระหว่างจำนวนวันกับหมายเลขแถวจึงคงที่ภายในแต่ละช่วงต่อเนื่องอีกครั้ง
WITH keyed AS (
SELECT
login_date,
(login_date - DATE '2000-01-01')
- ROW_NUMBER() OVER (ORDER BY login_date) AS grp
FROM daily_logins
)
SELECT MIN(login_date) AS start_date,
MAX(login_date) AS end_date,
COUNT(*) AS days_in_run
FROM keyed GROUP BY grp ORDER BY start_date;การหาผลต่างของวันที่ข้ามระบบ
ขั้นตอนแปลงวันที่เป็นจำนวนเต็มแตกต่างกันไปตามระบบฐานข้อมูล และผู้สัมภาษณ์มักชื่นชมความเข้าใจการเขียนข้ามระบบ:
- โพสต์เกรส: ลบค่าคงที่วันที่:
login_date - DATE '2000-01-01'จะได้จำนวนเต็ม - MySQL: ใช้
DATEDIFF(login_date, '2000-01-01') - เซิร์ฟเวอร์เอสคิวแอล: ใช้
DATEDIFF(day, '2000-01-01', login_date)
ในบางระบบยังมีวิธีที่กระชับยิ่งกว่า คือใช้การคำนวณช่วงเวลาลบจำนวนวันตาม ROW_NUMBER ออกจากวันที่โดยตรง จากนั้นจัดกลุ่มด้วย GROUP BY ตามวันที่อ้างอิงที่ได้
SELECT
login_date,
login_date - (ROW_NUMBER() OVER (ORDER BY login_date)
* INTERVAL '1 day') AS grp_date
FROM daily_logins;เพิ่มการแบ่งส่วนตามกลุ่ม
สำหรับช่วงต่อเนื่องแยกตามผู้ใช้ ให้แบ่งหมายเลขแถวตามคอลัมน์กลุ่ม ที่สำคัญ คีย์กลุ่มต้องรวมคอลัมน์ที่ใช้แบ่งส่วนด้วย เพราะผู้ใช้สองคนอาจบังเอิญได้ค่าผลต่างเดียวกัน
ดังนั้นให้ GROUP BY ทั้ง user_id และผลต่างที่คำนวณได้ การลืมคอลัมน์รหัสผู้ใช้ใน GROUP BY ขั้นสุดท้ายเป็นข้อผิดพลาดเล็กน้อยที่ผู้สัมภาษณ์ชอบตรวจพบ
WITH keyed AS (
SELECT user_id, day_no,
day_no - ROW_NUMBER()
OVER (PARTITION BY user_id ORDER BY day_no) AS grp
FROM logins
)
SELECT user_id, MIN(day_no) AS start_day,
MAX(day_no) AS end_day, COUNT(*) AS len
FROM keyed
GROUP BY user_id, grp
ORDER BY user_id, start_day;เทคนิคผลต่างเทียบกับ LAG: ควรใช้แบบใด
ตอนนี้คุณมีเทคนิคที่ใช้งานได้ดีสองแบบในชุดเครื่องมือแล้ว จงเลือกใช้ให้เหมาะสม:
- ผลต่างระหว่างหมายเลขแถวกับค่า: สั้นและสะอาดที่สุดสำหรับชุดค่าที่เพิ่มด้วยขั้นเท่ากัน (จำนวนเต็มที่ไม่มีช่องว่างและวันที่ต่อเนื่องกัน) ควรเลือกเป็นอันดับแรกเมื่อการอยู่ติดกันหมายถึง ต่างกันด้วยค่าคงที่
- LAG บวกผลรวมสะสม: ยืดหยุ่นกว่าเมื่อการอยู่ติดกันไม่ได้หมายถึงขั้นตัวเลขคงที่ เช่น สถานะเดียวกับแถวก่อนหน้า หรือกฎเฉพาะที่ไม่สม่ำเสมอ
ในการสัมภาษณ์ให้บอกว่าคุณเลือกวิธีใดและเพราะเหตุใด เหตุผลในการเลือกจะสร้างความประทับใจได้มากกว่าไวยากรณ์
รับมือค่าซ้ำอย่างรัดกุม
หากค่าหนึ่งสามารถซ้ำได้ แต่คุณยังต้องการหนึ่งช่วงต่อเนื่องต่อการเรียงติดกันแต่ละครั้ง ให้ตัดค่าซ้ำออกก่อนด้วย DISTINCT หรือขั้นตอนการจัดกลุ่ม เพื่อให้หมายเลขแถวสอดคล้องกับค่าทีละหนึ่งต่อหนึ่ง อีกทางเลือกคือใช้ DENSE_RANK แทน ROW_NUMBER เพื่อให้ค่าที่เท่ากันใช้ลำดับเดียวกัน
ถามผู้สัมภาษณ์เสมอว่ามีค่าซ้ำเกิดขึ้นได้หรือไม่ เพราะวิธีรับมือที่เหมาะสมขึ้นอยู่กับว่าควรนับค่าซ้ำให้ช่วงต่อเนื่องยาวขึ้น หรือควรละเว้นค่าซ้ำภายในช่วงนั้น
WITH d AS (SELECT DISTINCT day_no FROM logins)
SELECT day_no,
day_no - ROW_NUMBER() OVER (ORDER BY day_no) AS grp
FROM d;ตรวจสอบความเข้าใจ
ตรวจสอบให้แน่ใจว่าคุณเข้าใจว่าเทคนิคนี้ทำงานได้อย่างไร
ทบทวน: เทคนิคผลต่าง
ตอนนี้คุณมีคีย์สำหรับระบุช่วงต่อเนื่องที่สะอาดที่สุดแล้ว:
- สูตรคีย์:
value - ROW_NUMBER() OVER (ORDER BY value)จะมีค่าคงที่ภายในแต่ละช่วงที่ต่อเนื่องกัน - รวมกลุ่มด้วย
GROUP BYตามผลต่าง เพื่อหาจุดเริ่มต้น จุดสิ้นสุด และความยาว - สำหรับลำดับที่มีขั้นคงที่ ให้ ปรับมาตรฐาน (หารด้วยขนาดขั้น) ก่อน
- สำหรับวันที่ ให้แปลงเป็นจำนวนวันจำนวนเต็มด้วยฟังก์ชันหาผลต่างของระบบนั้น
- สำหรับแต่ละกลุ่ม: ใช้
PARTITION BYกับหมายเลขแถว และใส่คอลัมน์กลุ่มไว้ในGROUP BYขั้นสุดท้าย - ป้องกันค่าซ้ำด้วย
DISTINCTหรือDENSE_RANK
ต่อไปเราจะเปลี่ยนจากการค้นหาช่วงต่อเนื่องไปค้นหาพื้นที่ว่าง นั่นคือการหาช่องว่าง
คำถามที่พบบ่อย
บทเรียน “เทคนิคผลต่างของหมายเลขแถว” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “เทคนิคผลต่างของหมายเลขแถว” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Coding Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “เทคนิคผลต่างของหมายเลขแถว”
ลบ ROW_NUMBER ออกจากลำดับ เพื่อจัดกลุ่มค่าต่อเนื่องให้เป็นกลุ่มต่อเนื่อง คุณปฏิบัติ Coding Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Coding Interview Prep หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Coding Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “เทคนิคผลต่างของหมายเลขแถว” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Coding Interview Prep นี้ได้ไหม
ได้ บทเรียน Coding Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- รู้จักโจทย์ช่องว่างและกลุ่มต่อเนื่อง
- เทคนิคผลต่างของหมายเลขแถว
- ค้นหาช่องว่างในลำดับ
- กลุ่มต่อเนื่องเมื่อวันที่และสถานะเปลี่ยนแปลง