2009-11-12 2 views
1

내 테이블 (프로젝트) (인덱스를 사용하고 계십니까?) :MySQL의 : 여러 범위에서 선택 행을 최적화

당신이다시피
id, lft, rgt 
1, 1, 6 
2, 2, 3 
3, 4, 5 
4, 7, 10 
5, 8, 9 
6, 11, 12 
7, 13, 14 

, 이것은 nested set model를 사용하여 계층 적 데이터입니다. 나무가 꽤 인쇄 :

1 
2 
3 
4 
5 
6 
7 

는 내가 함께 할 수있는 프로젝트 1과 4에서 모든 하위 프로젝트를 선택합니다 그러나

SELECT p.id 
FROM projects AS p, projects AS ps 
WHERE (ps.id = 1 OR ps.id = 4) 
AND p.lft BETWEEN ps.lft AND ps.rgt 

을,이 큰 테이블과 매우 느립니다 (쿼리) EXPLAIN 실행 내가 얻을 :

+----+-------------+-------+-------+------------------------+---------+---------+------+------+-------------------------------------------------+ 
| id | select_type | table | type | possible_keys   | key  | key_len | ref | rows | Extra           | 
+----+-------------+-------+-------+------------------------+---------+---------+------+------+-------------------------------------------------+ 
| 1 | SIMPLE  | ps | range | PRIMARY,lft,rgt,lftRgt | PRIMARY | 4  | NULL | 2 | Using where          | 
| 1 | SIMPLE  | p  | ALL | lft,lftRgt    | NULL | NULL | NULL | 7040 | Range checked for each record (index map: 0x12) | 
+----+-------------+-------+-------+------------------------+---------+---------+------+------+-------------------------------------------------+ 

(. 프로젝트 테이블 LFT, RGT, 및 LFT-RGT에 인덱스가 당신이 볼 수 있듯이, MySQL은 어떤 인덱스를 사용하지 않으며, 7040 개 레코드를 루프)

+----+-------------+-------+-------+------------------------+---------+---------+-------+------+-------------+ 
| id | select_type | table | type | possible_keys   | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+------------------------+---------+---------+-------+------+-------------+ 
| 1 | SIMPLE  | ps | const | PRIMARY,lft,rgt,lftRgt | PRIMARY | 4  | const | 1 |    | 
| 1 | SIMPLE  | p  | range | lft,lftRgt    | lft  | 4  | NULL | 7 | Using where | 
+----+-------------+-------+-------+------------------------+---------+---------+-------+------+-------------+ 

드디어, 내 질문 :

SELECT p.id 
FROM projects AS p, projects AS ps 
WHERE ps.id = 1 
AND p.lft BETWEEN ps.lft AND ps.rgt 

가 설명 :

나는 단지 슈퍼 프로젝트 중 하나를 선택하면, MySQL은 인덱스를 사용하여 관리하는 것으로 나타났습니다 나는 거기에 여러 범위와 일치하는 행을 선택하고 인덱스에서 여전히 이익을 얻을 수있는 방법이 있습니까?

답변

1

MySQL의 메뉴얼에 7.2.5.1. The Range Access Method for Single-Part Indexes에서 : 현재

, MySQL이 공간 인덱스 범위 액세스 방법에 대한 여러 범위를 병합을 지원하지 않는다. 이 제한 사항을 해결하기 위해 각 공간 술어를 다른 SELECT에 넣는 것을 제외하고는 동일한 SELECT 문을 가진 UNION을 사용할 수 있습니다.

그래서 두 개의 서로 다른 선택 항목이 있어야합니다.

+0

:(내가 아는 대답은 아니지만 적어도 시간을 낭비하지 말고 알아내는 것이 좋습니다. – Joernsn

1

노조를 사용해 보셨습니까? 귀하의 두 번째 예제를 가지고 밑에 "union"을 추가하고 반복하지만 아이디 4와 일치 시키십시오. 작동하는지 모르겠지만 시도하는 것이 분명한 것 같습니다.

편집 :

SELECT p.id 
FROM projects AS p, projects AS ps 
WHERE ps.id = 1 
AND p.lft BETWEEN ps.lft AND ps.rgt 
UNION 
SELECT p.id 
FROM projects AS p, projects AS ps 
WHERE ps.id = 4 
AND p.lft BETWEEN ps.lft AND ps.rgt 
1

쿼리가 여러 범위를 병합합니다.

range 액세스 방법을 사용하여 p (결합을 이끄는)의 여러 범위를 결합합니다. p로부터 반환 된 각 행

, 그것은 p.lft과의 p.rgt 주어진 값 ps 모든 행을 검색하는 최선의 방법을 확인한다. 쿼리 선택도에 따라 ps 이상의 전체 검색 또는 두 가지 가능한 인덱스 중 하나에 대한 인덱스 조회가있을 수 있습니다.

EXPLAIN에 표시된 행 수는 아무런 의미가 없습니다. EXPLAIN은 가능한 최악의 결과를 보여줍니다. 반드시 모든 행을 검사한다는 의미는 아닙니다.옵티마이 저의 실행 여부는 실행시에만 알 수 있습니다.

여러 범위를 병합 할 수 없다는 문서 스 니펫은 GEOMETRY 유형을 사용하여 생성 한 인덱스 인 SPATIAL 인덱스에만 유효합니다. 이러한 인덱스는 위쪽으로 검색하는 쿼리 (주어진 프로젝트의 조상)에 적합하지만 아래쪽으로는 쿼리에 적합하지 않습니다.

일반 B-Tree 색인은 여러 범위를 결합 할 수 있습니다. documentation에서 : 인덱스의 모든 유형의

, OR 또는 AND과 함께 여러 범위 조건 범위 조건을 형성한다. 하나 하나의 FULLSCAN (선도 ps와 함께)를 사용하거나 여러 범위 스캔합니다

진짜 문제는 MySQL에 최적화 한 올바른 결정을 내릴 수 없다는 것입니다.

너는 10,000 행이고 ​​프로젝트 경계는 0-5002000-2500이다. 옵티마이 저는 각 경계가 인덱스로부터 이익을 얻는 것을 볼 것이고, range check은 두 번의 범위 접근을 야기 할 것이며, 하나의 fullscan이 더 좋을 것입니다.

프로젝트 경계가 예를 들어 0-30005000-6000 인 경우 더 나쁠 수 있습니다. 이 경우 옵티마이 저는 을 2 개의 fullscans로 만들지 만 충분할 것입니다.

CREATE INDEX ix_lft_id ON projects (lft, id) 

범위 조건보다는 커버링 인덱스를 통해 fullscan를 사용하는 티핑 포인트가 90%입니다 :

는이 순서대로 (lft, id)에 커버링 인덱스를해야한다, 최적화가 올바른 결정을 내릴 수 있도록 즉, 실제 계획에서 하나 이상의 전체 스캔을 가질 수는 없습니다.