优化此SQL查询

时间:2009-06-30 14:27:59

标签: php sql mysql optimization

这个SQL查询让我感到厌恶。我没有写它,但它是我们服务器问题的一个重要原因。我愿意将它分成多个查询并通过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,在粘贴之后我觉得很蠢......

编辑:以下是上述格式的示例查询中“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"

4 个答案:

答案 0 :(得分:6)

这里有三个主要问题:

  1. FIND_IN_SET

    这不是一件容易的事,索引无法让它变得更快。创建多对多关系表(或多个表)。

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

    MySQL不善于优化。您可以将时间跨度保存为几何框并在其上创建SPATIAL INDEX,这将更快(尽管可读性更低)

    • ORDER BY RAND()

    如果您使用它来对数据进行采样(即,您不需要所有行,而是需要一个小的随机子集),那么有一种更有效的方法可以执行此操作,如本文在我的博客中所述:

答案 1 :(得分:4)

  • 你在表a和表格g之间的中没有明确的连接:它们只与find_in_set相关联(g.group_id,a.groups)
  • 您在“a.groups”中的设置有多大,即csv字符串大多数时间都包含一个group-id?
  • 如果99%的案例只包含1个组,那么在php中对“a.groups”进行foreach循环,并从查询中执行真正的连接(或者可能完全消除你的user_group_table)。
    • 对于您拥有1个以上组成员身份的少数情况,查询仍然可以通过显式联接执行。

这将在你的php类/函数中添加更多代码。

答案 2 :(得分:1)

我猜你的问题在于日期。

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

我会尝试在这些字段上放置索引,看看是否有帮助。比较日期会导致数据库与表中的每一行进行比较。

答案 3 :(得分:0)

如果你执行了很多..尝试并使用绑定变量(而不是字符串连接)..所以查询不必每次都被解析..

编辑:抱歉..没有看到MYSQL标签。除非最近改变,否则我认为MySQL不喜欢预备语句。另一方面,ORACLE喜欢它们。