如何根据约束编写高效的MySQL查询来删除特定的行

时间:2010-01-22 15:09:46

标签: sql mysql mysql-management

我对我的MySQL数据库提出了一个问题,并希望获得最有效的信息。

我的问题如下,

我正在为我的棋盘游戏网站开发高级功能。一个高级功能是用户所玩的所有游戏将“永久”存储(以供用户随后查找)。对于普通用户,将删除超过18个月的游戏。

现在我需要找出一种有效的方法来删除普通非高级用户的游戏(超过18个月)并为高级用户保留游戏。

简化事情我得到了两张桌子(实际上还有一张桌子可以存储每场比赛的游戏参与者):

游戏,

 id=INT
 play_date=DATETIME
 end_score=INT
 player_id_1=INT
 player_id_2=INT

用户,

 id=INT
 premium=BOOLEAN (true=enabled, false=not enabled)

用户表包含300.000+行,而Games表包含几百万行。每天约有20,000场比赛 被添加到游戏桌中。

从非高级用户中删除超过18个月的游戏的最有效方法是什么。

到目前为止,我们已经为每个星期一早上的所有用户删除了超过18个月的游戏。

现在我需要考虑高价值和游戏日期。

一些解决方案(?):

  • 加入表格,尽管我们在游戏桌上谈论了数百万行,但这是不是没有?
  • 获取超过18个月的每个游戏条目,然后让每个用户从player_id_1& player_id_2,如果其中任何一个是 高级,让游戏,如果它超过18个月,否则删除它。因此,在一周内,这可能是20k * 7 = 140k的游戏。
  • 以上解决方案除了我每小时都这样做。然后我需要大约1000场比赛来检查。
  • ?在Games表中添加某种辅助变量?但是,如果用户停止使用高级版....

欢迎任何提示......

4 个答案:

答案 0 :(得分:3)

使用过期日期和索引。

在该列上允许NULL。

高级用户的游戏将为NULL。

使用expire_date<删除游戏sysdate将使用索引,它是一个INDEX RANGE SCAN(它必须是可订购索引,我的意思是,某种B树内部表示......但我不是MySQL的专家)。

修改

或者使用expire_date保存一个单独的注册表PK表。因此,高级用户记录不会占用太多空间。然后你从xxx中删除pk in(从expiring_table中选择pk)。

但这并不比以前的解决方案有很好的改进。

<强>年龄

也许你可以使用年龄(例如1岁= 1个月)。并在表格中设置字段“month_to_live”。对于非空的所有记录,每月更新字段+ = 1。那使用了相等过滤器。但正如我之前所说,我不是专家,所以我不知道你可以从中获得多少优化。)

我应该坚持使用expire_date字段(并且您有额外的功能可以将实时期限单独扩展到任何人,而不会在删除记录时产生额外的开销)。

答案 1 :(得分:1)

下载数据库转储并在您的计算机上进行基准测试。这应该可以让您很好地了解各种解决方案的性能

除此之外,这是对这些SQL查询问题的通常回复:对查询运行“解释”并确保您拥有正确的索引。

答案 2 :(得分:0)

加入不应该太糟糕,我猜你是不是在做那个“直播”的查询? 另一种选择是在显示游戏时进行查询:如果用户是高级用户,则不限制,否则限制范围。

答案 3 :(得分:0)

每次用户更改状态时,您都需要避免使用更新游戏表的解决方案,因为这是不必要且缓慢的。

这是一个。假设您每天都在同一时间运行查询:

DELETE games FROM games
JOIN users u1 ON (u1.id=games.player1_id AND NOT u1.premium)
JOIN users u2 ON (u1.id=games.player2_id AND NOT u2.premium)
WHERE games.play_date BETWEEN DATE_SUB( now(), INTERVAL 18 MONTH 1 DAY 1 HOUR)
AND DATE_SUB( now(), INTERVAL 18 MONTH )

当然你应该有一个游戏索引(play_date)。

这里的想法是日期范围检查仅检查可能需要删除的游戏,并且尚未经过昨天的查询检查。一种“滚动窗口”。

相反,这个:

WHERE games.play_date < DATE_SUB( now(), INTERVAL 18 MONTH )

每次都会检查表中所有现有的游戏,并且速度会慢得多。