0

我正在使用“解释”来查看为什么我的查询会命中数据库中的每一行,但我不明白为什么会这样。

有人可以看看并给我一个提示我错过了什么吗?

我的数据库是 MyISAM,我使用的是 Mysql 5.1,PHP5

这是我的桌子:

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

这是我的查询:

$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";

最后,我的解释...

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
4

3 回答 3

3

究竟应该如何使用“纬度”上的排序索引来更有效地评估你的 where 子句?...当在 where 子句的表达式中使用字段时,索引通常不适用。

此外,在这种情况下,我不确定您甚至可以重写子句以使索引适用,这有点少见。

附录
看看你实际上想要做什么,也许你可以在用户坐标周围使用一个固定大小的纬度/经度框,这样你就可以BETWEEN在纬度和经度上使用子句。然后你可以保持现在的复合体,并在' '最近的其他用户ORDER BY之后切断。x

然后你可能永远不会注意到匹配实际上是在一个盒子里,而不是你正在考虑的那个圆形半径。

于 2009-06-10T01:58:17.683 回答
2

很可能是因为您正在过滤 EXPRESSION 而不是索引列。

MySQL 索引列,而不是表达式。你有几个选择:

  1. 您可以将表达式归结为一个固定值并将其存储在一列或两列中吗?

如果那是不可能的,那么考虑创建一个非常快速的查找表,然后加入它。例如:

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

因为它将是一个很小的固定宽度表,所以即使使用全表扫描,您也应该能够非常快速地查询它。只需加入反对它以获得您想要的用户。

(注意:在进行这些长度之前检查性能要求,因为您可能根本不需要它)

于 2009-06-10T02:00:14.167 回答
2

您实际上并没有将任何东西与纬度或经度进行比较。为了使 indes 工作,mysql 需要设置一些影响纬度 =(或 < > 之间等)的东西。

尝试重写查询,使其读取

$lat_low 和 $lat_hi 之间的纬度和 $long_low 和 $long_hi 之间的经度

于 2009-06-10T02:02:57.127 回答