MySQL รับตำแหน่งแถวใน ORDER BY


88

ด้วยตาราง MySQL ต่อไปนี้:

+-----------------------------+
+ id INT UNSIGNED             +
+ name VARCHAR(100)           +
+-----------------------------+

ฉันจะเลือกเดียวแถวและตำแหน่งในหมู่แถวอื่น ๆ name ASCในตารางเมื่อเรียงตาม ดังนั้นหากข้อมูลตารางมีลักษณะเช่นนี้เมื่อจัดเรียงตามชื่อ:

+-----------------------------+
+ id | name                   +
+-----------------------------+
+  5 | Alpha                  +
+  7 | Beta                   +
+  3 | Delta                  +
+ .....                       +
+  1 | Zed                    +
+-----------------------------+

ฉันจะเลือกBetaแถวที่รับตำแหน่งปัจจุบันของแถวนั้นได้อย่างไร ชุดผลลัพธ์ที่ฉันกำลังมองหาจะเป็นดังนี้:

+-----------------------------+
+ id | position | name        +
+-----------------------------+
+  7 |        2 | Beta        +
+-----------------------------+

ฉันสามารถทำได้ง่ายๆSELECT * FROM tbl ORDER BY name ASCแล้วแจกแจงแถวใน PHP แต่ดูเหมือนว่าจะสิ้นเปลืองที่จะโหลดชุดผลลัพธ์ขนาดใหญ่สำหรับแถวเดียว



คำตอบ:


122

ใช้สิ่งนี้:

SELECT x.id, 
       x.position,
       x.name
  FROM (SELECT t.id,
               t.name,
               @rownum := @rownum + 1 AS position
          FROM TABLE t
          JOIN (SELECT @rownum := 0) r
      ORDER BY t.name) x
 WHERE x.name = 'Beta'

... เพื่อให้ได้ค่าตำแหน่งที่ไม่ซ้ำกัน นี้:

SELECT t.id,
       (SELECT COUNT(*)
          FROM TABLE x
         WHERE x.name <= t.name) AS position,
       t.name    
  FROM TABLE t      
 WHERE t.name = 'Beta'

... จะให้ค่าความสัมพันธ์เดียวกัน. IE: หากมีสองค่าในตำแหน่งที่สองทั้งคู่จะมีตำแหน่งเป็น 2 เมื่อแบบสอบถามแรกจะให้ตำแหน่ง 2 ต่อหนึ่งในนั้นและอีก 3 ค่า ...


@ จริง: ไม่มีอะไรจะพูด - ไม่มีทางเลือกอื่นนอกจากย้ายไปยังคู่แข่งที่รองรับฟังก์ชันการวิเคราะห์ (PostgreSQL, Oracle, SQL Server, DB2 ... )
OMG Ponies

2
@OMGPonies ลืมเครื่องหมายจุลภาคหลังจากpositionนั้น แต่มันสมบูรณ์แบบ
pierallard

ฉันรู้ว่ามันเป็นโพสต์เก่า แต่ฉันต้องการวิธีแก้ปัญหาที่คล้ายกัน อย่างไรก็ตามเมื่อใช้การรวมภายในให้จัดกลุ่มตามและเรียงลำดับตามฟิลด์ "ตำแหน่ง" จะละเว้นสิ่งเหล่านี้และค่าจะผสมกันทั้งหมด วิธีแก้ปัญหาใด ๆ
Daniel

@ แดเนียลคุณควรถามคำถามใหม่และอาจอ้างถึงคำถามนี้
PeerBr

@actual เนื่องจากนี่เป็นคำค้นหาสำหรับแถวเดียวจึงไม่ควรมีข้อกังวลเกี่ยวกับประสิทธิภาพที่สำคัญที่นี่ จะแตกต่างออกไปหากคุณพยายามที่จะได้รับการจัดอันดับของรายการทั้งหมด แต่คุณสามารถ "โกง" และใช้อันดับโดยปริยายโดยการเรียงลำดับตามคะแนน
AgmLauncher

20

นี่เป็นวิธีเดียวที่ฉันคิดได้:

SELECT `id`,
       (SELECT COUNT(*) FROM `table` WHERE `name` <= 'Beta') AS `position`,
       `name`
FROM `table`
WHERE `name` = 'Beta'

2
+1 เคล็ดลับดีๆ ... อย่างไรก็ตามคุณอาจต้องการใช้name <= 'Beta'แทน
Daniel Vassallo

แนวทางนี้จะให้positionค่าความสัมพันธ์เหมือนกัน
OMG Ponies

(ลบความคิดเห็นก่อนหน้าของฉัน - ฉันผิด) ... ถ้าคุณเพิ่มLIMIT 1ในนั้นจะเป็นอย่างไร? ในกรณีที่เสมอกันคุณจะได้แถวเดียวกับตำแหน่งสุดท้ายของการเสมอกัน
Daniel Vassallo

หาก OP สามารถรับประกันได้ว่าnameฟิลด์นั้นไม่ซ้ำกัน - ก็ไม่มีเหตุผลใดที่จะทำให้การสืบค้นซับซ้อนขึ้น ถ้าเขาทำไม่ได้ - รอให้เขาคาดหวังผลสำหรับชื่อที่ผูก
zerkms

8

หากแบบสอบถามนั้นเรียบง่ายและขนาดของชุดผลลัพธ์ที่ส่งคืนอาจมีขนาดใหญ่คุณอาจลองแบ่งออกเป็นสองแบบสอบถาม

แบบสอบถามแรกที่มีเกณฑ์การกรองแคบลงเพียงเพื่อดึงข้อมูลของแถวนั้นและแบบสอบถามที่สองใช้COUNTกับWHEREส่วนคำสั่งเพื่อคำนวณตำแหน่ง

ตัวอย่างเช่นในกรณีของคุณ

คำค้นหา 1:

SELECT * FROM tbl WHERE name = 'Beta'

แบบสอบถาม 2:

SELECT COUNT(1) FROM tbl WHERE name >= 'Beta'

เราใช้วิธีนี้ในตารางที่มีบันทึก 2M และนี่เป็นวิธีที่ปรับขนาดได้มากกว่าแนวทางของ OMG Ponies


5

คำตอบอื่น ๆ ดูซับซ้อนเกินไปสำหรับฉัน

นี่คือตัวอย่างง่ายๆสมมติว่าคุณมีตารางที่มีคอลัมน์:

userid | points

และคุณต้องการจัดเรียง userids ตามจุดและรับตำแหน่งแถว ("การจัดอันดับ" ของผู้ใช้) จากนั้นคุณใช้:

SET @row_number = 0;

SELECT 
    (@row_number:=@row_number + 1) AS num, userid, points
FROM
    ourtable
ORDER BY points DESC

num ให้ตำแหน่งแถว (การจัดอันดับ)

หากคุณมี MySQL 8.0+ คุณอาจต้องการใช้ROW_NUMBER ()


2

ตำแหน่งของแถวในตารางแสดงจำนวนแถวที่ "ดีกว่า" กว่าแถวเป้าหมาย

ดังนั้นคุณต้องนับแถวเหล่านั้น

เลือก COUNT (*) + 1 จากtableWHERE name<'Beta'

ในกรณีที่เสมอกันระบบจะคืนตำแหน่งสูงสุด

หากคุณเพิ่มแถวอื่นที่มีชื่อเดียวกันของ "เบต้า" หลังจากแถว "เบต้า" ที่มีอยู่ตำแหน่งที่ส่งคืนจะยังคงเป็น 2 เนื่องจากจะใช้ตำแหน่งเดียวกันในการจัดหมวดหมู่

หวังว่านี่จะช่วยให้ผู้ที่จะค้นหาสิ่งที่คล้ายกันในอนาคตเนื่องจากฉันเชื่อว่าเจ้าของคำถามสามารถแก้ไขปัญหาของเขาได้แล้ว


2

ฉันมีปัญหาที่คล้ายกันมากนั่นเป็นเหตุผลที่ฉันจะไม่ถามคำถามเดียวกันนี้ แต่ฉันจะแบ่งปันที่นี่ว่าฉันทำอะไรฉันต้องใช้กลุ่มด้วยและสั่งโดย AVG มีนักเรียนพร้อมลายเซ็นและโซคอร์และฉันต้องจัดอันดับพวกเขา (กล่าวอีกนัยหนึ่งฉันคำนวณ AVG ก่อนจากนั้นสั่งพวกเขาใน DESC และในที่สุดฉันก็ต้องเพิ่มตำแหน่ง (อันดับให้ฉัน) ดังนั้นฉันจึงทำ สิ่งที่คล้ายกันมากเป็นคำตอบที่ดีที่สุดที่นี่พร้อมการเปลี่ยนแปลงเล็กน้อยที่ปรับให้เข้ากับปัญหาของฉัน):

สุดท้ายฉันใส่positionคอลัมน์ (อันดับสำหรับฉัน) ไว้ใน SELECT ภายนอก

SET @rank=0;
SELECT @rank := @rank + 1 AS ranking, t.avg, t.name
  FROM(SELECT avg(students_signatures.score) as avg, students.name as name
FROM alumnos_materia
JOIN (SELECT @rownum := 0) r
left JOIN students ON students.id=students_signatures.id_student
GROUP BY students.name order by avg DESC) t 

คำตอบนี้เข้าใจง่ายกว่าคำตอบที่ยอมรับ +1
Eric Seastrand

1

ฉันกำลังอ่านคำตอบที่ได้รับการยอมรับและดูเหมือนจะซับซ้อนเล็กน้อยดังนั้นนี่คือเวอร์ชันที่เรียบง่ายของมัน

SELECT t,COUNT(*) AS position FROM t      
 WHERE name <= 'search string' ORDER BY name

0

ฉันมีประเภทที่คล้ายกันของปัญหาที่ฉันจำเป็นต้องมียศ ( ดัชนี ) order by votes descของตาราง ต่อไปนี้ใช้งานได้ดีสำหรับฉัน

Select *, ROW_NUMBER() OVER(ORDER BY votes DESC) as "rank"
From "category_model"
where ("model_type" = ? and "category_id" = ?)

-9

อาจเป็นสิ่งที่คุณต้องการด้วยการเพิ่มไวยากรณ์

LIMIT

ดังนั้นใช้

SELECT * FROM tbl ORDER BY name ASC LIMIT 1

ถ้าต้องการแค่แถวเดียว ..


คำตอบนี้ไม่สามารถแก้ปัญหาได้ที่นี่ คุณสามารถพิจารณาลบได้
Damián Rafael Lattenero
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.