2011-11-23 16 views
11

Tengo dos tablas: una con puntos, la otra con polys.Se une a los índices espaciales 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; 

que quieren unirse a los rangos de puntos en puntos del interior de polígonos. Las consultas parecen simples:

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

Esta consulta funciona rápido y utiliza los índices, que forma parte de explicar:

table | type | possible_keys | key | key_len 
ranges | range | poly   | poly | 34 

embargo, cuando intento para unirse con varias filas de la tabla points:

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

todo se descompone:

+----+-------------+------------+-------+---------------+---------+---------+------+--------+-------------+ 
| 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 |    | 
+----+-------------+------------+-------+---------------+---------+---------+------+--------+-------------+ 

Agregar FORCE INDEX (poly) no ayuda.

datos de muestra para probar consultas (lo siento, sólo la versión de php, no soy común con los procedimientos de 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

¿Puedes explicar a qué te refieres con 'everthing breaks down'? El mensaje de error es difícil de leer? – Bytemain

+0

¿Sería bueno que publicara una inserción de muestra para esas tablas y trató de volver a escribir esa condición IN para usar uniones? – Pentium10

Respuesta

6

Creo que es porque MySQL no soporta la fusión de los índices espaciales. No estoy seguro si todavía es cierto, pero lo he leído en algún lugar del pasado. Si tiene una instrucción OR, entonces los índices espaciales no se usan

En su caso, ¿dónde está haciendo points.id = 1, eso es una selección directa con un resultado devuelto que se usa en los mbrcontains. Eso usa el índice.

Cuando se agrega points.in (1,2,3), que devuelve los resultados 3 y cada uno necesita estar asignada a la tabla de rangos, por lo tanto, no funciona

resultado

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 

Puede simplificar su prueba sin el la tabla de puntos al hacer esto: SELECT * FROM rangos donde mbrcontains (poli, GeomFromWKB (punto (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 

Y ahora esto; SELECT * FROM rangos donde mbrcontains (poli, GeomFromWKB (punto (0, 0))) OR mbrcontains (poli, GeomFromWKB (PUNTO (10, 10)))

resultado

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 

ver que en el segundo caso, no está utilizando el índice y solo escaneando.

Puede forzar la consulta para usar el índice creando UNION para cada punto específico, pero no estoy seguro si eso será más rápido. Hice algunas pruebas localmente y fue un poco más lento que tu primera consulta.

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 

resultado

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, encontró la referencia en caso de que la desee: http://dev.mysql.com/doc/refman/5.0/en/range-optimization.html. Último párrafo en 7.3.1.3.1. –

0

Si todo lo que están tratando son cuadrados, me acaba de hacer frente a 4 números de la tabla que se pueden indexar en representación superior, izquierda, altura, anchura, a continuación, ejecutar su consulta donde su punto en cuestión tiene su coordenada "X" entre las coordenadas Izquierda, Izquierda + Anchura y "Y" entre Arriba, Arriba + Altura.

+0

Pero uso la geometría, porque es mucho más rápido que 'BETWEEN'. – Oroboros102

+0

Actualmente, MySQL no puede unirse en 'BETWEEN'. Entonces, tuve la idea de hacer tales uniones en índices espaciales. Pero, aún así, eso no funcionó. – Oroboros102

3

He utilizado con éxito consultas similares, con una sola diferencia en el modelo de datos: una clave espacial en la base de datos de puntos. En mi caso:

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; 

Y todo ha ido bien en las consultas como esta:

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; 

mi granito de arena,