我正在尝试从多个表中提取数据,当我使用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
请注意,帐户有300K记录,phone_calls有500万条记录。 我该怎么做才能更快地执行此ORDER BY?请注意,我的所有where子句字段,所有我的ON子句和ph.trigger_on都被编入索引。我正在使用InnoDB存储引擎而不是MyIsam。
由于
答案 0 :(得分:3)
如果你的LIMIT为5行,那么在没有订单的情况下,查询可以获取它找到的符合其他条件的前5行。
如果你有一个ORDER BY子句,它必须查看与你的其他条件匹配的所有行,然后选择最低的5行。
答案 1 :(得分:2)
请试试这个:
将FROM子句更改为:
FROM phone_calls AS ph FORCE INDEX pcto
这是理想的选择。如果它不起作用,那么添加一个注释,我会给你另一种方法,它可以保证工作,并为你提供所需的性能改进。
请注意:在查询中的“每个”列上构建索引并不重要(事实上并不好)。 MySQL每个表只能使用一个索引(或者每个表别名更正确)。您需要构建我们告诉您的索引。
答案 2 :(得分: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
答案 3 :(得分:0)
这是详细说明Ersun的解决方案/评论。
如果没有order by
,SQL会对查询进行评估。在这种情况下,它是一堆连接。很可能,您在连接字段上有索引。因此,通过从phone_calls
读取记录,查找数据,检查过滤条件并返回它来继续查询。然后它转到记录等等。总的来说,它可能会读取数千或数万条记录。
使用order by
,SQL必须评估所有查询中的记录。它必须读取所有电话,因为最后一个可能具有最小值。然后它进行排序并返回正确的记录。
您可以通过在phone_calls(status, owner_id, trigger_on)
上设置索引来满足where
子句来加快查询速度。
答案 4 :(得分:0)
当您在(SELECT)aka
上执行SELECT时,它真的像在临时表上工作一样。下面的示例在一个主要大表上有一些连接。当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