我们可以从我们的VOIP提供商处购买“中继线”,每当我们查看我们的中继线时,我们每分钟都会收到一笔费用(相当便宜)。提供商没有提供报告功能,因此我们几乎猜测我们应该获得哪些中继线,而且我们经常猜测非常糟糕。所以,我已经设置了一个包含所有呼叫记录的数据库。然后我创建了一个SQL查询,告诉我完成“免费”调用(行)所需的许多中继。这是我正在使用的查询:
USE cdrs;
CREATE TEMPORARY TABLE IF NOT EXISTS cdr_temp
AS (
SELECT callrecords.Timestamp, callrecords.CallEnd, callrecords.CallDirection, callrecords.Rate
FROM cdrs.callrecords
);
UPDATE cdrs.callrecords AS a
SET TrunksNeeded = (
select count(CallID)
FROM cdr_temp AS b
WHERE b.Timestamp <= a.Timestamp
AND b.CallEnd >= a.Timestamp
AND b.CallDirection = a.CallDirection
AND b.Rate > 0
)
WHERE TrunksNeeded IS NULL AND Rate > 0
LIMIT 50;
DROP TEMPORARY TABLE IF EXISTS cdr_temp;
注意,限制50 ......对于50个记录,这需要50-80秒。我尝试过使用索引进行优化。但我所做的一切似乎都没有帮助。下面是show table dump:
CREATE TABLE 'callrecords' (
'Timestamp' datetime DEFAULT NULL,
'AccountID' varchar(45) DEFAULT NULL,
'CNAME' varchar(45) DEFAULT NULL,
'To' varchar(255) DEFAULT NULL,
'From' varchar(255) DEFAULT NULL,
'CallDirection' varchar(45) DEFAULT NULL,
'hangup_cause' varchar(45) DEFAULT NULL,
'BillingSeconds' int(11) DEFAULT NULL,
'DurationSeconds' int(11) DEFAULT NULL,
'Rate' float DEFAULT NULL,
'RateName' varchar(45) DEFAULT NULL,
'Cost' float DEFAULT NULL,
'CallID' varchar(255) DEFAULT NULL,
'CallEnd' datetime DEFAULT NULL,
'TrunksNeeded' int(11) DEFAULT NULL,
KEY 'idx_calldata' ('Timestamp','CallEnd','CallDirection','Rate')
) ENGINE=InnoDB DEFAULT CHARSET=utf8
数据库中有大约150万条记录代表90天的通话记录。其中大约400k的速率超过0.意思是,它们是可计费的非内部呼叫。
我有两个问题。
1)是否有一种简单的方法来更改我正在使用的表或查询以使查询运行得更快?
2)如果没有,根据我的计算,将需要5天的时间来运行30天的记录查询。我知道这听起来很疯狂,但至少在接下来的一年左右,我对此感到满意。有没有办法发出这个命令,以便最终只在后台完成并忽略超时?
编辑: 在@Sentinel推荐的情况下向临时表添加索引有很大帮助。此外,我注意到我的硬盘驱动器正在最大化。所以我将临时数据库放入内存,这也是一个巨大的进步。看起来现在的查询将花费一天的时间来运行。但我仍然留下如何让查询运行那么久的问题......
更新了SQL查询:
USE cdrs;
CREATE TEMPORARY TABLE IF NOT EXISTS cdr_temp ENGINE=MEMORY
AS (
SELECT callrecords.Timestamp, callrecords.CallEnd, callrecords.CallDirection, callrecords.Rate
FROM cdrs.callrecords
);
alter table cdr_temp add index idx1 (CallDirection, rate, timestamp, callend);
UPDATE cdrs.callrecords AS a
SET TrunksNeeded = (
select count(CallID)
FROM cdr_temp AS b
WHERE b.Timestamp <= a.Timestamp
AND b.CallEnd >= a.Timestamp
AND b.CallDirection = a.CallDirection
AND b.Rate <> 0
)
WHERE TrunksNeeded IS NULL AND Rate <> 0
ORDER BY Timestamp
LIMIT 5000;
DROP TEMPORARY TABLE IF EXISTS cdr_temp;
答案 0 :(得分:1)
您报告的大部分时间都可能花在实例化临时表cdr_temp
上,而临时表没有索引以提高性能。
您是否尝试过不使用临时表:
UPDATE cdrs.callrecords AS a
SET TrunksNeeded = (
select count(CallID)
FROM cdrs.callrecords AS b
WHERE b.Timestamp <= a.Timestamp
AND b.CallEnd >= a.Timestamp
AND b.CallDirection = a.CallDirection
AND b.Rate <> 0
)
WHERE TrunksNeeded IS NULL AND Rate > 0
LIMIT 50;
如果您有(CallDirection,Rate)索引,也可能会获得更好的性能,尤其是当您测试b.Rate <> 0
而不是b.Rate > 0
时,因为查询优化器/规划器可能能够消除更多记录在进行检查重叠呼叫所需的范围扫描之前。
保留临时表并向其添加索引(请注意修订后的列顺序):
alter table cdr_temp add index idx1 (CallDirection, rate, timestamp, callend);
这将使用临时表和新索引,并且我推荐了代码更改。
UPDATE callrecords AS a
SET TrunksNeeded = (
select count(CallID)
FROM cdr_temp AS b
WHERE b.CallDirection = a.CallDirection
AND b.Rate <> 0
AND a.Timestamp BETWEEN b.Timestamp AND b.CallEnd
)
WHERE TrunksNeeded IS NULL AND Rate > 0
LIMIT 50;