Với MySQL, làm cách nào để tạo một cột chứa chỉ mục bản ghi trong một bảng?


100

Có cách nào tôi có thể lấy số hàng thực từ một truy vấn không?

Tôi muốn có thể đặt một bảng có tên league_girl theo một trường được gọi là điểm số; và trả lại tên người dùng và vị trí hàng thực của tên người dùng đó.

Tôi muốn xếp hạng người dùng để tôi có thể biết vị trí của một người dùng cụ thể. Joe ở vị trí 100 trên 200, tức là

User Score Row
Joe  100    1
Bob  50     2
Bill 10     3

Tôi đã thấy một vài giải pháp ở đây nhưng tôi đã thử hầu hết chúng và không có giải pháp nào trong số chúng thực sự trả lại số hàng.

Tôi đã thử điều này:

SELECT position, username, score
FROM (SELECT @row := @row + 1 AS position, username, score 
       FROM league_girl GROUP BY username ORDER BY score DESC) 

Như bắt nguồn

... nhưng nó dường như không trả lại vị trí hàng.

Bất kỳ ý tưởng?


hàng có phải là tên được lưu hay bạn muốn sắp xếp theo khóa chính?
Sarfraz

Trong SQL, số hàng thực sự không quan trọng. Những gì bạn nên làm là thêm một khóa chính tăng tự động vào bảng của bạn.
simendsjo

9
Khóa chính KHÔNG BAO GIỜ phải là số nhận dạng hàng vì chúng không đáng tin cậy cho vị trí hàng thực.
TheBounder

3
Bên cạnh đó, vì số hàng sẽ là một hàm của điểm mà tôi giả sử không phải là giá trị tĩnh, nên việc biến nó thành một giá trị tăng tự động (khóa chính hoặc không) sẽ không cho kết quả như mong muốn.
kasperjj

bạn có thể muốn lưu xấu xí cho một chức năng tùy chỉnh, xem datamakessense.com/mysql-rownum-row-number-function
AdrianBR

Câu trả lời:


174

Bạn có thể muốn thử những cách sau:

SELECT  l.position, 
        l.username, 
        l.score,
        @curRow := @curRow + 1 AS row_number
FROM    league_girl l
JOIN    (SELECT @curRow := 0) r;

Phần này JOIN (SELECT @curRow := 0)cho phép khởi tạo biến mà không yêu cầu SETlệnh riêng .

Trường hợp thử nghiệm:

CREATE TABLE league_girl (position int, username varchar(10), score int);
INSERT INTO league_girl VALUES (1, 'a', 10);
INSERT INTO league_girl VALUES (2, 'b', 25);
INSERT INTO league_girl VALUES (3, 'c', 75);
INSERT INTO league_girl VALUES (4, 'd', 25);
INSERT INTO league_girl VALUES (5, 'e', 55);
INSERT INTO league_girl VALUES (6, 'f', 80);
INSERT INTO league_girl VALUES (7, 'g', 15);

Truy vấn kiểm tra:

SELECT  l.position, 
        l.username, 
        l.score,
        @curRow := @curRow + 1 AS row_number
FROM    league_girl l
JOIN    (SELECT @curRow := 0) r
WHERE   l.score > 50;

Kết quả:

+----------+----------+-------+------------+
| position | username | score | row_number |
+----------+----------+-------+------------+
|        3 | c        |    75 |          1 |
|        5 | e        |    55 |          2 |
|        6 | f        |    80 |          3 |
+----------+----------+-------+------------+
3 rows in set (0.00 sec)

19
Bạn cũng có thể khởi tạo @curRowbằng cách thay thế các JOINcâu lệnh bằng một dấu phẩy, như thế này:FROM league_girl l, (SELECT @curRow := 0) r
Mike

2
@Mike: Đúng là như vậy. Và điều đó có lẽ cũng gọn gàng hơn. Cảm ơn vì đã chia sẻ mẹo này.
Daniel Vassallo

2
@smhnaji MySQL yêu cầu mọi "bảng dẫn xuất" phải được đặt một tên. Tôi quyết định gọi nó là "r" :) ... Nó không có mục đích gì trong trường hợp này, nhưng bạn thường sử dụng nó để tham chiếu các thuộc tính của bảng dẫn xuất, như thể nó là một bảng thực.
Daniel Vassallo

20
Mọi người nên lưu ý rằng số hàng này được tính toán trước khi bất kỳ đơn hàng nào diễn ra, vì vậy các con số có thể trở nên lộn xộn nếu đơn hàng thay đổi thứ tự hàng.
Grim ...

7
Có cách nào để có được số hàng này được tính sau ORDER BYkhông?
Pierre de LESPINAY

38
SELECT @i:=@i+1 AS iterator, t.*
FROM tablename t,(SELECT @i:=0) foo

có cách nào để làm điều này để cột trình lặp là một số nguyên và không phải là một số thập phân?
kraftydevil

7

Đây là cấu trúc của mẫu tôi đã sử dụng:

  select
          /*this is a row number counter*/
          ( select @rownum := @rownum + 1 from ( select @rownum := 0 ) d2 ) 
          as rownumber,
          d3.*
  from 
  ( select d1.* from table_name d1 ) d3

Và đây là mã làm việc của tôi:

select     
           ( select @rownum := @rownum + 1 from ( select @rownum := 0 ) d2 ) 
           as rownumber,
           d3.*
from
(   select     year( d1.date ), month( d1.date ), count( d1.id )
    from       maindatabase d1
    where      ( ( d1.date >= '2013-01-01' ) and ( d1.date <= '2014-12-31' ) )
    group by   YEAR( d1.date ), MONTH( d1.date ) ) d3

hoàn hảo, các công trình như nét duyên dáng và mẫu có thể được tái sử dụng với subquery như tham số ..
Zavael

Giải pháp này cũng hoạt động nếu truy vấn cơ sở của bạn sử dụng GROUP BY
Dave R,

4

Bạn cũng có thể dùng

SELECT @curRow := ifnull(@curRow,0) + 1 Row, ...

để khởi tạo biến đếm.


3
@curRowvẫn có thể có giá trị từ lần cuối cùng bạn chạy truy vấn này trong phiên hiện tại.
Bill Karwin

Đúng, nhưng chỉ khi bạn đang truy vấn lại trên cùng một trường hợp kết nối. Các biến cục bộ sẽ tự động được xử lý sau khi kết nối bị đóng.
Hearth

Vâng, đó là ý của tôi khi tôi nói "trong phiên hiện tại".
Bill Karwin

3

Giả sử MySQL hỗ trợ nó, bạn có thể dễ dàng thực hiện việc này với một truy vấn con SQL chuẩn:

select 
    (count(*) from league_girl l1 where l2.score > l1.score and l1.id <> l2.id) as position,
    username,
    score
from league_girl l2
order by score;

Đối với số lượng lớn kết quả được hiển thị, điều này sẽ hơi chậm và bạn sẽ muốn chuyển sang tự tham gia.


3

Nếu bạn chỉ muốn biết vị trí của một người dùng cụ thể sau khi sắp xếp theo điểm trường, bạn có thể chỉ cần chọn tất cả hàng từ bảng của mình nơi điểm trường cao hơn điểm người dùng hiện tại. Và sử dụng số hàng được trả về + 1 để biết vị trí của người dùng hiện tại này.

Giả sử rằng bảng của bạn là league_girlvà trường chính của bạn là id, bạn có thể sử dụng điều này:

SELECT count(id) + 1 as rank from league_girl where score > <your_user_score>

0

Tôi thấy câu trả lời ban đầu vô cùng hữu ích nhưng tôi cũng muốn lấy một tập hợp các hàng nhất định dựa trên số hàng mà tôi đang chèn. Như vậy, tôi đã gói toàn bộ câu trả lời ban đầu trong một truy vấn con để tôi có thể tham chiếu số hàng mà tôi đang chèn.

SELECT * FROM 
( 
    SELECT *, @curRow := @curRow + 1 AS "row_number"
    FROM db.tableName, (SELECT @curRow := 0) r
) as temp
WHERE temp.row_number BETWEEN 1 and 10;

Có một truy vấn con trong một truy vấn con không hiệu quả lắm, vì vậy, sẽ đáng để thử nghiệm xem bạn có nhận được kết quả tốt hơn hay không bằng cách để máy chủ SQL của bạn xử lý truy vấn này hoặc tìm nạp toàn bộ bảng và để ứng dụng / máy chủ web thao tác các hàng sau thực tế .

Theo cá nhân tôi, máy chủ SQL của tôi không quá bận rộn, vì vậy việc để nó xử lý các truy vấn con lồng nhau là tốt hơn.


0

Tôi biết OP đang yêu cầu một mysqlcâu trả lời nhưng vì tôi thấy các câu trả lời khác không phù hợp với tôi,

  • Hầu hết trong số họ thất bại với order by
  • Hoặc chúng chỉ đơn giản là rất kém hiệu quả và làm cho truy vấn của bạn rất chậm đối với một bảng béo

Vì vậy, để tiết kiệm thời gian cho những người khác như tôi, chỉ cần lập chỉ mục hàng sau khi truy xuất chúng từ cơ sở dữ liệu

ví dụ trong PHP:

$users = UserRepository::loadAllUsersAndSortByScore();

foreach($users as $index=>&$user){
    $user['rank'] = $index+1;
}

ví dụ trong PHP sử dụng bù đắp và giới hạn cho phân trang:

$limit = 20; //page size
$offset = 3; //page number

$users = UserRepository::loadAllUsersAndSortByScore();

foreach($users as $index=>&$user){
    $user['rank'] = $index+1+($limit*($offset-1));
}
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.