2009-01-07 4 views
6

Ich bin auf der Suche nach einer besseren Möglichkeit, die folgende Abfrage auszuführen. Ich habe eine Tabelle, die wie folgt aussieht:SQL Help: Zählen von Zeilen in einer einzelnen Abfrage mit einem geschachtelten SELECT

game_id | home_team_id | away_team_id 
1  | 100   | 200 
2  | 200   | 300 
3  | 200   | 400 
4  | 300   | 100 
5  | 100   | 400 

Und ich möchte eine Abfrage schreiben, die die Anzahl der Heimspiele zählt und Auswärtsspiele für jedes Team und gibt die folgenden:

team_id | home_games | away_games 
100  | 2   | 1 
200  | 2   | 1 
300  | 1   | 1 
400  | 0   | 2 

Gerade jetzt Ich habe dieses Monstrum geschrieben, das funktioniert, aber es ist langsam (ich weiß, dass es die ganze 2.800 Reihe zweimal vom Tisch zieht).

SELECT 
    home_team_id as team_id, 
    (SELECT count(*) FROM `game` WHERE home_team_id = temp_game.home_team_id) as home_games, 
    (SELECT count(*) FROM `game` WHERE home_team_id = temp_game.away_team_id) as away_games 
    FROM (SELECT * FROM `game`) as temp_game 
    GROUP BY home_team_id 

Kann ein SQL-Guru mir helfen, einen besseren Weg, Knock-out? Ich denke mein Problem ist, dass ich nicht verstehe, wie man eine eindeutige Liste der Team-IDs erhält, die bei den Zählungsabfragen geworfen werden. Ich wette, es gibt einen besseren Weg mit einem besser platzierten, verschachtelten SELECT. Danke im Voraus!

+0

Vielen Dank für die große Hilfe. Am Ende entschied ich, dass es dumm war, zu überarbeiten und ging mit Frank Flynn's 2-Tisch-Ansatz (fügte eine Team-Tabelle hinzu und schloss sich ihnen an). – Greg

Antwort

9

Es ist sauberer, wenn Sie einen anderen Tisch Team mit team_id und TEAM_NAME haben.

Was passiert: Die no WHERE-Klausel verursacht ein kartesisches Produkt zwischen den beiden Tabellen; Wir gruppieren uns nach team_id, um zu einer Zeile pro Team zurückzukehren. Jetzt gibt es alle Zeilen aus der Spieltabelle für jede team_id, also müssen Sie sie zählen, aber die SQL-Zählfunktion ist nicht ganz richtig (sie würde alle Zeilen oder alle eindeutigen Zeilen zählen). Also sagen wir team_id = home_team_id, das zu 1 oder 0 aufgelöst wird und wir addieren die 1s mit sum.

Die Team_name ist nur, weil es geeky zu sagen, dass "Team 200 20 Heimspiele hatte", wenn wir sagen sollten, dass "Mud City Stranglers 20 Heimspiele" hatte.

PS. Dies funktioniert auch, wenn es keine Spiele gibt (oft ein Problem in SQL, wo es ein Team mit 0 Spielen gibt und diese Zeile nicht angezeigt wird, weil der Join fehlschlägt).

+0

Wow, ich muss einen schweren Gehirnkrampf haben, weil das vollkommen Sinn macht. Ich habe eigentlich einen Teamtisch ... aber ich wollte mein Gehirn ein wenig trainieren (und dann stecken geblieben und kam zu SO!). :) – Greg

+0

Ich verneige mich vor deiner Großartigkeit! –

+1

Nicht wirklich eine gute Antwort, weil es Artefakte annimmt, die nicht bekannt waren oder präsentiert wurden. Ich bin begeistert, dass es für das ursprüngliche Poster funktioniert hat, aber es ist wirklich nicht passend. Es ist nur Zufall, dass es für das ursprüngliche Poster funktioniert hat. – casperOne

3

Wenn Sie die eindeutige Liste der Teams wollen, müssen Sie zweimal aus der Spieltabelle auswählen und die Heim- und Auswärtsmannschaft zusammenbringen (theoretisch könnte eine Mannschaft alle ihre Spiele auf der Straße oder zu Hause spielen, wenn Sie haben Logik, die das verhindert, dann könnten Sie diese Abfrage) anpassen:

select home_team_id as team_id from game union 
select away_team_id as team_id from game 

der union Betreiber sicher, dass Sie nur einzelne Elemente im Rück Satz bekommen machen wird (es sei denn, Sie union all verwenden)

Von dort können Sie Verwenden Sie die linken äußeren Joins, um Ihre Daten zu aggregieren:

select 
    u.team_id, count(h.game_id) as home_games, count(a.game_id) as away_games 
from 
    (
     select home_team_id as team_id from game union 
     select away_team_id as team_id from game 
    ) as u 
     left outer join game as h on h.home_team_id = u.team_id 
     left outer join game as a on a.away_team_id = u.team_id 
group by 
    u.team_id 

Wenn Sie Ihre Tabellen-Scans noch weiter reduzieren möchten (das obige ergibt vier), können Sie mehr Code hinzufügen, aber das kostet Sie. Sie können eine Liste von Zeilen mit dem team_id bekommen, und ob das Spiel wurde zu Hause gespielt oder weg:

select 
    case ha.home when 0 then g.away_team_id else g.home_team_id end as team_id, 
    case ha.home when 0 then 0 else 1 end as home_games, 
    case ha.home when 0 then 1 else 0 end as away_games 
from 
    game as g, (select 0 as home union select 1 as home) as ha 

Von dort können Sie einfach die Spiele zu Hause zusammenzufassen und für jedes Team weg:

select 
    t.team_id, sum(t.home_games) as home_games, sum(t.away_games) as away_games 
from 
    (
     select 
      case ha.home when 0 then g.away_team_id else g.home_team_id end as team_id, 
      case ha.home when 0 then 0 else 1 end as home_games, 
      case ha.home when 0 then 1 else 0 end as away_games 
     from 
      game as g, (select 0 as home union select 1 as home) as ha 
    ) as t 
group by 
    t.team_id 

Dies führt zu einem einzelnen Tabellen-Scan.

2

Greg,

Ich denke, Ihre ultimative Lösung sprachspezifisch sein. Aber wenn Sie in Oracle tun dies, so könnte man die Tabelle abfragen nur einmal mit dem folgenden:

SELECT game.home_team_id AS team_id, 
     SUM(CASE WHEN game.home_team_id = game.away_team_id 
       THEN 1 
       ELSE 0 END) AS home_games, 
     SUM(CASE WHEN game.home_team_id <> game.away_team_id 
       THEN 1 
       ELSE 0 END) AS away_games 
    FROM game 
GROUP BY game.home_team_id 
ORDER BY game.home_team_id; 

Sie sagen nicht, was Geschmack von SQL Sie verwenden so dass dies das Beste ist, was ich tun kann.

Best of luck,

Stew

P. S. Es sieht so aus, als hätte ich die gleiche Lösung wie MarlonRibunal gegeben. Ich hatte einfach keinen handlichen Link und musste den Code von Hand erstellen.: -/

+0

Ich benutze MySQL 5.0. Ich hatte gehofft, es könnte getan werden, ohne an herstellerspezifische Details zu denken ... aber naja! – Greg

+0

Greg, Ich habe versucht, dies als Hersteller-neutral zu schreiben, wie ich könnte. Ich weiß, dass CASE in mehreren Varianten unterstützt wird, also würde ich es versuchen und sehen, was passiert. –

+0

Ich habe es versucht ... es ist schnell, aber die Ergebnisse waren ein bisschen. Ich sehe eine genaue Auflistung der Heimspiele (ich weiß, was die Summen sein sollten), aber sie sind unter dem Label "Auswärtsspiele". Außerdem ist die Spalte "Heimspiele" nur Nullen, wenn ich weiß, dass sie Werte haben sollte. Irgendeine Idee, was zu zwicken? – Greg

0

Versuchen Sie folgendes:

Select Z.teamId, 
    Count(H.Game_Id) HomeGames, 
    Count(A.Game_Id) AwayGames 
From (Select Distinct home_team_id TeamId From Game 
     Union 
     Select Distinct away_team_id TeamId From Game) Z 
    Left Join Game H On H.home_team_id = Z.TeamId 
    Left Join Game A On A.away_team_id = Z.TeamId 
Group By Z.TeamId 
+0

Union ist nicht schlecht, aber die Verwendung der Team-Tabelle sollte das gleiche Problem für weniger Kosten lösen. –

+0

welche team tabelle? Ich sah nicht, dass er einen in seinem Schema hatte ... ein Teamtisch wäre viel besser. –

0
declare @ts table 

(
    team_id int 
) 

declare @t table 
(
    id int, 
    h int, 
    a int 
) 

insert into @ts values (100) 
insert into @ts values (200) 
insert into @ts values (300) 
insert into @ts values (400) 

insert into @t values (1, 100, 200) 
insert into @t values (2, 200, 300) 
insert into @t values (3, 200, 400) 
insert into @t values (4, 300, 100) 
insert into @t values (5, 100, 400) 

select s.team_id, t0.home, t1.away 
from @ts s 
    left outer join (select team_id, count(h) as [home] from @ts inner join @t on h = team_id group by team_id) t0 on t0.team_id = s.team_id 
    left outer join (select team_id, count(a) as away from @ts inner join @t on a = team_id group by team_id) t1 on t1.team_id = s.team_id 
0

Hier ist ein weiteres Beispiel.

Ich würde jedoch darauf hinweisen, dass Sie Ihre From-Klausel aus der Teamtabelle starten sollten, so dass Sie sicher sein werden, alle Teams aufzunehmen, auch wenn sie noch kein Spiel gespielt haben.

Diese Abfrage führt Ihre zwei Abfragen als Joins anstelle von Subselects durch, die besser funktionieren sollten.

- Hinweis: Coalesce ist wie ifnull für den Fall, dass Sie MySQL verwenden.

SELECT 
team_id as team_id, 
coalesce(home_game_counts.games,0) home_games, 
coalesce(away_game_counts.games,0) away_games 
FROM teams 
left join (select home_team_id, count(*) games from games group by home_team_id) as home_game_counts on home_game_counts.home_team_id = teams.team_id 
left join (select away_team_id, count(*) games from games group by away_team_id) as away_game_counts on away_game_counts.away_team_id = teams.team_id 
GROUP BY teams.team_id, home_game_counts.games , 
away_game_counts.games 
0

Diese Lösung ist ziemlich hässlich, aber es sollte schnell über große Datenmengen arbeiten:

select 
    teams.team_id 
,case when home.home_game_count is null 
     then 0 
     else home.home_game_count 
    end home_game_count 
,case when away.away_game_count is null 
     then 0 
     else away.away_game_count 
    end as away_game_count 
from 
    ( 
    select home_team_id as team_id from games 
    union 
    select away_team_id as team_id from games 
) teams 
    left outer join 
    ( 
    select home_team_id as team_id, count(*) as home_game_count 
    from games 
    group by home_team_id 
) home 
    on teams.team_id = home.team_id 
    left outer join 
    (
    select away_team_id as team_id, count(*) as away_game_count 
    from games 
    group by away_team_id 
) away 
    on teams.team_id = away.team_id 
0

Sorry, mein Fehler in der away_games Klausel. Ich habe den Vergleichsoperator (auf <>) geändert, anstatt den resultierenden Wert zu ändern. Ich musste zusätzliche Daten erstellen, um das Problem zu sehen.

SELECT team_id, 
     teams.team_name, 
     SUM(CASE 
       WHEN game.home_team_id = game.away_team_id THEN 
       1 
       ELSE 
       0 
      END) AS home_games, 
     SUM(CASE 
       WHEN game.home_team_id = game.away_team_id THEN 
       0 
       ELSE 
       1 
      END) AS away_games 
    FROM teams 
    LEFT OUTER JOIN game ON game.home_team_id = teams.team_id 
GROUP BY team_id, teams.team_name 
Verwandte Themen