SubQuery执行时间:localhost = 2秒/ server = 98+秒

时间:2015-03-12 21:06:41

标签: mysql subquery

请问有关子查询的帮助吗?

当我在本地XAMP mySQL数据库上运行查询时,查询需要2秒钟才能完成。然而,在我的网络服务器上使用相同的数据库,相同的查询需要98秒加上返回相同的结果。

当我说相同时,记录会从HeidiSQL导出和插入,所以我确定数据集没问题。数据库DDL也是从HeidiSQL创建的,但我猜我可能错过了创建数据库精确副本的一些关键步骤。

我还使用Heidi的导出功能创建了fiddle。虽然我应该指出查询在小提琴上的执行速度比在现实生活中快得多。

我正在执行的查询是......

SELECT  d.dayID, d.dayDate, d.item, w.Idx, w.word, w.wordID, w.asize, w.span
FROM words w
    INNER JOIN days d ON w.dayID = d.dayID
    WHERE w.word IN (
            SELECT w1.word
            FROM words w1
                INNER JOIN days d1 ON w1.dayID = d1.dayID
            WHERE d1.dayDate = '2012-02-27'
                AND d1.Item = 'a'
                AND w1.span  = 24
                AND w1.asize = 6
            )
    AND w.span = 24
    AND w.asize = 6
GROUP BY d.dayDate, d.item 
Order by d.dayDate, w.asize DESC, w.Idx;

它的目的是从days表中返回Days和Items的列表,其中word表中有重复的单词。

上面的查询将返回与此类似的结果......

dayID   dayDate         item    Idx     word        wordID      asize   span
1974    2012-11-22      B       3       item b      1367339     6       24
4370    2015-03-10      B       1       item b      3024989     6       24

使用phpMyAdmin,我在单词字段中添加了一个索引,这使得时间从98秒减少到46秒。但是,当然,46秒也太长了?

其他要注意的一点是,现实中的单词表包含约300万条记录。其他查询(非子查询)在眨眼之间运行。我想我只是在子查询中吮吸。

请问有人可以指出我正确的方向,以确定在服务器上执行查询需要这么长时间的原因吗?

1 个答案:

答案 0 :(得分:1)

作为一般规则,在处理复杂或大的子查询时,应避免使用IN。这是因为IN条件必须为数据源中的每一行评估一次。因此,如果您的数据源有1000行且IN条件有1000个元素,那么执行将是这样的:

  • 对于第1行,运行子查询并检查该值是否在子查询结果
  • 对于第2行,运行子查询并检查该值是否在子查询结果
  • ...

(不用说,如果子查询很复杂,那将是一个巨大的性能损失)

所以,你可以采取一些措施来加快速度:

  1. 请使用IN
  2. ,而不是使用JOIN
  3. 不使用子查询,而是创建临时表,添加适当的索引,然后使用JOIN
  4. 我将处理选项2.如果需要,可以使用完整子查询替换临时表。

    所以,让我们创建一个临时表:

    drop table if exists temp_words;
    create temporary table temp_words
        SELECT w1.word
        FROM words w1
            INNER JOIN days d1 ON w1.dayID = d1.dayID
        WHERE d1.dayDate = '2012-02-27'
          AND d1.Item = 'a'
          AND w1.span  = 24
          AND w1.asize = 6;
    alter table temp_words
        add index w(word);
    

    现在,不要使用IN,而是使用JOIN

    SELECT  d.dayID, d.dayDate, d.item, w.Idx, w.word, w.wordID, w.asize, w.span
    FROM words w
        INNER JOIN days d 
                ON w.dayID = d.dayID
        INNER JOIN temp_words as w1 -- Replace 'temp_words' with your subquery 
                                    -- if you don't want to use a temp table
                ON w.word = w1.word
    WHERE w.span = 24
      AND w.asize = 6
    GROUP BY d.dayDate, d.item 
    Order by d.dayDate, w.asize DESC, w.Idx;
    

    我认为您使用JOIN代替IN会发现性能大幅提升。

    关于临时表的必须了解的事情:

    1. 它们的行为与普通表一样,因此您可以像使用任何其他表一样使用它们:您可以插入,更新和删除行,您可以添加索引或以您想要的任何方式更改它们(或需要如果它们不再有用,你可以放弃它们。
    2. 它们仅对创建它们的连接可见。这意味着两个连接可以创建具有相同名称的临时表(但可能具有不同的结构),并且每个连接都可以使用它自己的" copy"。
    3. 一旦连接关闭或被杀死,它们就会被丢弃,因此如果您关闭或终止连接,则必须再次创建它们。