我的子查询添加20秒的执行时间。 我如何加快速度?

我有一张发送短信的表格,它必须加入到发送收据表中以获取最新的消息状态。

有997,148条发送短信。

我正在运行此查询:

SELECT
    m.id,
    m.user_id,
    m.api_key,
    m.to,
    m.message,
    m.sender_id,
    m.route,
    m.submission_reference,
    m.unique_submission_reference,
    m.reason_code,
    m.timestamp,
    d.id AS dlrid,
    d.dlr_status
FROM
    messages_sent m
LEFT JOIN
    delivery_receipts d
ON
    d.message_id = m.id
AND
    d.id = (SELECT MAX(id) FROM delivery_receipts WHERE message_id = m.id)

其中返回997,148个结果,包括每条消息的最新状态。

这需要22.8688秒才能执行。

这是messages_sent的SQL:

CREATE TABLE IF NOT EXISTS `messages_sent` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`api_key` varchar(40) NOT NULL,
`to` varchar(15) NOT NULL,
`message` text NOT NULL,
`type` enum('sms','mms') NOT NULL DEFAULT 'sms',
`sender_id` varchar(15) NOT NULL,
`route` tinyint(1) unsigned NOT NULL,
`supplier` tinyint(1) unsigned NOT NULL,
`submission_reference` varchar(40) NOT NULL,
`unique_submission_reference` varchar(40) NOT NULL,
`reason_code` tinyint(1) unsigned NOT NULL,
`reason` text NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `api_key` (`api_key`),
KEY `sender_id` (`sender_id`),
KEY `route` (`route`),
KEY `submission_reference` (`submission_reference`),
KEY `reason_code` (`reason_code`),
KEY `timestamp` (`timestamp`),
KEY `to` (`to`),
KEY `unique_submission_reference` (`unique_submission_reference`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1000342 ;

对于delivery_receipts

CREATE TABLE IF NOT EXISTS `delivery_receipts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`message_id` int(10) unsigned NOT NULL,
`dlr_id` bigint(20) unsigned NOT NULL,
`dlr_status` tinyint(2) unsigned NOT NULL,
`dlr_substatus` tinyint(2) unsigned NOT NULL,
`dlr_final` tinyint(1) unsigned NOT NULL,
`dlr_refid` varchar(40) NOT NULL,
`dlr_phone` varchar(12) NOT NULL,
`dlr_charge` tinyint(3) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `message_id` (`message_id`),
KEY `dlr_status` (`dlr_status`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1468592 ;

下面是一个EXPLAIN的SQL:


有一个窍门。

取而代之的是用子查询选取MAX元素,您可以像这样两次加入有趣的表格:

SELECT
    m.id,
    m.user_id,
    m.api_key,
    m.to,
    m.message,
    m.sender_id,
    m.route,
    m.submission_reference,
    m.unique_submission_reference,
    m.reason_code,
    m.timestamp,
    d.id AS dlrid,
    d.dlr_status
FROM
    messages_sent m
JOIN
    delivery_receipts d
ON
    d.message_id = m.id
LEFT JOIN
    delivery_receipts d1
ON
    d1.message_id = m.id
    AND
    d1.id > d.id
WHERE
    d1.id IS NULL

第二次表加入它有你想选择MAX的字段应该高于第一个表中的附加条件。 并过滤除了那些没有其他行更高的行之外的所有行。

这样只剩下最大行数。

我将你的LEFT JOIN改为JOIN。 我不确定你是否需要LEFT JOIN。 即使你仍然应该工作。

令人惊讶的是,这比子查询快得多。

您可能想尝试其他同一想法的变体:

SELECT
    m.id,
    m.user_id,
    m.api_key,
    m.to,
    m.message,
    m.sender_id,
    m.route,
    m.submission_reference,
    m.unique_submission_reference,
    m.reason_code,
    m.timestamp,
    d.id AS dlrid,
    d.dlr_status
FROM
    messages_sent m
JOIN
(
SELECT d0.* FROM
    delivery_receipts d0
LEFT JOIN
    delivery_receipts d1
ON
    d1.message_id = d0.message_id
    AND
    d1.id > d0.id
WHERE
    d1.id IS NULL
) d
ON
    d.message_id = m.id

确保你有表列中的字段message_id和id的多列索引delivery_receipts可能是这样的:

ALTER TABLE  `delivery_receipts` 
ADD INDEX  `idx` (  `message_id` ,  `id` );

减速似乎很大,但是如果你需要坚持这个查询,恐怕没有太大的改进空间。

一个问题是d.dlr_status的报告。 尝试从报告列的列表中删除这个,并查看查询时间是否提高。

如果所有内容都存储在messages_sent您将获得最佳性能。 这不会是NF,但如果你需要表现,这是一个选择。 为了实现这一点,在messages_sent创建iddlr_status列,并将适当的INSERTUPDATEDELETE触发器添加到delivery_receipts 。 触发器会更新messages_sent的相应列 - 这是查询时间和更新时间之间的折衷。


您可以在delivery_receipts表中“缓存”部分计算,只需将is_last_status布尔值添加到delivery_receipts表。 使用简单的触发器,您可以更改每次插入新收据的值。

比选择查询变得更简单:

SELECT
  m.id,
  m.user_id,
  m.api_key,
  m.to,
  m.message,
  m.sender_id,
  m.route,
  m.submission_reference,
  m.unique_submission_reference,
  m.reason_code,
  m.timestamp,
  d.id AS dlrid,
  d.dlr_status
FROM
  messages_sent m
LEFT JOIN
  delivery_receipts d
ON
  d.message_id = m.id
WHERE
  d.is_last_status = true

如果mysql支持部分索引,查询可能会加速更多。

链接地址: http://www.djcxy.com/p/55051.html

上一篇: My sub query is adding 20 seconds to the execution time. How can I speed it up?

下一篇: Why does MSDN recommend including object sender in delegate declarations?