SQL-Abfrage optimieren



  • Hi! Sorry fuer den Nichtssagenden Titel, ich wusste keinen besseren 😉 Trotzdem hoff ich es findet sich wer, der mir bei meinem Problem weiterhilft 🙂

    Ich hab folgende Tabelle:

    sqlite> CREATE TABLE test(
       ...> id INTEGER PRIMARY KEY,
       ...> winner INTEGER,
       ...> loser INTEGER);
    
    sqlite> SELECT * FROM test;
    0|1|2
    1|1|2
    2|1|3
    3|3|1
    4|3|1
    5|3|2
    6|4|1
    

    Und moechte gern eine Abfrage darauf fahren, die mir folgendes Ergebnis liefern soll:

    Statistics for player 1:
    
    ENEMY | WINS | DEFEATS
    ------+------+--------
      2   |   2  |   0
      3   |   1  |   2
      4   |   0  |   1
    

    Leider weiss ich nicht, wie ich das in SQL loesen kann. Momentan mach ich das, indem ich folgende 2 Abfragen ausfuehre und die dann von meinem Programm zu einer einzigen Tabelle zusammenfasse:

    sqlite> SELECT loser, COUNT(*) FROM test WHERE winner = 1 GROUP BY loser;
    2|2
    3|1
    
    sqlite> SELECT winner, COUNT(*) FROM test WHERE looser = 1 GROUP BY winner
    3|2
    4|1
    

    Allerdings moechte ich wenn moeglich die Arbeit von der Datenbank erledigen lassen. Deshalb meine Frage: ist es moeglich, eine SQL-Abfrage zu schreiben, welche das gewuenschte Ergebnis liefert? Wenn ja, wie?
    (Benutztes Datenbanksystem ist SQLite 3)



  • hmm... Mittlerweile hab ich eine Loesung gefunden. Da SQLite noch keine OUTER Joins unterstuetzt ist das Ganze ziemlich haesslich:

    SELECT w.loser, w.wins, d.defeats
    FROM
    	(SELECT loser, COUNT(*) AS wins FROM test WHERE winner = 1 GROUP BY loser) w
    	LEFT JOIN
    	(SELECT winner, COUNT(*) AS defeats FROM test WHERE looser = 1 GROUP BY winner) d
    	ON w.loser = d.winner
    UNION
    SELECT d.winner, w.wins, d.defeats
    FROM
    	(SELECT winner, COUNT(*) AS defeats FROM test WHERE loser = 1 GROUP BY winner) d
    	LEFT JOIN
    	(SELECT loser, COUNT(*) AS wins FROM test WHERE winner = 1 GROUP BY loser) w
    	ON w.loser = d.winner
    

    Gibts dafuer eine einfachere Loesung?



  • Laut Profiler ist obige Abfrage eines der groessten Bottlenecks in meiner Applikation. Hat irgendwer einen Rat, wie man die Abfrage optimieren koennte?



  • So geht es beim SQL-Server. Falls MySql kein "CASE WHEN"-Konstrukt kennt, ersetze es durch ein MySql-Konstrukt (falls vorhanden) oder eine Unterabfrage. Wie es performancemäßig aussieht, musst du selber herausfinden.

    SELECT enemy = CASE WHEN winner <> 1 THEN winner ELSE loser END,
           wins = ( SELECT COUNT(*) FROM test t2 WHERE winner = 1 AND loser = MAX(test.loser) ),
           defeats = ( SELECT COUNT(*) FROM test t2 WHERE winner = MAX(test.winner) AND loser = 1 )
    FROM test 
    WHERE winner = 1 
    OR loser = 1
    GROUP BY CASE WHEN winner <> 1 THEN winner ELSE loser END
    


  • Danke fuer die Antwort. SQLite besitzt leider keine CASE-Anweisung. Und so wie ich deine Abfrage verstehe (hab kein SQL-Server zur Hand ums auszutesten) liefert deine Abfrage ja nur die Spieler mit den meisten Gewinnern/Verlieren (du verwendest ein MAX im WHERE der Unterabfrage), oder missinterpretiere ich das falsch?MAX(test.loser)



  • Weiß zwar nicht ob SQLite 3 das kann, aber kannst ja mal versuchen

    select	enemy, sum(wins) as wins, sum(loses) as loses
    from	(
    	select	loser as enemy, count(*) as wins, 0 as loses
    	from	test
    	where	winner = 1
    	group	by loser
    
    	union all
    
    	select	winner as enemy, 0 as wins, count(*) as loses
    	from	test
    	where	loser = 1
    	group	by winner ) a
    group	by enemy
    


  • Blue-Tiger schrieb:

    Danke fuer die Antwort. SQLite besitzt leider keine CASE-Anweisung. Und so wie ich deine Abfrage verstehe (hab kein SQL-Server zur Hand ums auszutesten) liefert deine Abfrage ja nur die Spieler mit den meisten Gewinnern/Verlieren (du verwendest ein MAX im WHERE der Unterabfrage), oder missinterpretiere ich das falsch?MAX(test.loser)

    Hmm..., hab das mit deinen Testdaten durchgeführt und das gewünschte Resultat erhalten.

    EDIT: Stimmt, so gehts nicht...



  • Airdamn schrieb:

    Weiß zwar nicht ob SQLite 3 das kann, aber kannst ja mal versuchen

    select	enemy, sum(wins) as wins, sum(loses) as loses
    from	(
    	select	loser as enemy, count(*) as wins, 0 as loses
    	from	test
    	where	winner = 1
    	group	by loser
    	
    	union all
    	
    	select	winner as enemy, 0 as wins, count(*) as loses
    	from	test
    	where	loser = 1
    	group	by winner ) a
    group	by enemy
    

    Thx! Supertipp 🙂 Funktioniert wunderbar und brachte meinem Programm 10% mehr Geschwindigkeit 🙂 Thx a lot 🙂


Anmelden zum Antworten