2009-10-09 11 views
5

Naprawdę podchwytliwa instrukcja SQL, którą mam tutaj. Próbuję zbudować to zapytanie przez około godzinę. Może możesz mi pomóc.Tricky zapytanie SQL

Mamy tabelę z 3 kolumnami: nazwa gry | użytkownik | times_played

Zapytanie powinno wybrać trzy najlepsze gry (w zależności od total times_played) i trzech najlepszych użytkowników, którzy grali najwięcej razy w tej grze => 9 wierszy.

The result is like: 
CounterStrike | Smith 
CounterStrike | Jonny 
Counterstrike | Hans 
WoW   | George 
WoW   | Bob 
Wow   | Frank 
Need For Speed| James 
Need For Speed| Marion 
Need For Speed| Scarlet 

Byłoby bardzo miło, gdyby mógł mi pomóc =) Dzięki!

+0

Co baza danych? MySQL? Serwer SQL? Wyrocznia? Postgres? – Asaph

+0

Która technologia? MySQL? Serwer SQL? Czy możesz użyć procedury przechowywanej? –

+0

@foriamstu lepsze pytanie: * czy * należy użyć procedury przechowywanej? Z mojego doświadczenia prawie zawsze: "nie". Wydaje się przydatny w tym czasie, wraca, aby cię ugryźć w konserwacji. –

Odpowiedz

13

Aktualizacja:

@Steve Kass Jak podkreślił, nie zauważyłem, że chciał tylko pierwsze trzy mecze.

Oto zaktualizowana wersja:

W SQL Server, Oracle i PostgreSQL 8.4:

SELECT gamename, user 
FROM (
     SELECT r.gamename, user, 
       ROW_NUMBER() OVER (PARTITION BY game ORDER BY times_played DESC) rn, 
     FROM (
       SELECT gamename, ROW_NUMBER() OVER (ORDER BY SUM(times_played) DESC) AS game_rn 
       FROM results 
       GROUP BY 
         gamename 
       ) g 
     JOIN results r 
     ON  r.gamename = g.gamename 
     WHERE game_rn <= 3 
     ) q 
WHERE rn <= 3 
ORDER BY 
     gamename, times_played DESC 

W MySQL:

SELECT ro.gamename, ro.user 
FROM (
     SELECT gamename, SUM(times_played) AS rank 
     FROM results 
     ORDER BY 
       rank DESC 
     LIMIT 3 
     ) rd 
JOIN results ro 
ON  ro.gamename >= rd.gamename 
     AND ro.gamename <= rd.gamename 
     AND 
     (ro.times_played, ro.id) <= 
     (
     SELECT ri.times_played, ri.id 
     FROM results ri 
     WHERE ri.gamename = rd.gamename 
     ORDER BY 
       ri.times_played DESC, ri.id DESC 
     LIMIT 2, 1 
     ) 
ORDER BY 
     gamename, times_played DESC 

Musisz PRIMARY KEY dla tego zapytania do pracy, zakładając go nazywa się id.

to wyjaśnione bardziej szczegółowo w tym artykule w moim blogu:

W PostgreSQL 8.3 i poniżej:

SELECT gamename, ((ri)[s]).user 
FROM (
     SELECT gamename, ri, generate_series(1, 3) AS s 
     FROM (
       SELECT ro.gamename, 
         ARRAY 
         (
         SELECT ri 
         FROM results ri 
         WHERE ri.gamename = ro.gamename 
         ORDER BY 
           times_played DESC 
         LIMIT 3 
         ) AS ri 
       FROM (
         SELECT gamename, SUM(times_played) AS rank 
         FROM results 
         ORDER BY 
           rank DESC 
         LIMIT 3 
         ) rd 
       ) q 
     ) q2 
ORDER BY 
     gamename, s 
+3

Po prostu wydaje się to takie proste, gdy odpowiadasz :-) –

+0

Przypuszczam, że to "PARTYZJA PRZEZ" –

+0

Wrzuciłem zamówienie Game i rn, aby mieć pewność, że wyniki są wyświetlane we właściwej kolejności. –

1

nie sądzę Quassnoi zauważył, że prosiłeś o najwyższych użytkowników tylko dla trzech najlepszych gier (na podstawie całkowitej times_played). Oto zapytanie do tego (nie testowane na rzeczywistych danych, ponieważ nie podano instrukcji CREATE TABLE i INSERT). Dołączam także krawaty, których Quassnoi nie zrobił, aby pokazać ci tę opcję.

with GamesPlays(gamename,totalPlays) as (
    select 
    gamename, sum(times_played) 
    from results 
    group by gamename 
), GamesRanked(gamename,gameRank) as (
    select 
    gamename, 
    rank() over (
     order by totalPlays desc 
    ) 
    from GamesPlays 
), ResultsRanked(gamename,user,userRank) as (
    select 
    gamename, 
    user, 
    rank() over (
     partition by user 
     order by times_played desc 
    ) 
    from results; 
) 
    select 
    G.gamename, R.user 
    from ResultsRanked as R 
    join GamesRanked as G 
    on G.gamename = R.gamename 
    where gameRank <= 3 
    and userRank <= 3 
    order by 
    gameRank,userRank; 
+0

Dzięki za wskazanie tego. Są "12" upvotes i nic nie zostało zrobione, ale prostacka uwaga :) – Quassnoi

+0

Być może to, co publikujesz, jest tak rzadko źle, że ludzie uważają, że to strata czasu, aby przyjrzeć się uważnie. :) –

0
DROP TABLE #game_stats 


CREATE TABLE #game_stats (gamename VARCHAR(50),users VARCHAR(50),times_played INT); 

INSERT INTO #game_stats VALUES ('Counter Strike','Kamesh',2); 
INSERT INTO #game_stats VALUES ('Counter Strike','Hely',4); 
INSERT INTO #game_stats VALUES ('Counter Strike','Maitri',1); 
INSERT INTO #game_stats VALUES ('Counter Strike','Laxmi',5); 
INSERT INTO #game_stats VALUES ('WOW','Kamesh',21); 
INSERT INTO #game_stats VALUES ('WOW','laxmi',60); 
INSERT INTO #game_stats VALUES ('WOW','Hely',7); 
INSERT INTO #game_stats VALUES ('NFS','Hely',5); 
INSERT INTO #game_stats VALUES ('NFS','Kamesh',1); 
INSERT INTO #game_stats VALUES ('NFS','Maitri',12); 
INSERT INTO #game_stats VALUES ('NFS','Laxmi',21); 
INSERT INTO #game_stats VALUES ('CODE ZERO','Kamesh',45); 
INSERT INTO #game_stats VALUES ('CODE ZERO','Maitri',52); 
INSERT INTO #game_stats VALUES ('CODE ZERO','Laxmi',21); 
INSERT INTO #game_stats VALUES ('CODE ZERO','Kamesh',41); 
INSERT INTO #game_stats VALUES ('HITMAN','Maitri',142); 
INSERT INTO #game_stats VALUES ('HITMAN','Laxmi',210); 
INSERT INTO #game_stats VALUES ('HITMAN','Kamesh',41); 
INSERT INTO #game_stats VALUES ('HITMAN','Maitri',102); 
INSERT INTO #game_stats VALUES ('HITMAN','Mani',142); 
INSERT INTO #game_stats VALUES ('NFS','Mani',210); 
INSERT INTO #game_stats VALUES ('CODE ZERO','Mani',41); 
INSERT INTO #game_stats VALUES ('WOW','Mani',102); 

select * from #game_stats; 

SELECT RN, 
Gamename, 
Users, 
Times_played 
FROM 
(
    SELECT ROW_NUMBER() OVER (PARTITION BY GS.gamename ORDER BY SUM(GS.times_played) DESC) AS RN, 
     GS.gamename, 
     GS.users, 
     SUM(gs.times_played) as times_played 

FROM #game_stats GS 
WHERE GS.gamename IN (
      SELECT TOP 3 gamename 
          FROM #game_stats 
          GROUP BY gamename 
                   ORDER BY sum(times_played) DESC 
        ) 
GROUP BY GS.gamename,GS.users 
) a 
WHERE RN<=3 
ORDER BY gamename,times_played DESC 
0
SELECT DISTINCT GN,US,GT,UT 
FROM 
(SELECT GN,US,GT,UT, 
     Dense_rank() over(ORDER BY GT DESC) RGT, 
     Dense_rank() over(partition BY GN ORDER BY UT DESC) RUT 
FROM 
    (SELECT gamename GN, 
      users US, 
      times_played TP, 
      sum(times_played) over (partition BY gamename) GT , 
      sum(times_played) over (partition BY gamename,users) UT 
    FROM game_stats)) 
WHERE RGT <4 
AND RUT < 4 
ORDER BY GT DESC, 
     UT DESC 
+0

sprawdź z tym –