4

我正在尝试从多个表中提取数据,当我使用 ORDER BY 日期时间字段时,它会在至少 10 秒后返回结果,但是如果我在没有 ORDER BY 的情况下执行相同的查询,那么它会在 2 秒内返回结果。

这是我当前的查询

SELECT
ph.call_subject AS callSubject,
ac.account_name AS accountName,
DATE_FORMAT(ph.trigger_on, "%c/%e/%Y %h:%i %p") AS triggerOn,
ind.name AS industry,
cc.call_code_name AS callCode
FROM phone_calls AS ph
INNER JOIN accounts AS ac ON ph.account_id = ac.account_id
INNER JOIN industries AS ind ON ind.industry_id = ac.industry_id
INNER JOIN call_codes AS cc ON ph.call_code_id = cc.call_code_id
WHERE ac.status = 1 AND ph.status = 1 AND ph.owner_id = 1 AND ac.do_not_call = 0
AND ph.trigger_on BETWEEN '2012-11-19 00:00:00' AND '2013-03-19 23:59:59'
ORDER BY ph.trigger_on ASC LIMIT 0,1000

以下字段均为 INT(11) UNSIGNED 类型

ph.account_id
ac.account_id
ind.industry_id
ac.industry_id
ph.call_code_id
cc.call_code_id
ph.owner_id

以下字段均为 tinyint(1) 类型

ac.status 
ph.status
ac.do_not_call

此字段是日期时间类型

ph.trigger_on

请注意,accounts 有 300K 条记录,phone_calls 有 500 万条记录。我该怎么做才能更快地执行此 ORDER BY?请注意,我的所有 where 子句字段、我的所有 ON 子句和 ph.trigger_on 都已编入索引。我使用的是 InnoDB 存储引擎而不是 MyIsam。

谢谢

4

5 回答 5

5

请试试这个:

  1. 在列上建立索引(phone_calls.trigger_on, phone_calls.status, phone_calls.owner_id)调用它pcto

  2. 将您的 FROM 子句更改为:

    FROM phone_calls AS ph FORCE INDEX (pcto)

这是理想。如果它不起作用,请添加评论,我将为您提供另一种保证有效的方法,并为您提供所需的性能改进。

请注意:在查询中的“每个”列上建立索引并不重要(而且确实没有好处)。MySQL 每个表只能使用一个索引(或者更准确地说,每个表别名)。您需要构建我们告诉您的索引。

于 2013-03-20T10:12:49.223 回答
3

如果您的 LIMIT 为 5 行,那么在没有顺序的情况下,查询可以获取它找到的与其他条件匹配的前 5 行。

如果您有一个 ORDER BY 子句,它必须查看与您的其他条件匹配的所有行并选择最低的 5 个。

于 2013-03-20T00:11:45.800 回答
0

当您在 (SELECT) 上执行 SELECT 时aka,它真的就像在临时表上工作。下面的示例在一个主大表上有几个连接。当 ORDER BY 在整个表查询上时,此解决方案将查询时间缩短到 0.2 秒,而 20 秒。

   SELECT * FROM (SELECT `cse_notes`.`notes_id`, `cse_notes`.`dateandtime`, 
    `cse_case`.`case_id`, `cse_case_notes`.`attribute`
    FROM  `cse_notes` 
    INNER JOIN  `cse_case_notes` 
    ON `cse_notes`.`notes_uuid` =  `cse_case_notes`.`notes_uuid`
    INNER JOIN `cse_case` 
    ON  `cse_case_notes`.`case_uuid` = `cse_case`.`case_uuid`
    WHERE `cse_notes`.`deleted` = 'N' AND `cse_case`.`case_id` = :case_id
    AND `cse_notes`.customer_id = :customer_id) notes
    ORDER BY `dateandtime` DESC

这是运行非常缓慢的错误查询。我认为这很好,我不知道必须在过滤开始之前对整个表进行排序。单独索引没有帮助。

    SELECT `cse_notes`.`notes_id`, `cse_notes`.`dateandtime`,
    `cse_case`.`case_id`, `cse_case_notes`.`attribute`    
    FROM  `cse_notes`     
    INNER JOIN  `cse_case_notes` ON `cse_notes`.`notes_uuid` =  `cse_case_notes`.`notes_uuid`    
    INNER JOIN `cse_case` ON  `cse_case_notes`.`case_uuid` = `cse_case`.`case_uuid`    
    WHERE `cse_notes`.`deleted` = 'N' 
    AND `cse_case`.`case_id` = :case_id    
    AND `cse_notes`.customer_id = :customer_id    
    ORDER BY `cse_notes`.dateandtime DESC LIMIT 0, 1000
于 2015-03-07T03:25:29.800 回答
0

根据我的经验,从 SQL 查询中获得性能的最快方法是将其简化为多个步骤。利用临时表并减少每一步的连接和操作数量(消耗内存,获得速度)。请原谅我下面可能出现的语法错误,因为我已经很长时间没有使用 MySQL,但是您可以按如下方式重写您的查询:

CREATE TEMPORARY TABLE scratch1 AS (
    SELECT
            ph.call_subject AS callSubject,
            ac.account_name AS accountName,
            DATE_FORMAT(ph.trigger_on, "%c/%e/%Y %h:%i %p") AS triggerOn,
            ac.industry_id,
            ph.call_code_id
    FROM
            phone_calls AS ph
            INNER JOIN accounts AS ac ON ph.account_id = ac.account_id
    WHERE   
            ac.status = 1 AND ph.status = 1 AND ph.owner_id = 1 AND ac.do_not_call = 0
            AND ph.trigger_on BETWEEN '2012-11-19 00:00:00' AND '2013-03-19 23:59:59' )

ALTER TABLE scratch1 ADD industry VARCHAR(255)
ALTER TABLE scratch1 ADD callCode VARCHAR(255)

UPDATE scratch1 s JOIN industries ind ON ind.industry_id = s.industry_id
SET s.industry = ind.name

UPDATE scratch1 s JOIN call_codes cc ON cc.call_code_id = s.call_code_id
SET s.callCode = cc.call_code_name

CREATE TEMPORARY TABLE scratch2 AS (
    SELECT * FROM scratch1 ORDER BY triggerOn ASC )

SELECT * FROM scratch2 LIMIT 0, 1000
于 2013-03-20T00:43:44.300 回答
0

这是为了详细说明Ersun的解决方案/评论。

如果没有order by,SQL 将评估查询。在这种情况下,它是一堆连接。很可能,您在连接字段上有索引。因此,查询通过从 读取记录phone_calls、查找数据、检查过滤条件并返回它来进行。然后它进入记录等等。总的来说,它可能会读取几千或几万条记录。

使用order by,SQL 必须评估查询中的所有记录。它必须读取所有电话,因为最后一个电话可能具有最小值。然后它进行排序并返回正确的记录。

您可以通过建立索引phone_calls(status, owner_id, trigger_on)来满足该where子句来加快查询速度。

于 2013-03-20T00:51:32.487 回答