phew
phew

Reputation: 826

MySQL - How to GROUP BY more columns and SUM() each group?

currently my query looks like this:

SELECT alias.name killer,
       SUM(kill_stats.amount) amount
FROM kill_stats
        JOIN pickup ON pickup.logfile = 'CTFCL-20130813-1456-shutdown2'
        JOIN account ON account.steam_id = '0:1:705272'
        JOIN player victim ON victim.pickup_id = pickup.id AND victim.account_id = account.id
        JOIN player killer ON killer.pickup_id = pickup.id AND kill_stats.killer_id = killer.id
        JOIN alias ON killer.alias_id = alias.id
WHERE kill_stats.victim_id = victim.id AND NOT killer.team_id = victim.team_id
GROUP BY kill_stats.killer_id
ORDER BY amount DESC

kill_stats table layout:

CREATE TABLE `kill_stats` (
  `killer_id` int(11) UNSIGNED NOT NULL,
  `victim_id` int(11) UNSIGNED NOT NULL,
  `weapon_id` int(11) UNSIGNED NOT NULL,
  `conced` bit(1) NOT NULL,
  `fc` bit(1) NOT NULL,
  `amount` int(11) UNSIGNED NOT NULL,
  PRIMARY KEY(`killer_id`, `victim_id`, `weapon_id`, `conced`, `fc`),
  CONSTRAINT `Ref_Killer` FOREIGN KEY (`killer_id`)
    REFERENCES `player`(`id`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
  CONSTRAINT `Ref_Victim` FOREIGN KEY (`victim_id`)
    REFERENCES `player`(`id`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
  CONSTRAINT `Ref_Weapon` FOREIGN KEY (`weapon_id`)
    REFERENCES `weapon`(`id`)
    ON DELETE CASCADE
    ON UPDATE CASCADE
)
ENGINE=INNODB
CHARACTER SET utf8 
COLLATE utf8_unicode_ci ;

Here a more readable view replacing the foreign keys with dummy data:

+-----------+-----------+-----------+--------+----+--------+
| killer_id | victim_id | weapon_id | conced | fc | amount |
+-----------+-----------+-----------+--------+----+--------+
| Josephine |   Frank   |    RPG    |   NO   | NO |   14   |
+-----------+-----------+-----------+--------+----+--------+
| Josephine |   Frank   |  Shotgun  |   YES  | NO |   5    |
+-----------+-----------+-----------+--------+----+--------+
| Josephine |   Frank   |  Shotgun  |   NO   | NO |   3    |
+-----------+-----------+-----------+--------+----+--------+
| Miguel    |   Frank   |   Knife   |   NO   | NO |   1    |
+-----------+-----------+-----------+--------+----+--------+

Using this example table the query above would return a table like this:

+-----------+--------+
|  killer   | amount |
+-----------+--------+
| Josephine |   22   |
+-----------+--------+
|  Miguel   |   1    |
+-----------+--------+

What I would like the output to be is:

+-----------+-------------+--------------+-----------------+
|  killer   | total_kills | conced_kills | victim_had_flag |
+-----------+-------------+--------------+-----------------+
| Josephine |      22     |      5       |        0        |
+-----------+-------------+--------------+-----------------+
|  Miguel   |      1      |      0       |        0        |
+-----------+-------------+--------------+-----------------+

Showing how often a certain player was killed by other players, the total amount of times that they killed him, the amount of conced kills and how often the player carried the flag when he got killed by them.

I'm not really sure how to achieve that, I have tried GROUP BY kill_stats.killer_id, kill_stats.conced but the result is:

+-----------+--------+
|   killer  | amount |
+-----------+--------+
| Josephine |   14   |  -> the ones with kill_stats.conced = NO
+-----------+--------+
| Josephine |   5    |  -> the ones with kill_stats.conced = YES
+-----------+--------+
| Miguel    |   1    |  -> the ones with kill_stats.conced = NO (only row for that `killer_id`)
+-----------+--------+

I get multiple rows for killer_id and I want one row per killer_id holding all the data.

Ike Walker's solution was almost what I have been looking for, the final query to make it work as I wanted is:

SELECT alias.name killer,
       SUM(kill_stats.amount) as total_kills ,
       SUM(CASE WHEN kill_stats.conced then kill_stats.amount ELSE 0 END) as conced_kills ,
       SUM(CASE WHEN kill_stats.fc then kill_stats.amount ELSE 0 END) as victim_had_flag 
    FROM kill_stats
        JOIN pickup ON pickup.logfile = 'CTFCL-20130813-1456-shutdown2'
        JOIN account ON account.steam_id = '0:1:705272'
        JOIN player victim ON victim.pickup_id = pickup.id AND victim.account_id = account.id
        JOIN player killer ON killer.pickup_id = pickup.id AND kill_stats.killer_id = killer.id
        JOIN alias ON killer.alias_id = alias.id
WHERE kill_stats.victim_id = victim.id AND NOT killer.team_id = victim.team_id
GROUP BY kill_stats.killer_id
ORDER BY amount DESC

The difference to his solution which finally put me on the right track is that I always needed to SUM() kill_stats.amount into the new column conced_kills for each row that has the BIT(1) column set to 1.

Upvotes: 1

Views: 290

Answers (2)

Ed Gibbs
Ed Gibbs

Reputation: 26333

Try changing your query's SELECT list to this:

SELECT alias.name killer,
   SUM(kill_stats.amount) amount,
   SUM(kill_stats.conced) conced_kills,
   SUM(kill_stats.fc) victim_had_flag

... and then pick up with the FROM kill_stats and finish it exactly as you posted it. I'm assuming that conced will be 1 if true and 0 if false; same with fc. I'm also assuming that fc indicates "victim had flag".

Upvotes: 0

Ike Walker
Ike Walker

Reputation: 65527

The standard way to do this is by combining SUM() with CASE for your secondary counts.

Here's your example query rewritten this way to give you the output you are looking for:

SELECT alias.name killer,
       SUM(kill_stats.amount) as total_kills ,
       SUM(CASE WHEN kill_stats.conced then 1 ELSE 0 END) as conced_kills ,
       SUM(CASE WHEN kill_stats.fc then 1 ELSE 0 END) as victim_had_flag 
FROM kill_stats
        JOIN pickup ON pickup.logfile = 'CTFCL-20130813-1456-shutdown2'
        JOIN account ON account.steam_id = '0:1:705272'
        JOIN player victim ON victim.pickup_id = pickup.id AND victim.account_id = account.id
        JOIN player killer ON killer.pickup_id = pickup.id AND kill_stats.killer_id = killer.id
        JOIN alias ON killer.alias_id = alias.id
WHERE kill_stats.victim_id = victim.id AND NOT killer.team_id = victim.team_id
GROUP BY kill_stats.killer_id
ORDER BY amount DESC

Upvotes: 1

Related Questions