2

私のケースの詳細のために編集されました。

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');

上記のデータを使用して、以下のsqlを使用して毎週のハイスコアを取得します。

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

期待される結果 :

+-------+----------+-----------------+---------------------+---------------------+----------+
| 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 |
+-------+----------+-----------------+---------------------+---------------------+----------+

tbl_userとtbl_userscoresの2つのテーブルがあります。ユーザーがゲームをプレイするたびに、score1からscore4(4セッションのスコア。score4が最終スコア)として時間を節約できます。

tbl_userscoresは(fbuid、score4、updated_date、create_date)でインデックス付けされました。45Kのレコードがあり、成長を続けています。

毎週のハイスコアトップ30を獲得したいです。このクエリは、完了するのに平均45秒かかりました。

それで、私はそれをもっと良くする方法について専門家のアドバイスを求めたいと思います。

前もって感謝します。

4

1 に答える 1

0

ほとんどの時間は、ユーザーごとにmax(score4)を抽出する相関サブクエリに費やされていると思います。一度に上位30のスコアを取得するように再構築され、メインテーブルへのフィルターとして使用される場合があります。残念ながら、重複が発生する可能性があり、それらを回避するために最も早いupdated_dateを取得する必要があるため、このフィルターを取得するための追加の派生テーブルがあります。これが最も遅い部分であることが判明した場合は、minUpdated派生テーブルを削除し、完全なクエリをnot exists包み込み、score4ごとに最小限のupdated_dateでレコードを選択するために使用できます。通常、30を少し超えるレコードがあるため、これは高速になるはずです。

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
INNER JOIN tbl_userscores U2
   ON U1.FbUid = U2.FbUid
/* Top 30 scores by user */
INNER JOIN
(
  SELECT B.fbuid, 
         MAX(`score4`) AS best
    FROM tbl_userscores AS B
   WHERE 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-13 00:00:00'
   GROUP BY fbuid
   ORDER BY best DESC
   LIMIT 30
) A
   ON U2.FbUid = A.FbUid
  AND U2.Score4 = best
/* Filter by min(updated_date) in case of several same scores per user */
INNER JOIN
(
  SELECT FbUid, Score4, MIN(updated_date) updated_date
    FROM tbl_userscores
   GROUP BY FbUid, Score4
) minUpdated
   ON U2.FbUid = minUpdated.FbUid
  AND U2.Score4 = minUpdated.Score4
  AND U2.Updated_date = minUpdated.Updated_date
ORDER BY weeklyhighscore DESC

日付の比較をより有望なパターン>=および<に置き換えました。この変更により、日時の解決の問題が回避されます(1日の最後の999ミリ秒に更新が行われた場合、レコードが失われる可能性があります)。これは優れた防御ツールでもあります。ビジネスロジックが予期しない日付の時間部分を誰かがなんとか入力した場合でも、クエリは機能します。

于 2012-05-14T09:13:36.567 に答える