简体   繁体   English

优化此SQL查询

[英]Optimize this SQL Query

This SQL query disgusts me. 这个SQL查询让我感到厌恶。 I didn't write it, but it's a massive cause of issues on our servers. 我没有写它,但它是我们服务器问题的一个重要原因。 I'm willing to split it up into multiple queries and do some of the processing via PHP (like, the RAND()). 我愿意将它分成多个查询并通过PHP进行一些处理(比如,RAND())。

$sql = "SELECT a.code, a.ad_id, a.position, a.type, a.image, a.url, a.height, a.width
    FROM " . AD_TABLE ." a, " . USER_GROUP_TABLE . " g
    WHERE (a.max_views >= a.views OR a.max_views = '0')
    AND (FIND_IN_SET(" .$forum_id. ", a.show_forums) > 0 OR a.show_all_forums = '1')
    AND g.user_id = " . $user->data['user_id'] . "
    AND FIND_IN_SET(g.group_id, a.groups)
    AND FIND_IN_SET(" . $user->data['user_rank'] . ", a.ranks)
    AND a.start_time < " . time() . "
    AND a.end_time > " . time() . "
    AND (a.clicks <= a.max_clicks OR a.max_clicks = '0')
    ORDER BY rand()";

Yeesh, I feel icky after pasting that... 是的,粘贴之后我觉得很蠢...

EDIT: Below is the results of the "EXPLAIN" on a sample query in the above format, comma delimited: 编辑:以下是以上格式的示例查询“EXPLAIN”的结果,逗号分隔:

"id","select_type","table","type","possible_keys","key","key_len","ref","rows","Extra"
1,"SIMPLE","g","ref","user_id","user_id","3","const",6,"Using temporary; Using filesort"
1,"SIMPLE","a","ALL","max_views","","","",10,"Using where"

It is 它是

You have three major issues here: 你有三个主要问题:

  1. FIND_IN_SET . FIND_IN_SET

    It's not sargable, an index cannot make it faster. 这不是一件容易的事,一个索引不能让它变得更快。 Create a many-to-many relationship table (or tables). 创建多对多关系表(或多个表)。

    • a.start_time < GETDATE() AND a.end_time > GETDATE()

    MySQL is not good in optimizing that. MySQL不善于优化。 You can keep you timespans as geometry boxes and create a SPATIAL INDEX over them, this will be much faster (though less readable) 您可以将时间跨度作为几何框保留,并为它们创建一个SPATIAL INDEX ,这将更快(尽管可读性较差)

    • ORDER BY RAND()

    If you are using this to sample data (ie you don't need all rows but rather a small random subset), there is a more efficient way to do this, described in this article in my blog: 如果您使用它来对数据进行采样(即您不需要所有行而是需要一个小的随机子集),那么有一种更有效的方法可以执行此操作,如本文在我的博客中所述:

  • You do not have an explicit join in this query between table a and table g : they are only related by find_in_set (g.group_id, a.groups) 表a和表g之间没有显式连接此查询:它们仅与find_in_set相关联(g.group_id,a.groups)
  • How large is your set in "a.groups" , ie does the csv string contain one group-id most of the times? 您在“a.groups”中的设置有多大,即csv字符串大多数时间都包含一个group-id?
  • If 99% of the cases contain only 1 group, then make a foreach loop over "a.groups" in php and execute a real join (or probably it might eliminate your user_group_table altogether) from the query. 如果99%的案例只包含1个组,那么在php中对“a.groups”进行foreach循环,并从查询中执行真正的连接(或者可能完全消除你的user_group_table)。
    • For the minority of cases where you have more than 1 group membership, the query will still perform OK with an explicit join. 对于您拥有多个组成员资格的少数情况,查询仍将使用显式连接执行OK。

This will add some more code in your php class/function. 这将在你的php类/函数中添加更多代码。

I am guessing your problem lies with the dates. 我猜你的问题在于日期。

AND a.start_time < " . time() . "
AND a.end_time > " . time() . "

I would try putting indexes on these fields and see if that helps. 我会尝试在这些字段上放置索引,看看是否有帮助。 Comparing dates causes the database to compare to each row in the table. 比较日期会导致数据库与表中的每一行进行比较。

If you execute this a lot.. try and use bind variables (instead of string concatenation) .. so the query does not have to be parsed every single time.. 如果你执行这么多..尝试并使用绑定变量(而不是字符串连接)..所以查询不必每次都被解析..

edit: Sorry..didn't see the MYSQL tag. 编辑:抱歉..没有看到MYSQL标签。 Unless it's changed recently I don't think MySQL likes prepared statements. 除非最近改变,否则我认为MySQL不喜欢预备语句。 ORACLE on the other hand LOVES them. 另一方面,ORACLE喜欢它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM