Edited for detail of my case.
CREATE TABLE IF NOT EXISTS `tbl_user` (
`id` int(50) NOT NULL auto_increment,
`fbuid` bigint(20) unsigned NOT NULL,
`fullname` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `fbuid` (`fbuid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;
INSERT INTO `tbl_user` (`id`, `fbuid`, `fullname`) VALUES
(1, 1002, 'User B'),
(2, 1001, 'User A'),
(3, 1003, 'User C'),
(4, 1004, 'User D'),
(5, 1005, 'User E'),
(6, 1006, 'User F');
CREATE TABLE IF NOT EXISTS `tbl_userscores` (
`fbuid` bigint(20) NOT NULL,
`game_id` varchar(255) NOT NULL,
`score1` bigint(20) NOT NULL default '0',
`score2` bigint(20) NOT NULL default '0',
`score3` bigint(20) NOT NULL default '0',
`score4` bigint(20) NOT NULL default '0',
`created_date` datetime NOT NULL,
`updated_date` datetime NOT NULL,
PRIMARY KEY (`game_id`),
UNIQUE KEY `fbuid` (`fbuid`,`game_id`),
KEY `fbuid_2` (`fbuid`,`game_id`,`score4`),
KEY `fbuid_3` (`fbuid`,`game_id`,`score4`,`updated_date`),
KEY `fbuid_4` (`fbuid`,`game_id`,`score1`,`score2`,`score3`,`score4`,`created_date`,`updated_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `tbl_userscores` (`fbuid`, `game_id`, `score1`, `score2`, `score3`, `score4`, `created_date`, `updated_date`) VALUES
(1001, '13361975565253060', 650, 3300, 7675, 14500, '2012-05-05 13:59:55', '2012-05-05 14:01:50'),
(1001, '1336278398787510', 3100, 87725, 326675, 573625, '2012-05-06 12:28:20', '2012-05-06 12:33:27'),
(1001, '13368015862343980', 12875, 82550, 158625, 299550, '2012-05-12 13:48:08', '2012-05-12 13:53:15'),
(1001, '13369691453105020', 7925, 58525, 283100, 368225, '2012-05-14 12:20:47', '2012-05-14 12:25:54'),
(1002, '1336328839124400', 1275, 11475, 31450, 50475, '2012-05-07 02:27:34', '2012-05-07 02:28:20'),
(1002, '13363686059958120', 11025, 48900, 72725, 115150, '2012-05-07 13:30:21', '2012-05-07 13:31:07'),
(1002, '13364088902032830', 6650, 6700, 10200, 17625, '2012-05-08 00:41:46', '2012-05-08 00:42:32'),
(1002, '13364910479425300', 3600, 17050, 60450, 114800, '2012-05-08 23:31:03', '2012-05-08 23:31:49'),
(1002, '13364949763272710', 17250, 168125, 479475, 596925, '2012-05-07 00:37:33', '2012-05-07 00:41:21'),
(1003, '13363240964199380', 84150, 84150, 84150, 84150, '2012-05-07 01:11:37', '2012-05-07 01:12:22'),
(1003, '1336465518338010', 297275, 351300, 437150, 468350, '2012-05-08 16:31:52', '2012-05-08 16:32:38'),
(1003, '13368122913207860', 0, 82350, 94150, 102750, '2012-05-12 16:45:20', '2012-05-12 16:48:09'),
(1003, '13368125091164060', 423925, 428125, 521875, 589750, '2012-05-12 16:54:00', '2012-05-12 16:54:47'),
(1004, '13363118226930570', 3275, 10975, 16250, 22900, '2012-05-06 21:43:58', '2012-05-06 21:44:43'),
(1004, '13366228756934380', 23275, 149100, 380600, 382075, '2012-05-10 12:08:46', '2012-05-10 12:10:49'),
(1004, '13366232802957960', 3650, 23525, 49975, 49975, '2012-05-10 12:14:55', '2012-05-10 12:15:42'),
(1005, '13361215491096720', 1200, 16250, 39125, 55800, '2012-05-04 16:52:59', '2012-05-04 16:54:29'),
(1005, '13361216729657120', 11000, 29800, 82575, 188550, '2012-05-04 16:55:03', '2012-05-04 16:56:33'),
(1005, '13361364491988250', 6925, 50925, 89100, 180425, '2012-05-04 21:01:12', '2012-05-04 21:02:43'),
(1005, '13362204979150640', 11300, 39800, 63675, 78725, '2012-05-05 20:22:08', '2012-05-05 20:23:36'),
(1005, '13362311869003160', 11575, 61500, 134200, 233600, '2012-05-05 23:20:17', '2012-05-05 23:21:48'),
(1005, '133628163373910', 3500, 40175, 131375, 251725, '2012-05-06 13:21:03', '2012-05-06 13:22:35'),
(1006, '13361224889844730', 6700, 30575, 49650, 50475, '2012-05-04 17:08:24', '2012-05-04 17:09:10'),
(1006, '13366294182421110', 16800, 87675, 119150, 206500, '2012-05-10 13:57:42', '2012-05-10 14:00:15'),
(1006, '13366296357158010', 23050, 99025, 229075, 381925, '2012-05-10 14:01:27', '2012-05-10 14:03:58'),
(1006, '13368319289949330', 22975, 130375, 350600, 355150, '2012-05-12 22:13:00', '2012-05-12 22:15:08');
With above data, I use sql below to get weekly highscore.
SELECT U1.fbuid, U1.fullname, U2.score4 AS weeklyhighscore, U2.created_date, U2.updated_date, TIMEDIFF( U2.updated_date, U2.created_date ) AS Duration
FROM tbl_user AS U1, (
SELECT fbuid, score4, MIN( updated_date ) AS updated_date, created_date
FROM tbl_userscores AS A
WHERE A.score4
IN (
SELECT MAX( `score4` ) AS best
FROM tbl_userscores AS B
WHERE A.fbuid = B.fbuid
AND B.score1 >0
AND B.score2 >0
AND B.score3 >0
AND B.score4 >0
AND `updated_date` >= '2012-05-06 00:00:00' AND `updated_date` <= '2012-05-12 23:59:59'
GROUP BY fbuid
)
GROUP BY A.fbuid
ORDER BY `A`.`score4` DESC , updated_date ASC
) AS U2
WHERE U1.fbuid = U2.fbuid
ORDER BY weeklyhighscore DESC
LIMIT 0 , 30
Expected result :
+-------+----------+-----------------+---------------------+---------------------+----------+
| fbuid | fullname | weeklyhighscore | created_date | updated_date | Duration |
| 1002 | User B | 596925 | 2012-05-07 00:37:33 | 2012-05-07 00:41:21 | 00:03:48 |
| 1003 | User C | 589750 | 2012-05-12 16:54:00 | 2012-05-12 16:54:47 | 00:00:47 |
| 1001 | User A | 573625 | 2012-05-06 12:28:20 | 2012-05-06 12:33:27 | 00:05:07 |
| 1004 | User D | 382075 | 2012-05-10 12:08:46 | 2012-05-10 12:10:49 | 00:02:03 |
| 1006 | User F | 381925 | 2012-05-10 14:01:27 | 2012-05-10 14:03:58 | 00:02:31 |
| 1005 | User E | 251725 | 2012-05-06 13:21:03 | 2012-05-06 13:22:35 | 00:01:32 |
+-------+----------+-----------------+---------------------+---------------------+----------+
I have two table, tbl_user and tbl_userscores. Each time user played a game, it will save times as score1 to score4 (4 session of scores, which score4 is final score).
tbl_userscores was indexed with (fbuid,score4,updated_date,create_date). It have 45K records, and keep growing.
I want to get top 30 weekly highscorer. This query took me average 45sec to complete.
So I would like to seek expert’s advice on how to make it much better.
Thanks in advance.
I believe that most of the time is spent in correlated subquery that extracts max(score4) per user. It might be restructured to get top 30 scores at once and used as a filter to main table. Unfortunately, as you might get duplicates and need to take earliest updated_date to avoid them, there is additional derived table to get this filter. If this proves to be the slowest part, you might remove
minUpdatedderived table, envelop complete query and usenot existsto select records with minimal updated_date per score4 only. This should be faster as you will tipically have just a bit over 30 records.I’ve replaced date comparison with more promising pattern >= and <. This change avoids the datetime resolution issues (you might loose a record if an update is made in last 999 milliseconds of a day). This is also great defence tool – your query will work even if somebody somehow manages to input time portion of a date where your business logic does not expect one.