우리는 VoIP 공급자로부터 "트렁크"를 구입할 수 있으며 트렁크를 통과 할 때마다 분당 요금이 부과됩니다 (예쁜 페니). 공급자는보고 기능을 제공하지 않으므로 어떤 트렁크를 사용해야하는지 추측하고 있으며 빈번하게 추측하고 있습니다. 그래서, 우리는 그것의 모든 통화 기록을 가지고있는 DB를 셋업했습니다. 그런 다음 "무료"전화 (행)를 완료하는 데 필요한 많은 트렁크를 알려주는 SQL 쿼리를 만들었습니다. 내가 사용하고있는 쿼리는 다음과 같습니다.중첩 된 mySQL 쿼리 최적화 ... 또는 무기한 실행
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-80 초입니다. 색인을 사용하여 최적화를 시도했습니다. 그러나 내가하는 일은 도움이되지 않는다. 다음은 쇼 테이블 덤프입니다.
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
90 일간의 통화 로그를 나타내는 DB에는 약 150 만 개의 레코드가 있습니다. 그리고 약 400k는 0을 초과합니다. 의미는 청구 가능하고 비 내부 통화입니다.
두 가지 질문이 있습니다.
1) 쿼리를 빠르게 실행하기 위해 사용하고있는 테이블이나 쿼리를 변경하는 쉬운 방법이 있습니까?
2) 계산하지 못하면 30 일 분량의 레코드에 대해 쿼리를 실행하는 데 5 일이 걸립니다. 나는 그것이 미친 소리가 난다는 것을 알고있다. 그러나 내년 또는 그렇게 최소한, 나는 그것으로 OK이다. 이 명령을 발행하여 백그라운드에서 완료하고 제한 시간을 무시하는 방법이 있습니까?
EDIT : 임시 테이블에 @Sentinel 권장 색인을 추가하면 많은 도움이됩니다. 또한, 내 하드 디스크가 최대 밖으로 나타났습니다. 그래서 임시 DB를 메모리에 두었습니다. 쿼리가 실행되는 데 하루가 조금 걸릴 것 같습니다. 하지만 난 여전히 ... 나는 그렇게 오래에 대한 쿼리를 실행하도록 할 수 있습니다 방법에 대한 질문을
업데이트 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;
느린'UPDATE' 교화하는 더 좋은 방법 : http://mysql.rjweb.org/doc.php/deletebig#deleting_in_chunks 당신이 전화를 사전 필터링에 의해 몇 가지 추가 성능 향상을 짜낼 수 있습니다 –
을 cdr_temp 테이블을 인스턴스화 할 때 기록합니다. 구체적으로'cdrs.callrecords에서 ... rate <> 0'을 선택하여'SET TrunksNeeded = (... ... ')에서 작업하고있는 레코드 수를 줄일 수 있습니다.)'subquery – Sentinel