我正在尝试获取每个' telephone_number'的最大记录。 process_status =' 0'为此,我使用以下查询。
SELECT ID, CUSTID, telephone_number, TOTAL_USAGE, ACCOUNT_STATUS
FROM SPRINTABLE_DATA t
JOIN (SELECT MAX( id ) AS maxid FROM SPRINTABLE_DATA GROUP BY telephone_number)dt
ON t.id = dt.maxid WHERE process_status = '0'
AND RESET_FLAG = '0'
ORDER BY id DESC limit 0,700
上面的查询给了我想要的结果,但问题是这太慢了..
我的表有大约2000万行,这个查询有时需要大约15-20分钟。
可以采取哪些措施来改善这种情况?
这是结构:。
CREATE TABLE `SPRINTABLE_DATA` (
`ID` bigint(11) NOT NULL AUTO_INCREMENT,
`CUSTID` int(11) DEFAULT NULL,
`telephone_number` varchar(20) DEFAULT NULL,
`TOTAL_USAGE` int(11) DEFAULT NULL,
`PROCESS_STATUS` tinyint(4) DEFAULT '0',
`RESET_FLAG` tinyint(4) DEFAULT '0',
`RESET_REASON` varchar(10) DEFAULT NULL,
`PLAN_ID` varchar(20) DEFAULT NULL,
`ACCOUNT_STATUS` varchar(30) DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `telephone_number` (`telephone_number`),
KEY `CALL_CUST` (`CALL_START_TIME`,`CUSTID`),
KEY `telephone_number1` (`telephone_number `,`PROCESS_STATUS`,`SOC_ADDED`),
KEY `CURRENT_USAGE` (`CURRENT_USAGE`),
KEY `TOTAL_USAGE` (`TOTAL_USAGE`)
) ENGINE=InnoDB AUTO_INCREMENT=36392272 DEFAULT CHARSET=latin1
答案 0 :(得分:2)
您似乎正在寻找最近被叫的700个号码。 (如果不正确,请编辑您的问题。
您的查询遵循一个很好的做法,即在子查询中检索每个项目的最新日志行(在您的案例中为电话号码),如下所示。
SELECT MAX( id ) AS id
FROM SPRINTABLE_DATA
GROUP BY telephone_number
要优化此子查询的性能,您需要在两个字段上使用复合索引:(telephone_number, id)
,按此顺序。如果您没有该索引,请将其添加。这是为了允许所谓的松散索引扫描,这是一种非常有效的方法来满足查询。
其次,您正在寻找(我推测)您的一小部分数据。据推测,你有超过700个不同的telephone_number
值。这意味着您只使用ORDER BY对大量数据进行排序,以便使用LIMIT将其丢弃。所以,让我们做一个延迟连接,排序最少数量的列,然后检索你需要的所有信息。
以下是如何获取所需的700行的ID值
SELECT q.ID /* get our 700 records */
FROM SPRINTABLE_DATA q
JOIN (
SELECT MAX( id ) AS id
FROM SPRINTABLE_DATA
GROUP BY telephone_number
) r ON q.id = r.id
WHERE q.process_status = '0'
AND q.RESET_FLAG = '0'
ORDER BY q.ID DESC
LIMIT 0,700
这可以输出700个id号码。您需要对索引进行一些实验,以找出最有效的方法来优化它。
可能是一个索引 process_status, RESET_FLAG, id
会有所帮助。也可以改变索引中列的顺序,如下所示:
id, process_status, RESET_FLAG
尝试两者。
最后,我们将使用它作为子查询来执行连接(所谓的延迟连接)来获取实际的详细记录。这种技术不需要对所有数据进行排序。
SELECT t.ID, t.CUSTID, t.telephone_number, t.TOTAL_USAGE, t.ACCOUNT_STATUS
FROM SPRINTABLE_DATA t
JOIN (
SELECT q.ID /* get our 700 records */
FROM SPRINTABLE_DATA q
JOIN (
SELECT MAX( id ) AS id
FROM SPRINTABLE_DATA
GROUP BY telephone_number
) r ON q.id = r.id
WHERE q.process_status = '0'
AND q.RESET_FLAG = '0'
ORDER BY q.ID DESC
LIMIT 0,700
) s ON t.ID = s.ID
ORDER BY t.ID DESC
这将产生相同的结果,但会更快。
现在,最后,如果可以从符合条件的700个数字中选择最新的呼叫,您可以大大简化此查询。但是,这将以微妙的方式更改结果集。在这种情况下,您的呼叫选择子查询将如下所示:
SELECT MAX( id ) AS id /* 700 matching numbers */
FROM SPRINTABLE_DATA
WHERE process_status = '0'
AND reset_flag = '0'
GROUP BY telephone_number
ORDER BY ID desc
LIMIT 0,700
使用化合物覆盖索引
reset_flag, process_status, telephone_number, ID
这个查询会很快。在这种情况下,您的最终查询将是
SELECT t.ID, t.CUSTID, t.telephone_number, t.TOTAL_USAGE, t.ACCOUNT_STATUS
FROM SPRINTABLE_DATA t
JOIN (
SELECT MAX( id ) AS id /* 700 matching numbers */
FROM SPRINTABLE_DATA
WHERE process_status = '0'
AND reset_flag = '0'
GROUP BY telephone_number
ORDER BY ID desc
LIMIT 0,700
) s ON t.ID = s.ID
ORDER BY t.ID DESC
答案 1 :(得分:0)
对您的查询稍作修改
SELECT ID, CUSTID, telephone_number, TOTAL_USAGE, ACCOUNT_STATUS
FROM SPRINTABLE_DATA t
JOIN (SELECT telephone_number,MAX( id ) AS maxid FROM SPRINTABLE_DATA GROUP BY telephone_number)dt
ON t.id = dt.maxid WHERE process_status = '0'
AND RESET_FLAG = '0'
ORDER BY id DESC limit 0,700
如果尚未添加这些索引
ALTER TABLE SPRINTABLE_DATA ADD KEY (telephone_number,id)
ALTER TABLE SPRINTABLE_DATA ADD KEY (process_status,reset_flag,id)
另一个可能最快的选项是使用相关子查询
SELECT ID, CUSTID, telephone_number, TOTAL_USAGE, ACCOUNT_STATUS
FROM SPRINTABLE_DATA t WHERE EXISTS
(SELECT MAX( id ) FROM SPRINTABLE_DATA tt WHERE t.id=tt.id AND tt.process_status = '0'
AND tt.RESET_FLAG = '0' )
ORDER BY id DESC limit 0,700
为此你需要
ALTER TABLE SPRINTABLE_DATA ADD KEY (id,process_status,reset_flag)