2011-11-23 9 views
11

私は2つのテーブルを持っています:1つはポイント、もう1つはpolysです。空間的なmysqlインデックスの結合

CREATE TABLE `points` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `point` point NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM; 

CREATE TABLE `ranges` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `poly` polygon NOT NULL, 
    PRIMARY KEY (`id`), 
    SPATIAL KEY `poly` (`poly`) 
) ENGINE=MyISAM; 

ポリゴンの点のポイントに範囲を結合したいと考えています。クエリはシンプルに見える:私はテーブルpointsからいくつかの行に参加しようとすると、

​​

:しかし

SELECT * 
    FROM points 
    LEFT JOIN ranges 
     ON MBRCONTAINS(poly, point) 
    WHERE points.id = 2; 

このクエリは、高速で動作し、インデックスを使用し、説明の一部

SELECT * 
    FROM points 
    LEFT JOIN ranges 
    ON MBRCONTAINS(poly, point) 
    WHERE points.id IN (1,2,3); 

すべてが故障する:

+----+-------------+------------+-------+---------------+---------+---------+------+--------+-------------+ 
| id | select_type | table  | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+------------+-------+---------------+---------+---------+------+--------+-------------+ 
| 1 | SIMPLE  | points  | range | PRIMARY  | PRIMARY | 4  | NULL |  3 | Using where | 
| 1 | SIMPLE  | ranges  | ALL | poly   | NULL | NULL | NULL | 155183 |    | 
+----+-------------+------------+-------+---------------+---------+---------+------+--------+-------------+ 

FORCE INDEX (poly)を追加することは役に立ちません。クエリをテストする

サンプル・データ(申し訳ありませんが、唯一のPHPのバージョンは、私は、SQLの手続きに一般的ではありませんよ):

//points 
for($i=0;$i<=500;$i++) { 
    $point = mt_rand(); 
    mysql_query('INSERT INTO points (point) VALUES (POINTFROMWKB(POINT('.$point.', 0)))'); 
} 

$qty = 20000; 
$max = mt_getrandmax(); 
$add = $max/$qty 
$end = 0; 

//polys 
while($end < $max) { 
    $start = $end; 
    $end = mt_rand($start, $start + $add); 
    mysql_query('INSERT INTO ranges (poly) VALUES (
     GEOMFROMWKB(POLYGON(LINESTRING(
      POINT('.$start.', -1), 
      POINT('.$end.', -1), 
      POINT('.$end.', 1), 
      POINT('.$start.', 1), 
      POINT('.$start.', -1) 
     ))) 
    )'); 
} 
+0

http://dba.stackexchange.com/? –

+0

「永遠に崩壊する」ということは何を意味するのか説明できますか?エラーメッセージは読みにくいですか? – Bytemain

+0

これらのテーブルのサンプルを挿入するのは親切でしょうか?また、その結合条件を使用するためにそのIN条件を書き直そうとしましたか? – Pentium10

答えて

6

私は、MySQLが空間インデックスをマージサポートしていないからだと考えています。まだそれが本当であるかどうかわからないが、私はそれを過去のどこかで読んだ。 ORステートメントがある場合、空間インデックスは使用されません

あなたの場合、points.id = 1はどこですか?これはmbrcontainsで使用される結果が返されるストレートセレクトです。それはインデックスを使用します。

あなたは3件の結果を返し、それぞれが範囲テーブルにマップする必要があり、したがって、あなたができる

結果

id select_type  table type possible_keys key  key_len  ref  rows filtered Extra 
1 SIMPLE points range PRIMARY  PRIMARY  4 NULL 3 100.00 Using where 
1 SIMPLE ranges ALL  poly NULL NULL NULL 6467418  100.00 

を働いていないpoints.in(1,2,3)、追加これを行うことにより、ポイントテーブルなしで、あなたのテストを簡素化:mbrcontains(ポリ、GEOMFROMWKB(POINT(0、0)))

id select_type  table type possible_keys key  key_len  ref  rows filtered Extra 
1 SIMPLE ranges range poly poly 34 NULL 1 100.00 Using where 

そして今、この範囲SELECT * FROM。 mbrcontains(ポリ、GEOMFROMWKB(点(0、0)))OR mbrcontains(ポリ、GEOMFROMWKB(点(10,10)))

結果

id select_type  table type possible_keys key  key_len  ref  rows filtered Extra 
1 SIMPLE ranges ALL  poly NULL NULL NULL 6467418  100.00 Using where 

でのその参照範囲SELECT * FROM 2番目のケースでは、インデックスを使用せずにスキャンするだけです。

特定のポイントごとにUNIONを作成してクエリを強制的に使用することができますが、それがより高速になるかどうかはわかりません。私はローカルでいくつかのテストを行い、それはあなたの最初のクエリよりも少し遅かったです。

EXPLAIN EXTENDED 
SELECT * 
FROM points 
FORCE INDEX (PRIMARY) 
LEFT JOIN ranges 
FORCE INDEX (poly) ON mbrcontains(poly, point) 
WHERE points.id = 1 
UNION DISTINCT 
SELECT * 
FROM points 
FORCE INDEX (PRIMARY) 
LEFT JOIN ranges 
FORCE INDEX (poly) ON mbrcontains(poly, point) 
WHERE points.id = 2 
UNION DISTINCT 
SELECT * 
FROM points 
FORCE INDEX (PRIMARY) 
LEFT JOIN ranges 
FORCE INDEX (poly) ON mbrcontains(poly, point) 
WHERE points.id = 3 

結果

id select_type  table type possible_keys key  key_len  ref  rows filtered Extra 
1 PRIMARY  points const PRIMARY  PRIMARY  4 const 1 100.00 
1 PRIMARY  ranges range poly poly 34 NULL 1 100.00 Using where 
2 UNION points const PRIMARY  PRIMARY  4 const 1 100.00 
2 UNION ranges range poly poly 34 NULL 1 100.00 Using where 
3 UNION points const PRIMARY  PRIMARY  4 const 1 100.00 
3 UNION ranges range poly poly 34 NULL 1 100.00 Using where 
NULL UNION RESULT <union1,2,3> ALL  NULL NULL NULL NULL NULL NULL  
+1

@ oroboros102、あなたが望む場合に備えて参照が見つかりました:http://dev.mysql.com/doc/refman/5.0/en/range-optimization.html。 7.3.1.3.1の最後の段落。 –

0

あなたが扱っているすべての正方形であれば、私はちょうどあなたを実行し、上、左、高さ、幅を表すインデックスを作成することができ、あなたのテーブル内の4つの数字を扱うでしょう問題のあなたのポイントが、左、左+幅、および「Y」座標の間に「X」座標を持ち、「Top」、「Top + Height」の間で照会します。

+0

しかし、BETWEENよりはるかに速いため、ジオメトリを使用します。 – Oroboros102

+0

実際には、MySQLは 'BETWEEN'に参加できません。だから、私は空間索引にそのような結合を作る考えがありました。しかし、それでも、それはうまくいきませんでした。 – Oroboros102

3

データモデルに1つの違いがありますが、ポイントデータベースの空間キーのみを使用して、同様のクエリを使用できました。私の場合:

CREATE TABLE geopoints (
    pid int(11) NOT NULL AUTO_INCREMENT, 
    description varchar(255) NOT NULL DEFAULT '', 
    geopoint point NOT NULL, 
    PRIMARY KEY (pid), 
    SPATIAL KEY geopoint (geopoint) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

、すべてがこのようなクエリではうまくいった:

SELECT pt.pid, x(geopoint), Y(geopoint), pl.pid, AsText(geopolygon) 
    FROM geopoints pt INNER JOIN geopolygons pl ON MBRCONTAINS(geopolygon, geopoint) 
WHERE pt.pid IN (1,2,4,5) AND pl.pid BETWEEN 1 AND 5; 

私の2セント、

関連する問題