【问题标题】:Query Optimization using WHERE IN使用 WHERE IN 进行查询优化
【发布时间】:2010-06-04 15:37:56
【问题描述】:

我想知道是否有人可以解释 IN 是如何计算的?好吧,最终我试图找出为什么这个查询很慢以及如何优化它。我等了 3 多分钟,当我取消查询时,它只返回了 1000 行,这似乎不需要那么长时间。

SELECT t2.* 
FROM report_tables.roc_test_results as t2 
WHERE t2.job IN (SELECT DISTINCT(t1.job) 
                   FROM report_tables.roc_test_results as t1 
                  WHERE t1.operation = 'TEST' 
                    AND result = 'Passed' 
                    AND STR_TO_DATE(t1.date_created,'%d-%M-%Y') BETWEEN '2009-10-01' 
                                                                    AND '2009-10-31')

我不确定总查询应该返回什么,如果我不得不猜测我会说大约 2000 条记录,则子查询返回 332(非 Distinct 时为 336)。

谁能给我一些关于如何优化这个查询的建议?另外,我想知道,子查询是每次计算还是只计算一次并存储它?

按照要求,DESC 的结果...(顺便说一句,请不要笑,我是自学成才的,所以我确信这张表设计得很糟糕。)

Field                     Type               Null     Key    Default    Extra
------                      -----              -----     ---    -------    -----
operation                 varchar(10)         NO   
tester                 varchar(25)            NO   
result                 varchar(45)            NO   
fail_mode              varchar(45)         NO   
primary_failure        varchar(25)           NO   
ref_des                varchar(45)           NO   
rf_hours               varchar(15)          NO   
ac_hours               varchar(15)          NO   
comments               text              NO   
job                    varchar(15)           NO   
rma                    bigint(20) unsigned    NO   
item                   varchar(45)          NO   
item_description       text                  NO   
serial                 varchar(25)            NO   
created_by             varchar(25)            NO   
collection             bigint(20) unsigned    NO    PRI  
date_created           varchar(15)          NO   

【问题讨论】:

    标签: mysql query-optimization where-in


    【解决方案1】:

    date_created 数据类型需要更改为 DATETIME,然后才值得在列上定义索引。原因是,如果您现在将数据类型从字符串更改为 DATETIME,索引将毫无价值。

    您提到您正在使用LOAD DATA INFILE,并且源文件包含 DD-MON-YY 格式的日期。 MySQL will implicitly convert strings into DATETIME if the YY-MM-DD format is used,因此如果您可以在使用 LOAD DATA INFILE 之前在源文件中更正此问题,那么其余的应该就位了。

    之后,covering index 使用:

    • 工作
    • 操作
    • 结果
    • 创建日期

    ...会是个好主意。

    【讨论】:

    • 正确,我现在知道我可以在 LOAD DATA INFILE 期间使用 @ 和 SET 更改格式,但是当我添加原始数据时我没有。我想因为我已经有依赖于它的格式的应用程序,所以我有点卡住了。我要么必须保持原样并永远等待结果,同时还要有一个可怕的索引表,要么我必须通过我的应用程序并通过删除 STR_TO_DATE 来更改所有查询,因为它不再需要(我可能会做后者)。这个覆盖索引看起来很有趣,感谢您指出这一点。
    【解决方案2】:

    首先,您不需要子查询中的 distinct,因为 IN 无论如何都会消除重复项 您是否需要 WHERE 子句中的函数调用,并且您在 date_created 列上有索引吗?

    当你改变时会发生什么

    WHERE STR_TO_DATE(t1.date_created,'%d-%M-%Y') 
    BETWEEN '2009-10-01' AND '2009-10-31')
    

    WHERE 1.date_created >= '2009-10-01' 
    AND 1.date_created < '2010-01-01'
    

    如果在列上使用函数,有时不会使用索引

    【讨论】:

    • 是的,我需要 where 子句,基本上它会提取该期间发货的所有 RMA,然后我会收集与发货 RMA 列表相关的所有历史数据。我确实有一个索引,但它基本上是一个自动递增的索引,我将如何合并它。对不起,我是自学的,所以我不太了解细节。我阅读了有关索引的手册,但仍然没有看到如何合并与数据无关的索引。
    • 我问你是否需要函数而不是 WHERE 子句。我拥有的 WHERE 子句应该和你的一样,但应该运行得更快
    • STR_TO_DATE 将字符串转换为日期,暗示 date_created 是 VARCHAR/TEXT/etc 数据类型,因此该函数是必需的。
    • 哦,对不起,是的,我确实需要它。我不知道在使用 LOAD DATA INFILE 时如何使用 SET,所以日期看起来像 09 年 10 月 1 日。除非有办法在没有该功能的情况下仍然使用该格式。
    • 另外,可能值得一提的是,我有使用这些数据的应用程序,所以我无法真正更改它,因为它会弄乱假设它是 D-Mon-Year 格式的应用程序
    【解决方案3】:

    我的建议是将 IN 替换为 JOIN,然后考虑在某些列上添加索引,例如作业,可能还有操作和/或结果。您应该阅读 MySQL 手册中的索引,以及使用 EXPLAIN 优化查询:

    http://dev.mysql.com/doc/refman/5.1/en/indexes.html

    http://dev.mysql.com/doc/refman/5.1/en/using-explain.html

    以下是将 IN 转换为 JOIN 的示例:

    SELECT distinct t2.* 
    FROM roc_test_results as t2
    inner join roc_test_results as t1 on t1.job = t2.job
    WHERE t1.operation = 'TEST' 
    AND t1.result = 'Passed' 
    AND STR_TO_DATE(t1.date_created,'%d-%M-%Y') BETWEEN '2009-10-01' AND '2009-10-31';
    

    【讨论】:

    • STR_TO_DATE 将字符串转换为日期,这意味着 date_created 是 VARCHAR/TEXT/etc 数据类型,因此该函数是必需的。由于转换为不同的数据类型,因此不会使用列上的索引。
    • 如果这听起来像一个愚蠢的问题,请原谅我,但是,如果日期不是索引的最左边的前缀并且我不包括主索引,那将有什么帮助。另外,我不确定您的第二段是否会返回我正在寻找的内容。我首先收集在日期范围内通过 TEST 的 RMA 列表,然后我需要任何日期范围内与在日期范围内通过 TEST 的 RMA 相关的所有行。
    • @OMG 小马:你是对的。我没有仔细看这个函数。我只是假设它将 DATETIME 转换为 DATE。所以索引无济于事。
    • @Geoff:索引无济于事。我没有仔细看你的询问。见我之前的评论。至于我重写查询的方式,我再次没有仔细看原文,所以你是对的,它不会给你你想要的。我会用一个连接重写它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-02
    • 1970-01-01
    • 1970-01-01
    • 2019-12-15
    • 1970-01-01
    • 2020-07-29
    • 2015-02-25
    相关资源
    最近更新 更多