2013-06-23 4 views
5

Ho un tavolo da gioco con questi campi:SQL Server: Posizione per somma dei punti e l'ordine da classifica

ID Name  Email  Points 
---------------------------------- 
1  John  [email protected] 120 
2  Test  [email protected]  100 
3  John  [email protected] 80 
4  Bob  [email protected]  50 
5  John  [email protected] 80 

voglio raggrupparli per posta elettronica (e-mail Identifica che entrambi i giocatori sono gli stessi, non importa che riga 2 e 4 hanno nomi diversi) e hanno anche somma dei punti e l'ultimo nome inserito nei risultati e classificarli con la somma heighest di punti per il più basso

il risultato che voglio dalla tabella di esempio è:

Ranking  Name  Points Games_Played  Average_Points 
------------------------------------------------------------------------------------------ 
1   John  200   2    100 
2   Bob   150   2    75 
3   John  80   1    80 

Potrei ottenere il ranking, la somma dei punti e il punteggio medio ma ottenere l'ultimo nome inserito Penso che sia necessario unirsi nuovamente allo stesso tavolo e sembra un po 'sbagliato.

Qualche idea su come procedere?

risposta

4

La visualizzazione del nome e del gruppo di posta elettronica causerà l'utilizzo, ad es. MIN (Nome) e portare a nomi duplicati.

Select Rank() over (order by Points desc) as Rank 
,Name,Points,Games_Played,Average_Points 
from 
(
Select Min(Name) as Name,Email,Sum(Points) as Points 
,Count(*) as Games_Played,AVG(Points) as Average_Points 
From @a Group by Email 
) a 
order by Rank 

SQLFiddle

nel Fiddle sono due linee è necessario togliere di vedere il comportamento sui risultati identici commentato.

+0

Risposta favolosa. Grazie ..... ho risolto il mio problema e ho imparato alcune cose nuove. non dovrebbe essere Max (nome)? cosa significa Min (nome)? –

+0

Vorrei usare la posta elettronica per essere chiaro e omettere il nome, dipende da te che usi il nome MINIMO o il nome massimo, è comunque un tipo casuale. – bummi

+0

beh voglio solo sapere qual è la funzionalità di Max o Min per il nome in quanto non è un numero, funziona come l'ordinamento del dizionario? –

0

credo che questo sia quello che ti serve

select ROW_NUMBER() OVER (ORDER BY sum(r1.points) Desc) as Ranking, 
    r1.name as Name, 
    sum(r1.points) as Points, 
    r3.gplayed as 'Games Played', 
    r2.points 'Average Points' 
from ranks r1 
    join (select avg(points) as points, email from ranks group by email) r2 
     on r1.email = r2.email 
    join (select email, count(*) as gplayed from ranks group by email) r3 
     on r1.email = r3.email 
group by 
    r1.email, 
    r1.name, 
    r2.points, 
    r3.gplayed 

Ecco un SQL Fiddle.

3

È possibile utilizzare Ranking Functions da SQL-Server 2005 verso l'alto:

WITH Points 
    AS (SELECT Sum_Points = Sum(points) OVER ( 
           partition BY email), 
       Games_Played = Count(ID) OVER ( 
           partition BY email), 
       Average_Points = AVG(Points) OVER ( 
           partition BY email), 
       Rank = DENSE_RANK() OVER ( 
           Partition BY email Order By Points DESC), 
       * 
     FROM dbo.Game) 
SELECT Ranking=DENSE_RANK()OVER(ORDER BY Sum_Points DESC), 
     Name, 
     Points=Sum_Points, 
     Games_Played, 
     Average_Points 
FROM Points 
WHERE Rank = 1 
Order By Sum_Points DESC; 

DEMO

Si noti che il risultato è diverso dal momento che sto mostrando la riga con il punto più alto nel caso in cui l'email è non unico, quindi "Test" anziché "Bob".

2

Di seguito sono soluzioni separate per SQL Server 2012+, 2005 al 2008 R2 e 2000:

2012+

CREATE TABLE #PlayerPoints 
    (ID INT PRIMARY KEY 
    , Name VARCHAR(10) NOT NULL 
    , Email VARCHAR(20) NOT NULL 
    , Points INT NOT NULL); 

INSERT INTO #PlayerPoints (ID, Name, Email, Points) 
VALUES 
     (1, 'John', '[email protected]', 120) 
    , (2, 'Test', '[email protected]', 100) 
    , (3, 'John', '[email protected]', 80) 
    , (4, 'Bob', '[email protected]', 50) 
    , (5, 'John', '[email protected]', 80) 

WITH BaseData 
AS 
    (SELECT ID 
     , Email 
     , Points 
     , LastRecordName = LAST_VALUE(Name) OVER 
      (PARTITION BY Email 
      ORDER BY ID DESC 
      ROWS UNBOUNDED PRECEDING) 
    FROM #PlayerPoints) 
SELECT Email 
    , LastRecordName = MAX(LastRecordName) 
    , Points = SUM(Points) 
    , Games_Played = COUNT(*) 
    , Average_Points = AVG(Points) 
FROM BaseData 
GROUP BY Email 
ORDER BY Points DESC; 

2005 a 2008 R2

CREATE TABLE #PlayerPoints 
    (ID INT PRIMARY KEY 
    , Name VARCHAR(10) NOT NULL 
    , Email VARCHAR(20) NOT NULL 
    , Points INT NOT NULL); 

INSERT INTO #PlayerPoints (ID, Name, Email, Points) 
VALUES 
     (1, 'John', '[email protected]', 120) 
    , (2, 'Test', '[email protected]', 100) 
    , (3, 'John', '[email protected]', 80) 
    , (4, 'Bob', '[email protected]', 50) 
    , (5, 'John', '[email protected]', 80) 

WITH BaseData 
AS 
    (SELECT ID 
     , Email 
     , Name 
     , ReverseOrder = ROW_NUMBER() OVER 
      (PARTITION BY Email 
      ORDER BY ID DESC) 
    FROM #PlayerPoints) 
SELECT pp.Email 
    , LastRecordName = MAX(bd.Name) 
    , Points = SUM(pp.Points) 
    , Games_Played = COUNT(*) 
    , Average_Points = AVG(pp.Points) 
FROM #PlayerPoints pp 
JOIN BaseData bd 
    ON pp.Email = bd.Email 
    AND bd.ReverseOrder = 1 
GROUP BY pp.Email 
ORDER BY Points DESC; 

CREATE TABLE #PlayerPoints 
    (ID INT PRIMARY KEY 
    , Name VARCHAR(10) NOT NULL 
    , Email VARCHAR(20) NOT NULL 
    , Points INT NOT NULL); 

INSERT INTO #PlayerPoints (ID, Name, Email, Points) 
SELECT 1, 'John', '[email protected]', 120 
UNION ALL 
SELECT 2, 'Test', '[email protected]', 100 
UNION ALL 
SELECT 3, 'John', '[email protected]', 80 
UNION ALL 
SELECT 4, 'Bob', '[email protected]', 50 
UNION ALL 
SELECT 5, 'John', '[email protected]', 80; 

SELECT pp.Email 
    , LastRecordName = MAX(sppmi.Name) 
    , Points = SUM(pp.Points) 
    , Games_Played = COUNT(*) 
    , Average_Points = AVG(pp.Points) 
FROM #PlayerPoints pp 
JOIN 
    (SELECT spp.Email 
     , spp.Name 
    FROM #PlayerPoints spp 
    JOIN 
     (SELECT Email 
      , MaximumID = MAX(ID) 
     FROM #PlayerPoints 
     GROUP BY Email) mi 
     ON spp.ID = mi.MaximumID) sppmi 
    ON pp.Email = sppmi.Email 
GROUP BY pp.Email 
ORDER BY Points DESC; 
+0

+1. . . La tua è l'unica soluzione che gestisce correttamente il vincolo effettivo sul nome. Penso che la soluzione sia limitata, basandosi su una funzione disponibile solo in SQL Server 2012, quando non è specificata nell'OP. –

+0

Aggiungerò anche una soluzione 2005+. –

+0

E ha aggiunto una soluzione 2000. –

0

Solo la soluzione da @RegisteredUser sembra gestire il vincolo sullo name. Tuttavia, esso richiede SQL Server 2012, ecco una soluzione più generale:

 Select dense_rank() over (order by sum(points) desc) as ranking 
      max(case when islastid = 1 then Name end) as Name, Email, Sum(Points) as Points, 
      Count(*) as Games_Played, AVG(Points) as Average_Points 
     From (select g.*, 
        row_number() over (partition by email order by id desc) as islastid 
      from games g 
      ) t 
     Group by Email; 

Non avete abbastanza informazioni per la questione di scegliere tra rank() e dense_rank().

Inoltre, questa versione è più semplice rispetto ad altre versioni, poiché è possibile combinare funzioni di finestra e funzioni di aggregazione.

Problemi correlati