php - 优化mysql随机查询

标签 php mysql query-optimization

我有两个表:

Table GAME: id(int), added_at(bigint)
Table META: id_game(int), meta(VARCHAR(64))

现在,每个游戏可以有 0 个或多个与其相关的元标签。我正在尝试检索 9 场比赛:

  • 1 款具有“特色”META 的随机游戏
  • 1 款具有“高级”META 的随机游戏
  • 1 场具有“双倍积分”META 的随机游戏
  • 3 款最新游戏(​​ORDER BY added_at DESC)
  • 不属于上述 6 款游戏的 3 款随机游戏

到目前为止,我有一个相当古怪的系统来执行此操作,它看起来或多或少像这样:

$feat = getGameMetaRandom(1, 'featured');
$prem = getGameMetaRandom(1, 'premium');
$dubl = getGameMetaRandom(1, 'doublepoints');
$last = getGameLatest(3);
$rand = getGameRandom(3);

目前,每个随机函数需要两个查询(来自 getGameMetaRandom($count, $meta); ):

SELECT FLOOR(RAND() * (COUNT(*) - " . ($count - 1) .")) AS `offset` 
FROM table_meta WHERE meta = '{$meta}'

SELECT t1.* FROM table_meta t2
LEFT JOIN table_game t1 ON t1.id = t2.id_game
WHERE t2.meta = '{$meta}' LIMIT {$offset}, {$count}

(gameRandom 非常相似)如您所见,这忽略了我对 不是上述 6 个游戏中的任何一个的限制,而且所有这些都需要 9 个查询,并且随机化并不是真正的随机。

所以我的三个目标和可能的解决方案是:

  1. 如何使 3 个随机游戏不重复上述任何游戏。选择前六个游戏后,我可能可以列出它们的 ID,并在最后一个查询中使用 NOT IN () 使用它们,但这不会过度优化。
  2. 如何使随机游戏随机进行,而不是选择随机偏移并从中获取n个游戏?显然使用 ORDER BY RAND() ,但我听说它有多慢,但我认为除非我的表有数百行,否则没有什么区别?
  3. 如何减少查询次数?将前三个查询分组为一个,剩下 5 个查询,或者通过使用 ORDER BY RAND() 我可以忽略第一个“偏移检索”查询并使用类似 SELECT t1.* FROM table_meta t2 LEFT JOIN table_game t1 ON t1.id = t2.id_game WHERE t2.meta = '{$meta}' ORDER BY RAND() LIMIT {$count} 的查询。

但是,这些人通常需要使用 ORDER BY RAND() 并且我看到的一些测试使它看起来非常慢。有任何进一步改进的提示吗?

最佳答案

游戏 table :

root@localhost [kris]> show create table games\G
*************************** 1. row ***************************
       Table: games
Create Table: CREATE TABLE `games` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `flags` enum('features','premium','doublepoints') NOT NULL,
  `added_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8184 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

示例游戏:

root@localhost [kris]> insert into games values ( NULL, floor(rand() * 4 ), now() - interval 1200 second);
Query OK, 1 row affected, 1 warning (0.00 sec)

Note (Code 1592): Statement may not be safe to log in statement format.

更多示例游戏:

root@localhost [kris]> insert into games select NULL, floor(rand() * 4), now() - interval 1200 second from games;
Query OK, 1 row affected, 1 warning (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

Note (Code 1592): Statement may not be safe to log in statement format.

重复上面的语句,直到有足够的样本数据。数据截断警告可以忽略,它们是在 enum() 列中插入 0 的产物,从而导致无旗游戏,这正是我们想要的。

root@localhost [kris]> select count(*) from games;
+----------+
| count(*) |
+----------+
|     8192 |
+----------+
1 row in set (0.00 sec)

我们创建一个随机的游戏列表:

root@localhost [kris]> create table shuffle like games;
Query OK, 0 rows affected (0.09 sec)

root@localhost [kris]> alter table shuffle modify column id integer not null, drop primary key, add column shuffleid integer not null auto_increment, add primary key (shuffleid), add index(flags), add index(added_at), add index(id);
Query OK, 0 rows affected (0.13 sec)
Records: 0  Duplicates: 0  Warnings: 0

随机播放游戏:

root@localhost [kris]> insert into shuffle select id, flags, added_at, NULL from games order by rand();
Query OK, 8192 rows affected, 1 warning (0.34 sec)
Records: 8192  Duplicates: 0  Warnings: 0

Note (Code 1592): Statement may not be safe to log in statement format.

现在只需获取您需要的内容即可:

root@localhost [kris]> select min(id) as id from shuffle where flags = 'premium' 
    union all select min(id) from shuffle where flags = 'features' 
    union all select min(id) from games where flags = 'doublepoints' 
    union all ( select id from shuffle order by added_at limit 3 );
+------+
| id   |
+------+
| 8216 |
| 8214 |
| 8218 |
| 8213 |
| 8214 |
| 8216 |
+------+
6 rows in set (0.00 sec)

在第二个查询中选择不在上述集合中的 3 个随机行会更有效:

root@localhost [kris]> select id from shuffle where id not in ( 8216, 8214, 8218, 8213, 8214, 8216) limit 3;
+------+
| id   |
+------+
| 8215 |
| 8219 |
| 8220 |
+------+
3 rows in set (0.00 sec)

然后从随机播放中删除 9 个值,以便后续使用该表将生成 9 个新值(或者如果您愿意,可以保留 3 个最近的值)。

关于php - 优化mysql随机查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5350930/

相关文章:

mysql - 如何计算同一查询中的所有结果和特定计数 - MySql

mysql - 选择不使用表格的两个日期之间的所有日期(生成日期列表)

sql-server - 为什么在 SQL Server 中的索引列上执行 top(1) 速度很慢?

javascript - 从文件填充 html 列表

php - 在 Laravel 中删除包含文件的文件夹

php - 带有 backbone.js 的 Zend 框架是个好主意吗?

php - 这个 PHP/MySQL 语句是否容易受到 SQL 注入(inject)攻击?

database - 查询高基数字段

postgresql - 在 PostgreSQL 中将 varchar 更改为 boolean

php - "Access denied for user ' '@' localhost ' to database ' forge”随机出现