2013-04-12 6 views
12

Ho una tabella come segue:In SQL, come selezionare i migliori 2 file per ogni gruppo

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

Ecco il sample

La funzione di aggregazione per group by solo mi permette di ottenere il punteggio più alto per ogni name. Vorrei fare una query per ottenere il punteggio più alto di 2 per ogni name, come dovrei fare?

mio risultato atteso è

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

risposta

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

, ma che può causare problemi di prestazioni. c'è qualche altro modo più rapido per implementare questa query? –

+0

Questo effettivamente causa problemi di prestazioni piuttosto pesanti (la sotto-selezione è quadratica). –

+0

Questo può essere fatto linearmente, vedi "MySQL Query to Get Top 2" qui http://www.sqlines.com/mysql/how-to/get_top_n_each_group – mdubez

0

Per questo si può fare questo-

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

ma per ottenere la prima query 2, è necessario utilizzare un ID quindi eseguire il limite per ID come 0,2.

+0

Ho paura che non è quello che mi aspetto – waitingkuo

+0

sì, ero solo dando un metodo puoi farlo semplicemente, se hai mantenuto l'ID per ogni riga (chiave primaria) funzionerà di più e avrai a disposizione più funzioni. Nel modo in cui hai bisogno del suo lungo codice e inoltre, sarà più difficile per te usare qualsiasi altra cosa su di esso in futuro. – devilcrab

9

In MySQL, è possibile utilizzare le variabili definite dall'utente per ottenere un numero di riga in ogni gruppo:

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; 

Vedi Demo

+0

Grazie per questa soluzione, sono ancora nuovo in SQL. Spero di poterlo comprendere in futuro :) – waitingkuo

+0

@waitingkuo Sfortunatamente MySQL non ha funzioni di windowing che ti consentono di assegnare facilmente un numero di riga a ciascuna riga di un gruppo. – Taryn

+0

@bluefeet grazie questa è una soluzione molto bella, ha funzionato molto velocemente anche su file 30k qualcosa, la mia soluzione precedente usando i join era molto lenta – asm234

0

si può fare somthething in questo modo:

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

Se non ti dispiace avere una colonna aggiuntiva, puoi utilizzare il seguente codice:

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

funzione Classifica prevede rango per ogni partizione, nel tuo caso è il nome

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))