MySQL nhận vị trí hàng trong ORDER BY


86

Với bảng MySQL sau:

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

Làm thế nào tôi có thể chọn một đơn hàng và vị trí của nó trong số các hàng khác trong bảng, khi được sắp xếp theo name ASC. Vì vậy, nếu dữ liệu bảng trông như thế này, khi được sắp xếp theo tên:

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

Làm cách nào để chọn Betahàng có vị trí hiện tại của hàng đó? Tập hợp kết quả mà tôi đang tìm kiếm sẽ như sau:

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

Tôi có thể làm một cách đơn giản SELECT * FROM tbl ORDER BY name ASCsau đó liệt kê các hàng trong PHP, nhưng có vẻ lãng phí khi tải một tập kết quả lớn tiềm năng chỉ cho một hàng.



Câu trả lời:


120

Dùng cái này:

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'

... để nhận giá trị vị trí duy nhất. Điều này:

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

... sẽ cung cấp cho các mối quan hệ cùng một giá trị. IE: Nếu có hai giá trị ở vị trí thứ hai, chúng sẽ có vị trí là 2 khi truy vấn đầu tiên sẽ đưa ra vị trí là 2 cho một trong số chúng và 3 cho vị trí còn lại ...


@actual: Không có gì để nói - không có gì thay thế, ngoài việc chuyển sang đối thủ cạnh tranh hỗ trợ các chức năng phân tích (PostgreSQL, Oracle, SQL Server, DB2 ...)
OMG Ponies 11/12/12

2
@OMGPonies Chỉ cần quên một dấu phẩy sau position, nhưng nó hoàn hảo.
pierallard

Tôi biết đó là một bài viết khá cũ, nhưng tôi cần một giải pháp tương tự. Tuy nhiên, khi sử dụng các phép nối bên trong, nhóm theo thứ tự và theo thứ tự, trường "vị trí" bỏ qua những điều này và giá trị bị trộn lẫn. Bất kỳ giải pháp?
Daniel

@Daniel, bạn nên đặt một câu hỏi mới và có thể tham khảo câu hỏi này.
PeerBr

@actual, vì đây là truy vấn cho một hàng, nên không có mối quan tâm đáng kể nào về hiệu suất ở đây. Sẽ khác nếu bạn đang cố gắng có được thứ hạng của một danh sách đầy đủ, nhưng bạn có thể chỉ "gian lận" và sử dụng thứ hạng ngầm bằng cách sắp xếp theo điểm.
AgmLauncher,

20

Đây là cách duy nhất mà tôi có thể nghĩ ra:

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

2
1 đẹp lừa ... Tuy nhiên bạn muốn có thể muốn sử dụng name <= 'Beta'thay vì
Daniel Vassallo

Cách tiếp cận này sẽ cung cấp các positiongiá trị tương tự cho các mối quan hệ.
OMG Ponies

(Đã xóa nhận xét trước đây của tôi - Tôi đã sai) ... Điều gì sẽ xảy ra nếu bạn thêm một LIMIT 1vào đó? Trong trường hợp hòa, bạn sẽ chỉ nhận được một hàng với vị trí cuối cùng của cà vạt.
Daniel Vassallo

Nếu OP có thể đảm bảo nametrường đó là duy nhất - thì không có lý do gì để làm cho truy vấn phức tạp hơn. Nếu anh ấy không thể - thì chúng ta hãy chờ đợi kết quả kỳ vọng của anh ấy cho những cái tên bị ràng buộc.
zerkms

8

Nếu truy vấn đơn giản và kích thước của tập kết quả trả về có khả năng lớn, thì bạn có thể cố gắng chia nó thành hai truy vấn.

Truy vấn đầu tiên với tiêu chí lọc thu hẹp chỉ để truy xuất dữ liệu của hàng đó và truy vấn thứ hai sử dụng mệnh đề COUNTwith WHEREđể tính toán vị trí.

Ví dụ trong trường hợp của bạn

Truy vấn 1:

SELECT * FROM tbl WHERE name = 'Beta'

Truy vấn 2:

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

Chúng tôi sử dụng phương pháp này trong một bảng có bản ghi 2M và đây là cách có thể mở rộng hơn so với phương pháp của OMG Ponies.


4

Các câu trả lời khác dường như quá phức tạp đối với tôi.

Đây là một ví dụ đơn giản , giả sử bạn có một bảng với các cột:

userid | points

và bạn muốn sắp xếp các sử dụng theo điểm và lấy vị trí hàng ("xếp hạng" của người dùng), thì bạn sử dụng:

SET @row_number = 0;

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

num cung cấp cho bạn vị trí hàng (xếp hạng).

Nếu bạn có MySQL 8.0+ thì bạn có thể muốn sử dụng ROW_NUMBER ()


2

Vị trí của một hàng trong bảng biểu thị số hàng "tốt hơn" so với hàng được nhắm mục tiêu.

Vì vậy, bạn phải đếm những hàng đó.

CHỌN COUNT (*) + 1 TỪ tableWHERE name<'Beta'

Trong trường hợp hòa, vị trí cao nhất được trả về.

Nếu bạn thêm một hàng khác có cùng tên "Beta" sau hàng "Beta" hiện có, thì vị trí được trả về sẽ vẫn là 2, vì chúng sẽ có cùng vị trí trong phân loại.

Hy vọng điều này sẽ giúp ích cho những người sẽ tìm kiếm thứ gì đó tương tự trong tương lai, vì tôi tin rằng chủ sở hữu câu hỏi đã giải quyết được vấn đề của mình.


2

Tôi đã gặp một vấn đề rất giống nhau, đó là lý do tại sao tôi sẽ không hỏi câu hỏi tương tự, nhưng tôi sẽ chia sẻ ở đây tôi đã làm gì, tôi đã phải sử dụng một nhóm theo và đặt hàng của AVG. Có những sinh viên, với chữ ký và điểm số, và tôi phải xếp hạng họ (nói cách khác, đầu tiên tôi tính AVG, sau đó sắp xếp chúng trong DESC, và cuối cùng tôi cần thêm vị trí (xếp hạng cho tôi), vì vậy tôi đã làm một cái gì đó Rất giống với câu trả lời hay nhất ở đây, với một chút thay đổi để điều chỉnh vấn đề của tôi):

Cuối cùng tôi đặt cột position(xếp hạng cho tôi) trong CHỌN bên ngoài

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 

Câu trả lời này dễ hiểu hơn câu được chấp nhận. +1
Eric Seastrand

1

Tôi đã xem qua câu trả lời được chấp nhận và nó có vẻ hơi phức tạp nên đây là phiên bản đơn giản của nó.

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

0

Tôi gặp các loại vấn đề tương tự khi tôi yêu cầu xếp hạng ( Chỉ mục ) của bảng order by votes desc. Những điều sau đây phù hợp với tôi.

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

-9

có thể là những gì bạn cần với cú pháp thêm

LIMIT

vì vậy hãy sử dụng

SELECT * FROM tbl ORDER BY name ASC LIMIT 1

nếu bạn chỉ cần một hàng ..


câu trả lời này không giải quyết được vấn đề ở đây. Bạn có thể xem xét xóa nó
Damián Rafael Lattenero
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.