【问题标题】:MySQL Subquery Optimization - where not in(subquery)MySQL 子查询优化 - where not in(子查询)
【发布时间】:2013-04-26 22:24:07
【问题描述】:

我正在尝试优化以下查询。我认为外部连接可以解决问题,但我不知道如何将它组合在一起。

// ---------------------------------
// Simplified representation of data
// ---------------------------------
create table views (
   user_id,
   article_id
)

create table article_attributes (
   article_id,
   article_attribute_id
)

create table articles (
   id,
   title,
   date
)

Views 表有数千万条记录。 文章表有几十万。

我正在尝试匹配具有与其相关联的特定属性且尚未被用户查看的所有文章。

我已经尝试过,但不能很好地扩展:

select a.title, a.sid as article_id, a.total_views as times_read, a.date 
from articles a 
join article_attributes att on att.article_id = a.sid 

where a.sid not in( 
   select v.article_id 
   from views v
   join article_attributes att on att.article_id = v.article_id 
   where user_id = 132385 
   and att.article_attribute_id = 10
   group by v.article_id 
) 
and att.article_attribute_id = 10 
and a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 day) 
order by total_views desc 
limit 5

这很好用,但用户查看的文章越多,速度就会明显变慢。任何想法或建议将不胜感激。

【问题讨论】:

    标签: php mysql query-optimization


    【解决方案1】:
    SELECT a.title, a.sid AS article_id, a.total_views AS times_read, a.date
    FROM articles a 
        JOIN article_attributes att 
            ON a.id = att.article_id AND att.article_attribute_id = 10 
        LEFT JOIN views v 
            ON a.id = v.article_id AND v.user_id = 132385  
    WHERE v.user_id IS NULL
    
    1. 第一个连接只获取具有给定属性的文章。
    2. 第二个连接采用第一个连接的结果并返回带有 user_id 的行以及第一个结果中没有 user_id 的所有剩余行。(基本上所有具有属性 132385 且 user_id 为 10 或 NULL 的文章)
    3. 那么我们想要的只是 user_id 为 NULL 的结果

    尽量避免嵌套查询,让引擎完成它的工作。请注意,您可以在最后标记其他过滤器(DATE、ORDER BY)。

    【讨论】:

      【解决方案2】:

      试试这个查询

       select a.title, a.sid as article_id, a.total_views as times_read, a.date 
       from 
          articles a 
       left join 
          views v
       on 
          a.sid = v.article_id AND v.article_id is null
       join 
          article_attributes att 
       on 
          att.article_id = v.article_id AND v.user_id = 132385 AND att.article_attribute_id = 10
       where  
           a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 day) 
       order by 
          total_views desc limit 5
      

      articles(total_views, sid, date)创建必要的索引

      view(article_id, user_id)

      article_attributes(article_id, article_attribute_id)

      希望这会有所帮助。

      【讨论】:

        【解决方案3】:

        我建议不要将子查询用作where 条件,而是在连接中使用它。另外,我建议你不要在子查询中使用group by,而是select distinct

        select
            a.title, a.sid as article_id, a.total_views as times_read, a.date 
        from
            (articles a
            inner join article_attributes att on a.sid = att.article_id)
            left join (
                select distinct
                    v.article_id 
                from views v
                    inner join article_attributes att on v.article_id = att.article_id
                where
                    user_id = 132385
                    and att.article_atribute_id = 10
                ) as b on a.sid = b.article_id
        where
            b.article_id is null
            and att.article_attribute_id = 10 
            and a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 day)
        

        希望对你有帮助

        【讨论】:

          【解决方案4】:

          EXISTS 应该比IN 工作得更好:

          SELECT a.title,
                 a.sid AS article_id,
                 a.total_views AS times_read,
                 a.date
          FROM articles a
          JOIN article_attributes att ON att.article_id = a.sid
          WHERE NOT EXISTS (SELECT 0
                            FROM views v
                            JOIN article_attributes att ON att.article_id = v.article_id
                            WHERE user_id = 132385
                            AND att.article_attribute_id = 10
                            AND v.article_id = a.sid )
           AND att.article_attribute_id = 10
           AND a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)
           ORDER BY total_views DESC LIMIT 5
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-03-03
            • 1970-01-01
            • 1970-01-01
            • 2015-09-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多