2013-04-12 38 views
12

Tôi có một bảng như sau:Trong SQL, làm thế nào để chọn 2 hàng đầu cho mỗi nhóm

NAME SCORE 
----------------- 
willy  1 
willy  2 
willy  3 
zoe   4 
zoe   5 
zoe   6 

Đây là sample

Chức năng tập hợp cho group by chỉ cho phép tôi để có được điểm số cao nhất cho mỗi name. Tôi muốn thực hiện truy vấn để nhận được số điểm cao nhất là 2 cho mỗi name, tôi nên làm như thế nào?

sản lượng dự kiến ​​của tôi là

NAME SCORE 
----------------- 
willy  2 
willy  3 
zoe   5 
zoe   6 

Trả lời

19
SELECT * 
FROM test s 
WHERE 
     (
      SELECT COUNT(*) 
      FROM test f 
      WHERE f.name = s.name AND 
        f.score >= s.score 
     ) <= 2 
+1

nhưng điều đó có thể gây ra vấn đề hiệu suất. có cách nào khác nhanh hơn để triển khai truy vấn này không? –

+0

Điều này thực sự gây ra vấn đề hiệu suất khá nặng (lựa chọn phụ là bậc hai). –

+0

Điều này có thể được thực hiện một cách tuyến tính, xem "MySQL Query to Get Top 2" ở đây http://www.sqlines.com/mysql/how-to/get_top_n_each_group – mdubez

0

Đối với điều này bạn có thể làm this-

http://www.sqlfiddle.com/#!2/ee665/4

nhưng để có được 2 truy vấn đầu tiên, bạn nên sử dụng một ID sau đó chạy giới hạn cho ID như 0,2.

+0

Tôi sợ rằng nó không phải là những gì tôi mong đợi – waitingkuo

+0

yea tôi chỉ là cho phương pháp bạn có thể làm điều đó đơn giản, nếu bạn đã duy trì ID cho mỗi hàng (khóa chính) nó sẽ làm việc nhiều hơn và bạn sẽ có nhiều chức năng hơn trong tay. Trong cách bạn cần mã dài của nó và cũng có thể, nó sẽ khó khăn hơn cho bạn để sử dụng bất cứ điều gì khác hơn trong tương lai. – devilcrab

9

Trong MySQL, bạn có thể sử dụng các biến người dùng định nghĩa để có được một số lượng hàng trong mỗi nhóm:

select name, score 
from 
(
    SELECT name, 
    score, 
    (@row:=if(@prev=name, @row +1, if(@prev:= name, 1, 1))) rn 
    FROM test123 t 
    CROSS JOIN (select @row:=0, @prev:=null) c 
    order by name, score desc 
) src 
where rn <= 2 
order by name, score; 

Xem Demo

+0

Cảm ơn bạn vì giải pháp này, tôi vẫn còn mới trong SQL. Hy vọng rằng tôi có thể hiểu điều này trong tương lai :) – waitingkuo

+0

@waitingkuo Thật không may MySQL không có chức năng cửa sổ mà sẽ cho phép bạn dễ dàng gán một số hàng cho mỗi hàng trong một nhóm. – Taryn

+0

@ bluefeet cảm ơn đây là một giải pháp rất hay, nó hoạt động rất nhanh ngay cả trên 30k hàng nào đó, giải pháp trước đây của tôi sử dụng kết nối rất chậm – asm234

0

Bạn có thể làm somthething như thế này:

SET @num :=0, @name :=''; 
SELECT name, score, 
    @num := IF(@name= name, @num +1, 1) AS row_number, 
    @name := name AS dummy 
FROM test 
GROUP BY name, score 
HAVING row_number <=2 
2

Nếu bạn không nhớ có cột bổ sung thì bạn có thể sử dụng mã sau:

SELECT Name, Score, rank() over(partition by Name, order by Score DESC) as rank 
From Table 
Having rank < 3; 

chức năng Rank cung cấp thứ hạng cho mỗi phân vùng, trong trường hợp của bạn nó là tên

0
SELECT * FROM ( 
    SELECT VD.`cat_id` , 
     @cat_count := IF((@cat_id = VD.`cat_id`), @cat_count + 1, 1) AS 'DUMMY1', 
     @cat_id := VD.`cat_id` AS 'DUMMY2', 
     @cat_count AS 'CAT_COUNT' 
    FROM videos VD 
    INNER JOIN categories CT ON CT.`cat_id` = VD.`cat_id` 
     ,(SELECT @cat_count :=1, @cat_id :=-1) AS CID 
    ORDER BY VD.`cat_id` ASC) AS `CAT_DETAILS` 
    WHERE `CAT_COUNT` < 4 

------- STEP FOLLOW ---------- 
1 . select * from ('FILTER_DATA_HERE') WHERE 'COLUMN_COUNT_CONDITION_HERE' 
2. 'FILTER_DATA_HERE' 
    1. pass 2 variable @cat_count=1 and @cat_id = -1 
    2. If (@cat_id "match" column_cat_id value) 
     Then @cat_count = @cat_count + 1  
     ELSE @cat_count = 1  
    3. SET @cat_id = column_cat_id  

3. 'COLUMN_COUNT_CONDITION_HERE' 
    1. count_column < count_number  

4. ' EXTRA THING ' 
    1. If you want to execute more than one statement inside " if stmt " 
    2. IF(condition, stmt1 , stmt2) 
     1. stmt1 :- CONCAT(exp1, exp2, exp3) 
     2. stmt2 :- CONCAT(exp1, exp2, exp3) 
    3. Final "If" Stmt LIKE 
     1. IF (condition , CONCAT(exp1, exp2, exp3) , CONCAT(exp1, exp2, exp3))  
Các vấn đề liên quan