我有一张已发送短信的表格,该短信必须加入送货收据表才能获得最新的短信状态。
发送了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 ;
这是SQL的EXPLAIN
:
答案 0 :(得分:4)
有一招。
相反,选择带有子查询的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
确保您在表delivery_receipts中有字段message_id和id的多列索引可能是这样的:
ALTER TABLE `delivery_receipts`
ADD INDEX `idx` ( `message_id` , `id` );
答案 1 :(得分:0)
放缓似乎很大,但如果你需要坚持这个问题,我担心没有太大的改进空间。
一个问题是报告d.dlr_status
。尝试从报告列的列表中删除它,并查看查询时间是否有所改善。
如果所有内容都存储在messages_sent
中,您将获得最佳性能。这不再是NF,但如果您需要性能,它是一个选项。要实现此目的,请在id
中创建dlr_status
和messages_sent
列,并将相应的INSERT
,UPDATE
和DELETE
个触发器添加到delivery_receipts
。触发器将更新messages_sent
中的相应列 - 这是查询时间和更新时间之间的权衡。
答案 2 :(得分:0)
您可以在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支持部分索引,那么查询可能会加快速度。