2010-04-25 6 views
2

두 테이블 70,000 개의 항목이있는 표 A와 600,000 개의 항목이있는 표 B가 있습니다. 다음과 같이 구조는 다음과 같습니다이 MySQL 쿼리를 최적화하기위한 다른 방법이 있습니까?

표 A :

+-----------+---------------------+------+-----+---------+----------------+ 
| Field  | Type    | Null | Key | Default | Extra   | 
+-----------+---------------------+------+-----+---------+----------------+ 
| id  | bigint(20) unsigned | NO | PRI | NULL | auto_increment | 
| number | bigint(20) unsigned | YES |  | NULL |    | 
+-----------+---------------------+------+-----+---------+----------------+ 

표 B :

+-------------+---------------------+------+-----+---------+----------------+ 
| Field  | Type    | Null | Key | Default | Extra   | 
+-------------+---------------------+------+-----+---------+----------------+ 
| id   | bigint(20) unsigned | NO | PRI | NULL | auto_increment | 
| number_s | bigint(20) unsigned | YES | MUL | NULL |    | 
| number_e | bigint(20) unsigned | YES | MUL | NULL |    | 
| source  | varchar(50)   | YES |  | NULL |    | 
+-------------+---------------------+------+-----+---------+----------------+ 

내가 표 A의 값 중 하나를 사용하여 표 B에있는 경우 찾기 위해 노력하고있다 다음 코드 :

$sql = "SELECT number from TableA"; 
$result = mysql_query($sql) or die(mysql_error()); 

while($row = mysql_fetch_assoc($result)) { 
     $number = $row['number']; 
     $sql = "SELECT source, count(source) FROM TableB WHERE number_s < $number AND number_e > $number GROUP BY source"; 
     $re = mysql_query($sql) or die(mysql_error); 
     while($ro = mysql_fetch_array($re)) { 
       echo $number."\t".$ro[0]."\t".$ro[1]."\n"; 
     } 
} 

나는 쿼리가 빠르게 진행되기를 바랬지 만 어떤 이유로 그것은 끔찍하지 않다. 빨리.

mysql> explain SELECT source, count(source) FROM TableB WHERE number_s < 1812194440 AND number_e > 1812194440 GROUP BY source; 
+----+-------------+------------+------+-------------------------+------+---------+------+--------+----------------------------------------------+ 
| id | select_type | table  | type | possible_keys   | key | key_len | ref | rows | Extra          | 
+----+-------------+------------+------+-------------------------+------+---------+------+--------+----------------------------------------------+ 
| 1 | SIMPLE  | TableB  | ALL | number_s,number_e  | NULL | NULL | NULL | 696325 | Using where; Using temporary; Using filesort | 
+----+-------------+------------+------+-------------------------+------+---------+------+--------+----------------------------------------------+ 
1 row in set (0.00 sec) 

인가가 나는이 밖으로 짜낼 수있는 최적화 : ("수"의 특정 값) 선택에 설명 내 것은 다음 나에게 준다?

동일한 작업을 위해 저장 프로 시저를 작성했지만 처음부터 제대로 작동하지 않는 것 같습니다 ... 구문 오류가 없습니다 ... 하루 동안 실행 해 보았습니다. 이상하게 느껴지는 아직 달리고 있었다. 당신이 number_enumber_s 열에 대한 별도의 인덱스를 가지고있는 것처럼

CREATE PROCEDURE Filter() 
Begin 
    DECLARE number BIGINT UNSIGNED; 
    DECLARE x INT; 
    DECLARE done INT DEFAULT 0; 
    DECLARE cur1 CURSOR FOR SELECT number FROM TableA; 
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; 
    CREATE TEMPORARY TABLE IF NOT EXISTS Flags(number bigint unsigned, count int(11)); 
    OPEN cur1; 
    hist_loop: LOOP 
    FETCH cur1 INTO number; 
    SELECT count(*) from TableB WHERE number_s < number AND number_e > number INTO x; 
    IF done = 1 THEN 
     LEAVE hist_loop; 
    END IF; 
    IF x IS NOT NULL AND x>0 THEN 
     INSERT INTO Flags(number, count) VALUES(number, x); 
    END IF; 
    END LOOP hist_loop; 
    CLOSE cur1; 
END 
+2

나는 이것을 똑바로 만듭시다. 당신은 700,001 개의 쿼리를 실행 중입니다. 당신은 그것이 빠르지 않은 것에 놀랐습니까? – Thomas

+0

글쎄 .. 나는 그다지 빠르지 않다는 말은하지 않고있다. 내가 더 빨리 할 수있는 최적화가 있는지 묻고있다. :) – Legend

+0

'$ number BETWEEN number_s 및 number_e'? – extraneon

답변

4

포인트가 포함 된 간격을 찾으려고합니다. 이것은 B-tree 색인 (대부분의 데이터베이스에서 기본 색인 유형)이 너무 빠르지 만 R-tree 색인은 이러한 종류의 조회에 적합합니다. MySQL은 인덱스의 타입을 직접 변경할 수는 없지만, MySQL이 GEOMETRY 컬럼 타입을 사용하여 R-Tree를 사용하도록 할 수있다.

Quassnoihis article on nested sets in MySQL을 포함합니다. 상당히 동일하지는 않지만 매우 유사합니다. 기사에서 인용 :

* Searching for an IP address in the IP range ban list 
* Searching for a given date within a date range 

및 몇몇 다른 사람 :

알려진 값을 포함하는 모든 범위 검색이 필요한 작업 의 특정 클래스도 있습니다.이러한 작업 MySQL의

+0

R-Tree는 결코 저에게 일어나지 않았습니다 ... – Legend

+0

이 답변은 아마 누군가가'table1의 모든 레코드에 대해 '와 같은 코드를 사용할 때 여전히 재미있는 느낌을 얻습니다. .. table2에서 어디 table1'에서 무언가를 기반으로 조건'. 조인은 더 자연스러운 사고 방식이 될 것입니다. – extraneon

+0

@extraneon : 실제로, 기사를 읽으면 Quassnoi가 제안한 것과 정확히 똑같은 것을 볼 수 있습니다. 'JOIN t_hierarchy hrp MBRWithin (Point (0, hp.lft), hrp.sets)'에 있습니다. 여기에 모든 간격을 원하지는 않지만, 하나가 있다는 것을 알고 있으면 충분합니다. –

2

그것은 아마 별도의 ADD INDEX(number_e)ADD INDEX(number_s) 컬럼으로 작성, 나에게 보인다.

두 열을 모두 포함하는 인덱스를 추가하면 둘 다 쿼리에서 사용되므로 MySQL은 단일 열 인덱스 중 하나를 사용하도록 선택하지 않고 전체 테이블 스캔이 더 빠를 것입니다 (쿼리가 광범위한 값에 걸쳐있는 경우 흔하지 않습니다).

ALTER TABLE tblB ADD INDEX(number_s,number_e); 

MySQL은 그냥 단지 number_s에 대해 질의를 위해 만든 하나를 사용할 수있는 당신은, 그 이후 개별 number_s 인덱스를 필요가 없습니다, 그래서 당신은뿐만 아니라 하나를 삭제할 수 있습니다.

+0

결합 된 색인 +1. 많은 차이를 관찰하지 못했지만 나는 앞으로 나아가서 제안 된 R-Trees를 시도 할 것입니다. 감사! – Legend

1

우선 R 트리 능력을 이용하여 개선 될 수 있으며, I는 원하는 출력이 그룹에 입력 number_e 및 number_s, 그 카운트 사이에있는 모든 '소스'라고 가정.

나는 구문에 마구 해요,하지만 당신은보다 큰 사업자

편집/작음 사용하여 명시 적으로 비교하는 대신 거기 조항 'BETWEEN'를 사용하는 것이 좋습니다 : Zombat의 말씀도 적용; 색인도 도움이 될 것입니다.

+0

+1에 대한 제안. 감사! – Legend

관련 문제