Optimization 优化MySQL查询

Optimization 优化MySQL查询,optimization,mysql,query-optimization,Optimization,Mysql,Query Optimization,我们有一个查询,目前正在杀死我们的数据库,我知道必须有一种方法来优化它。我们有3张桌子: 项目-项目表,其中每个项目都有相关的对象id、长度、难度等级、等级、平均等级和状态 列表-列表表,基本上是用户创建的项目列表 列表项-包含两列的表:列表项id、项目项id 我们一直在使用以下查询来显示一个简单的HTML表,该表显示每个列表以及与该列表相关的许多属性,包括所包含列表项的属性平均值: select object_id, user_id, slug, title, description, ite

我们有一个查询,目前正在杀死我们的数据库,我知道必须有一种方法来优化它。我们有3张桌子:

  • 项目-项目表,其中每个项目都有相关的对象id、长度、难度等级、等级、平均等级和状态
  • 列表-列表表,基本上是用户创建的项目列表
  • 列表项-包含两列的表:列表项id、项目项id 我们一直在使用以下查询来显示一个简单的HTML表,该表显示每个列表以及与该列表相关的许多属性,包括所包含列表项的属性平均值:

    select object_id, user_id, slug, title, description, items, 
           city, state, country, created, updated,
           (select AVG(rating) from items
              where object_id IN 
                  (select object_id from list_items where list_id=lists.object_id) 
                AND status="A"
           ) as 'avg_rating',
           (select AVG(avg_rating) from items
              where object_id IN 
                  (select object_id from list_items where list_id=lists.object_id) 
                AND status="A"
           ) as 'avg_avg_rating',
           (select AVG(length) from items 
              where object_id IN 
                  (select object_id from list_items where list_id=lists.object_id) 
                AND status="A"
           ) as 'avg_length',
           (select AVG(difficulty_rating) from items 
              where object_id IN
                  (select object_id from list_items where list_id=lists.object_id) 
                AND status="A"
           ) as 'avg_difficulty' 
        from lists
        where user_id=$user_id AND status="A" 
        order by $orderby LIMIT $start,$step
    
    我们之所以没有在一次查询中对此进行分解,以获得所有列表和后续查找,从而得出每个列表的平均值,是因为我们希望用户能够对平均值列进行排序(即“按平均值排序”)


    希望我的解释有道理。必须有一种更有效的方法来做到这一点,我希望MySQL的专家能为我指明正确的方向。谢谢

    这是一个该死的问题。。。您可能应该编辑您的问题并更改查询,使其更具可读性,尽管由于其复杂性,我不确定这是否可行

    无论如何,这里的简单答案是将数据库反规范化一点,并将列表表本身的所有平均值缓存在索引的十进制列中。所有这些子查询都在折磨你


    最困难的部分,你需要弄清楚的是如何保持这些平均值的更新。通常一种简单的方法是将所有项目的计数和所有这些值的总和存储在两个单独的字段中。任何时候执行一个操作,都要将计数增加1,将总和增加任意值。然后更新表avg\u field=sum\u field/count\u field。

    这是一个地狱般的查询。。。您可能应该编辑您的问题并更改查询,使其更具可读性,尽管由于其复杂性,我不确定这是否可行

    无论如何,这里的简单答案是将数据库反规范化一点,并将列表表本身的所有平均值缓存在索引的十进制列中。所有这些子查询都在折磨你


    最困难的部分,你需要弄清楚的是如何保持这些平均值的更新。通常一种简单的方法是将所有项目的计数和所有这些值的总和存储在两个单独的字段中。任何时候执行一个操作,都要将计数增加1,将总和增加任意值。然后更新表avg\u field=sum\u field/count\u field。

    这里介绍如何找到瓶颈:

    在选择之前添加关键字EXPLAIN。这将导致发动机输出执行选择的方式


    要了解有关使用此方法进行查询优化的更多信息,请参阅:

    此处如何查找瓶颈:

    在选择之前添加关键字EXPLAIN。这将导致发动机输出执行选择的方式


    要了解有关使用此方法进行查询优化的更多信息,请参阅:

    需要考虑的几点:

  • 确保所有联接都在两侧建立索引。例如,您可以在多个位置连接列表项。列表id=列表。对象id列表id对象id都应该有索引

  • 你有没有研究过平均值的变化是什么?让工作线程(或cronjob)周期性地计算平均值,而不是每次运行此查询时都在RDBMS上加载负载,这可能会给您带来好处。当然,你需要将平均值存储在一个单独的表格中

  • 另外,您是使用状态作为枚举还是varchar?枚举的基数要低得多;如果对于<强>状态< /强>列的值范围有限,请考虑切换到该类型。


  • -aj

    需要考虑以下几点:

  • 确保所有联接都在两侧建立索引。例如,您可以在多个位置连接列表项。列表id=列表。对象id列表id对象id都应该有索引

  • 你有没有研究过平均值的变化是什么?让工作线程(或cronjob)周期性地计算平均值,而不是每次运行此查询时都在RDBMS上加载负载,这可能会给您带来好处。当然,你需要将平均值存储在一个单独的表格中

  • 另外,您是使用状态作为枚举还是varchar?枚举的基数要低得多;如果对于<强>状态< /强>列的值范围有限,请考虑切换到该类型。


  • -aj

    看起来您可以用连接替换所有子查询:

    SELECT     l.object_id,
               l.user_id,
               <other columns from lists>
               AVG(i.rating) as avgrating,
               AVG(i.avg_rating) as avgavgrating,
               <other averages>
    FROM       lists l
    LEFT JOIN  list_items li 
    ON         li.list_id = l.object_id
    LEFT JOIN  items i 
    ON         i.object_id = li.object_id
               AND i.status = 'A'
    WHERE      l.user_id = $user_id AND l.status = 'A' 
    GROUP BY   l.object_id, l.user_id, <other columns from lists>
    
    选择l.object\u id,
    l、 用户id,
    平均值(即额定值)为平均值,
    AVG(即AVG_额定值)作为AVGAV光栅,
    从清单l
    左联接列表\u项li
    在li.list\u id=l.object\u id上
    左连接项目i
    在i.object\u id=li.object\u id上
    而i.status='A'
    其中l.user\u id=$user\u id和l.status='A'
    按l.object\u id、l.user\u id分组,
    

    这将为数据库引擎节省大量工作。

    看起来您可以用连接替换所有子查询:

    SELECT     l.object_id,
               l.user_id,
               <other columns from lists>
               AVG(i.rating) as avgrating,
               AVG(i.avg_rating) as avgavgrating,
               <other averages>
    FROM       lists l
    LEFT JOIN  list_items li 
    ON         li.list_id = l.object_id
    LEFT JOIN  items i 
    ON         i.object_id = li.object_id
               AND i.status = 'A'
    WHERE      l.user_id = $user_id AND l.status = 'A' 
    GROUP BY   l.object_id, l.user_id, <other columns from lists>
    
    选择l.object\u id,
    l、 用户id,
    平均值(即额定值)为平均值,
    AVG(即AVG_额定值)作为AVGAV光栅,
    从清单l
    左联接列表\u项li
    在li.list\u id=l.object\u id上
    左连接项目i
    在i.object\u id=li.object\u id上
    而i.status='A'
    其中l.user\u id=$user\u id和l.status='A'
    按l.object\u id、l.user\u id分组,
    

    这将为数据库引擎节省大量工作。

    除了索引之外,即使是粗略的分析也表明,您的查询包含许多DBMS优化器无法发现的冗余(SQL是一种冗余语言)
      select object_id, user_id, slug, title, description, items, city, state, country, created, updated,
             (select AVG(rating)            from items where object_id IN LI AND status="A") as 'avg_rating',
             (select AVG(avg_rating)        from items where object_id IN LI AND status="A") as 'avg_avg_rating',
             (select AVG(length)            from items where object_id IN LI AND status="A") as 'avg_length',
             (select AVG(difficulty_rating) from items where object_id IN LI AND status="A") as 'avg_difficulty'
        from lists
       where user_id=$user_id AND status="A"
    order by $orderby
       LIMIT $start, $step