2009-06-10 8 views
0

J'utilise 'explain' pour voir pourquoi ma requête touche toutes les lignes de ma base de données, et je ne comprends pas pourquoi c'est le cas.Mysql n'utilise pas mes index

Quelqu'un pourrait jeter un coup d'oeil et me donner un indice de ce qui me manque?

Ma base de données est MyISAM, et je me sers Mysql 5.1, PHP5

Voici ma table:

-- 
-- Table structure for table `users` 
-- 

CREATE TABLE IF NOT EXISTS `users` (
    `user_id` bigint(20) NOT NULL auto_increment, 
    `name` varchar(40) default NULL, 
    `city` varchar(90) default NULL, 
    `latitude` float NOT NULL default '0', 
    `longitude` float NOT NULL default '0', 
    PRIMARY KEY (`user_id`), 
    UNIQUE KEY `name` (`name`), 
    KEY `Radius Search` (`latitude`,`longitude`), 
    KEY `Radius 2` (`longitude`,`latitude`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=38666 ; 

Voici ma question:

$query =  
"SELECT 
    name, city 
FROM 
    users 
WHERE 
    (
     (69.1 * (latitude - " . $user->latitude . ")) * 
     (69.1 * (latitude - " . $user->latitude . ")) 
    ) + ( 
     (69.1 * (longitude - " . $user->longitude . ") * COS(" . $user->latitude . "/57.3)) * 
     (69.1 * (longitude - " . $user->longitude . ") * COS(" . $user->latitude . "/57.3)) 
    ) < " . pow($radius, 2) . " 
ORDER BY 
    (
     (69.1 * (latitude - " . $user->latitude . ")) * 
     (69.1 * (latitude - " . $user->latitude . ")) 
    ) + ( 
     (69.1 * (longitude - " . $user->longitude . ") * COS(" . $user->latitude . "/57.3)) * 
     (69.1 * (longitude - " . $user->longitude . ") * COS(" . $user->latitude . "/57.3)) 
    ) ASC"; 

Et enfin, mon expliquer ...

id select_type  table type possible_keys key  key_len  ref  rows Extra 
1 SIMPLE users ALL  NULL NULL NULL NULL 38665 Using where; Using filesort 

Répondre

2

Probablement parce que vous filtrez sur une EXPRESSION plutôt que sur une colonne indexée.

Les index MySQL sur les colonnes, pas les expressions. Vous avez quelques options ici:

  1. Pouvez-vous réduire l'expression à une valeur fixe et la stocker dans une ou deux colonnes?

Si cela n'est pas possible, alors pensez à créer une table de recherche très rapide et rejoignez-la. Par exemple:

CREATE TABLE user_loc 
(
    longitude float(n,m) not null, 
    latitude float(n,m) not null, 
    user_id int unsigned not null 
); 

Parce que ce sera une petite table de largeur fixe, vous devriez être en mesure d'interroger très rapide, même avec un scan de table. Il suffit de vous joindre à elle pour obtenir les utilisateurs que vous voulez.

(note: vérifier les exigences de performance avant d'aller à ces longueurs, comme vous ne pouvez pas besoin du tout)

3

Comment exactement est-il supposé utiliser un index trié sur 'latitude' pour évaluer plus efficacement votre clause where? ... Lorsqu'un champ est utilisé dans une expression de la clause where, les index ne s'appliquent généralement pas.

En outre, dans ce cas, je ne suis pas sûr que vous pouvez même réécrire la clause pour que l'index s'applique, ce qui est un peu plus rare.

Addendum:
En regardant ce que vous en train d'essayer de le faire, peut-être vous pouvez utiliser une taille fixe boîte lat/long autour des coordonnées des utilisateurs, de sorte que vous pouvez utiliser BETWEEN clauses sur lat et long. Ensuite, vous pouvez conserver le complexe ORDER BY tel qu'il est maintenant, et couper après le 'x' les autres utilisateurs les plus proches.

Ensuite, vous pouvez ne jamais remarquer que les correspondances sont réellement dans une boîte plutôt que d'un rayon circulaire de celui que vous envisagez.

+1

$ Vous pouvez garder le rayon circulaire et juste * ajouter * entre les clauses; alors l'indexation devrait commencer, mais les choses en dehors du cercle seront toujours exclues par l'endroit où. – ysth

+0

Il y a un bon exemple de ceci à http://www.goondocks.com/blog/08-01-22/zip_code_radius_search_using_mysql.aspx – ysth

2

Vous ne comparez pas quoi que ce soit à la latitude ou la longitude. Pour indes travailler mysql nees à se voir quelque chose à l'effet latitude = (ou <> entre, etc).

Essayez de réécrire la requête afin qu'il lit

OU latitude entre lat_low $ ET lat_hi $ et la longitude entre long_low $ ET long_hi

Questions connexes