【问题标题】:Optimize Query with huge NOT IN Statement使用巨大的 NOT IN 语句优化查询
【发布时间】:2012-05-09 18:30:22
【问题描述】:

我正在尝试查找仅在某个时间戳之前存在的源站点。这个查询对于这项工作来说似乎很糟糕。知道如何优化或可能改进的索引吗?

select distinct sourcesite 
  from contentmeta 
  where timestamp <= '2011-03-15'
  and sourcesite not in (
    select distinct sourcesite 
      from contentmeta 
      where timestamp>'2011-03-15'
  );

sourcesite和timestamp都有索引,但是查询还是需要很长时间

mysql> EXPLAIN select distinct sourcesite from contentmeta where timestamp <= '2011-03-15' and sourcesite not in (select distinct sourcesite from contentmeta where timestamp>'2011-03-15');
+----+--------------------+-------------+----------------+---------------+----------+---------+------+--------+-------------------------------------------------+
| id | select_type        | table       | type           | possible_keys | key      | key_len | ref  | rows   | Extra                                           |
+----+--------------------+-------------+----------------+---------------+----------+---------+------+--------+-------------------------------------------------+
|  1 | PRIMARY            | contentmeta | index          | NULL          | sitetime | 14      | NULL | 725697 | Using where; Using index                        |
|  2 | DEPENDENT SUBQUERY | contentmeta | index_subquery | sitetime      | sitetime | 5       | func |     48 | Using index; Using where; Full scan on NULL key |
+----+--------------------+-------------+----------------+---------------+----------+---------+------+--------+-------------------------------------------------+

【问题讨论】:

    标签: mysql query-optimization


    【解决方案1】:

    子查询不需要 DISTINCT,外部查询上的 WHERE 子句也不需要,因为您已经通过 NOT IN 进行过滤。

    试试:

    select distinct sourcesite
    from contentmeta
    where sourcesite not in (
        select sourcesite
        from contentmeta
        where timestamp > '2011-03-15'
    );
    

    【讨论】:

    • 这可以在没有“Not IN”的情况下完成。因为它是最昂贵的mysql操作之一
    • @MoyedAnsari:NOT IN 不是“最昂贵”的操作。这个问题需要NOT INNOT EXISTS 子查询或LEFT JOIN - IS NULL 查询。
    【解决方案2】:

    这应该可行:

    SELECT DISTINCT c1.sourcesite
    FROM contentmeta c1
    LEFT JOIN contentmeta c2
      ON c2.sourcesite = c1.sourcesite
      AND c2.timestamp > '2011-03-15'
    WHERE c1.timestamp <= '2011-03-15'
      AND c2.sourcesite IS NULL
    

    为获得最佳性能,请在 contentmeta 上设置一个多列索引(sourcesitetimestamp)。

    通常,连接比子查询执行得更好,因为派生表不能利用索引。

    【讨论】:

      【解决方案3】:

      我发现“不在”在许多数据库中都不能很好地优化。请改用left outer join

      select distinct sourcesite 
      from contentmeta cm 
      left outer join
      (
         select distinct sourcesite
         from contentmeta
         where timestamp>'2011-03-15'
      ) t
        on cm.sourcesite = t.sourcesite
      where timestamp <= '2011-03-15' and t.sourcesite is null
      

      这假定sourcesite 永远不会为空。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多