Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do a mysql join with default value for missing rows

Tags:

mysql

I have a table tracking daily scores by users. It looks roughly like this:

CREATE TABLE `DailyScores` (
  `player_id` INTEGER NOT NULL,
  `day_id` INTEGER NOT NULL,
  `score` DOUBLE NOT NULL
);

I also have a player table that looks like this:

CREATE TABLE `Players` (
  `player_id` INTEGER NOT NULL,
  `weighted_score` DOUBLE NOT NULL
);

Now the players weighted_score is to be recalculated daily from the players historical data. I can do this in several passes, something like this:

-- Clear out the old values
UPDATE Players SET Players.weighted_score = 0;

-- Then several times with different @weight and @day values
UPDATE Players p JOIN DailyScores ds
  ON p.player_id = ds.id 
  WHERE ds.day_id = @day 
  SET p.weighted_score = p.weighted_score + @weight * ds.score;

This handles the case that there may be a day that a player does not have a score entry for.

However I'd like to rewrite the UPDATE to be more like this:

UPDATE Players p
  JOIN DailyScores ds1 ON p.player_id = ds1.id 
  JOIN DailyScores ds2 ON p.player_id = ds2.id 
  JOIN DailyScores ds3 ON p.player_id = ds3.id 
  WHERE 
    ds1.day_id = @day1 AND
    ds2.day_id = @day2 AND
    ds3.day_id = @day3 
  SET p.weighted_score = @w1 * ds1.score + @w2 * ds2.score + @w3 * ds3.score;

But I think this will fail if there is a missing score. Is there anyway to make the values for the missing days be 0?

like image 631
Michael Anderson Avatar asked Jan 27 '26 08:01

Michael Anderson


1 Answers

Use the COALESCE() function. It returns the first of its parameters which is not NULL.

UPDATE Players p
  LEFT JOIN DailyScores ds1 ON ( p.player_id = ds1.id AND ds1.day_id = @day1)
  LEFT JOIN DailyScores ds2 ON ( p.player_id = ds2.id AND ds2.day_id = @day2)
  LEFT JOIN DailyScores ds3 ON ( p.player_id = ds3.id AND ds3.day_id = @day3)
  SET p.weighted_score = 
     @w1 * COALESCE(ds1.score, 0) +
     @w2 * COALESCE(ds2.score, 0) +
     @w3 * COALESCE(ds3.score, 0);

Also you have to do a LEFT JOIN since there may be no record in DailyScores when the player didn't play.

like image 88
fancyPants Avatar answered Jan 29 '26 23:01

fancyPants



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!