簡體   English   中英

我的子查詢為執行時間增加了20秒。 我怎樣才能加快速度?

[英]My sub query is adding 20 seconds to the execution time. How can I speed it up?

我有一個已發送短信的表格,該短信必須加入到送達回執表中才能獲得最新的短信狀態。

有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

在此輸入圖像描述

有一個技巧。

而選擇帶有子查詢的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` );

放緩似乎很大,但如果您需要堅持這個查詢,我擔心沒有太大的改進空間。

一個問題是報告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支持部分索引,那么查詢可能會更快。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM