สำคัญ: โปรดพิจารณาอัปเกรดเป็น MySQL 8+ และใช้ฟังก์ชั่น ROW_NUMBER () ที่กำหนดและมีการบันทึกไว้และคัดแฮ็กแฮ็กเก่าที่เชื่อมโยงกับฟีเจอร์ จำกัด รุ่นโบราณของ MySQL
ตอนนี้นี่เป็นหนึ่งในแฮ็กเหล่านั้น:
คำตอบที่นี่ที่ใช้ตัวแปรในแบบสอบถามส่วนใหญ่ / ทั้งหมดดูเหมือนจะไม่สนใจข้อเท็จจริงที่ว่าเอกสารอธิบาย (ถอดความ)
อย่าพึ่งพารายการในรายการ SELECT ที่ได้รับการประเมินตามลำดับจากบนลงล่าง อย่ากำหนดตัวแปรในรายการ SELECT หนึ่งรายการและใช้ในอีกรายการหนึ่ง
เช่นนี้มีความเสี่ยงที่พวกเขาจะปั่นป่วนคำตอบที่ผิดเพราะพวกเขามักจะทำ
select
(row number variable that uses partition variable),
(assign partition variable)
ถ้าสิ่งเหล่านี้ถูกประเมินจากล่างขึ้นบนหมายเลขแถวจะหยุดทำงาน (ไม่มีพาร์ติชัน)
ดังนั้นเราต้องใช้สิ่งที่มีคำสั่งรับประกันการดำเนินการ ใส่เคสเมื่อ:
SELECT
t.*,
@r := CASE
WHEN col = @prevcol THEN @r + 1
WHEN (@prevcol := col) = null THEN null
ELSE 1 END AS rn
FROM
t,
(SELECT @r := 0, @prevcol := null) x
ORDER BY col
ในฐานะที่เป็นเค้าร่าง ld ลำดับการกำหนดของ prevcol มีความสำคัญ - prevcol จะต้องเปรียบเทียบกับค่าของแถวปัจจุบันก่อนที่เราจะกำหนดค่าจากแถวปัจจุบัน (มิฉะนั้นจะเป็นค่า col แถวปัจจุบันไม่ใช่ค่า col ของแถวก่อนหน้า) .
นี่คือวิธีการรวมกัน:
เมื่อมีการประเมินครั้งแรก หาก col ของแถวนี้เหมือนกับ col ของแถวก่อนหน้านี้ @r จะเพิ่มขึ้นและส่งคืนจาก CASE ค่านำที่ส่งคืนนี้ถูกเก็บไว้ใน @r มันเป็นคุณสมบัติของ MySQL ที่การมอบหมายจะส่งกลับค่าใหม่ของสิ่งที่ถูกกำหนดลงใน @r ลงในแถวผลลัพธ์
สำหรับแถวแรกของชุดผลลัพธ์ @prevcol เป็นโมฆะ (มันถูกกำหนดค่าเริ่มต้นเป็นโมฆะในเคียวรีย่อย) ดังนั้นเพรดิเคตนี้จึงเป็นเท็จ เพรดิเคตแรกนี้จะส่งกลับค่า false ทุกครั้งที่มีการเปลี่ยนแปลง col (แถวปัจจุบันแตกต่างจากแถวก่อนหน้า) นี่เป็นสาเหตุที่ทำให้ WHEN ที่สองถูกประเมิน
เมื่อเพรดิเคตที่สองเป็นเท็จเสมอและมีอยู่จริงเพื่อกำหนดค่าใหม่ให้กับ @prevcol เนื่องจากคอลัมน์ของแถวนี้แตกต่างจากคอลัมน์ของแถวก่อนหน้า (เรารู้สิ่งนี้เพราะหากเหมือนกันคอลัมน์แรกที่จะถูกใช้) เราจึงต้องกำหนดค่าใหม่เพื่อใช้ในการทดสอบครั้งต่อไป เนื่องจากการมอบหมายนั้นเกิดขึ้นและผลลัพธ์ของการมอบหมายนั้นถูกเปรียบเทียบกับค่าว่างและสิ่งใดก็ตามที่มีค่าเท่ากับ null นั้นเป็นเท็จภาคแสดงนี้จึงเป็นเท็จเสมอ แต่อย่างน้อยก็ประเมินว่ามันทำงานเพื่อรักษาคุณค่าของ col จากแถวนี้ดังนั้นจึงสามารถประเมินกับค่า col ของแถวถัดไป
เนื่องจาก WHEN ที่สองเป็นเท็จมันหมายถึงในสถานการณ์ที่คอลัมน์ที่เราแบ่งพาร์ติชันโดย (col) มีการเปลี่ยนแปลงมันเป็น ELSE ที่ให้ค่าใหม่สำหรับ @r เริ่มหมายเลขจาก 1
พวกเราไปถึงสถานการณ์ที่สิ่งนี้:
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY pcol1, pcol2, ... pcolX ORDER BY ocol1, ocol2, ... ocolX) rn
FROM
t
มีรูปแบบทั่วไป:
SELECT
t.*,
@r := CASE
WHEN col1 = @pcol1 AND col2 = @pcol2 AND ... AND colX = @pcolX THEN @r + 1
WHEN (@pcol1 := pcol1) = null OR (@pcol2 := col2) = null OR ... OR (@pcolX := colX) = null THEN null
ELSE 1
END AS rn
FROM
t,
(SELECT @r := 0, @pcol1 := null, @pcol2 := null, ..., @pcolX := null) x
ORDER BY pcol1, pcol2, ..., pcolX, ocol1, ocol2, ..., ocolX
เชิงอรรถ:
p ใน pcol หมายถึง "พาร์ติชัน", o ใน ocol หมายถึง "คำสั่ง" - ในรูปแบบทั่วไปฉันลด "prev" จากชื่อตัวแปรเพื่อลดความยุ่งเหยิงที่มองเห็น
วงเล็บที่(@pcolX := colX) = null
มีความสำคัญ หากไม่มีพวกเขาคุณจะกำหนด null ให้กับ @pcolX และสิ่งต่างๆจะหยุดทำงาน
มันเป็นเรื่องประนีประนอมที่จะต้องมีการสั่งซื้อชุดผลลัพธ์โดยคอลัมน์พาร์ติชันด้วยเช่นกันสำหรับคอลัมน์ก่อนหน้านี้เปรียบเทียบกับการทำงาน คุณไม่สามารถสั่งให้ rownumber ของคุณตามคอลัมน์หนึ่งได้ แต่ชุดผลลัพธ์ของคุณสั่งให้ไปที่อื่นคุณอาจแก้ไขปัญหานี้ได้ด้วยแบบสอบถามย่อย แต่ฉันเชื่อว่าเอกสารยังระบุว่าการสั่งซื้อแบบสอบถามย่อยอาจถูกละเว้นเว้นแต่จะใช้ LIMIT และอาจส่งผลกระทบ ประสิทธิภาพ
ฉันไม่ได้เจาะลึกเกินกว่าการทดสอบว่าวิธีนี้ใช้ได้ผล แต่ถ้ามีความเสี่ยงที่เพรดิเคตในวินาทีที่จะเพิ่มประสิทธิภาพ (สิ่งใดเทียบกับโมฆะนั้นเป็นโมฆะ / เท็จดังนั้นทำไมต้องทำงานมอบหมาย) และไม่ทำงาน มันก็หยุด สิ่งนี้ดูเหมือนจะไม่เกิดขึ้นในประสบการณ์ของฉัน แต่ฉันยินดีรับความคิดเห็นและเสนอวิธีการแก้ปัญหาหากมันเกิดขึ้นอย่างสมเหตุสมผล
อาจเป็นการดีที่จะส่งค่า null ที่สร้าง @pcolX ไปยังประเภทคอลัมน์จริงของคุณในแบบสอบถามย่อยที่สร้างตัวแปร @pcolX ได้แก่ : select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)
greatest-n-per-group
เพื่อแนะนำคุณถึงคำถามที่คล้ายกัน