我的数据库中有一个邮政编码表,该表与业务表结合使用以查找符合特定条件且最接近指定邮政编码的企业。我做的第一件事就是只获取纬度和经度,因为它在页面上的几个地方使用过。我使用:
$zipResult = mysql_fetch_array(mysql_query("SELECT latitude,longitude FROM zipCodes WHERE zipCode='".mysql_real_escape_string($_SESSION['zip'])."' Limit 1"));
$latitude = $zipResult['latitude'];
$longitude = $zipResult['longitude'];
$radius = 100;
$lon1 = $longitude - $radius / abs(cos(deg2rad($latitude))*69);
$lon2 = $longitude + $radius / abs(cos(deg2rad($latitude))*69);
$lat1 = $latitude - ($radius/69);
$lat2 = $latitude + ($radius/69);
从那里,我生成查询:
$query2 = "Select * From (SELECT business.*,zipCodes.longitude,zipCodes.latitude,
(3956 * 2 * ASIN ( SQRT (POWER(SIN((zipCodes.latitude - $latitude)*pi()/180 / 2),2) + COS(zipCodes.latitude* pi()/180) * COS($latitude *pi()/180) * POWER(SIN((zipCodes.longitude - $longitude) *pi()/180 / 2), 2) ) )) as distance FROM business INNER JOIN zipCodes ON (business.listZip = zipCodes.zipCode)
Where business.active = 1
And (3958*3.1415926*sqrt((zipCodes.latitude-$latitude)*(zipCodes.latitude-$latitude) + cos(zipCodes.latitude/57.29578)*cos($latitude/57.29578)*(zipCodes.longitude-$longitude)*(zipCodes.longitude-$longitude))/180) <= '$radius'
And zipCodes.longitude between $lon1 and $lon2 and zipCodes.latitude between $lat1 and $lat2
GROUP BY business.id ORDER BY distance) As temp Group By category_id ORDER BY distance LIMIT 18";
结果是这样的:
Select *
From (SELECT business.*,zipCodes.longitude,zipCodes.latitude, (3956 * 2 * ASIN ( SQRT (POWER(SIN((zipCodes.latitude - 39.056784)*pi()/180 / 2),2) + COS(zipCodes.latitude* pi()/180) * COS(39.056784 *pi()/180) * POWER(SIN((zipCodes.longitude - -84.343573) *pi()/180 / 2), 2) ) )) as distance
FROM business
INNER JOIN zipCodes ON (business.listZip = zipCodes.zipCode)
Where business.active = 1
And (3958*3.1415926*sqrt((zipCodes.latitude-39.056784)*(zipCodes.latitude-39.056784) + cos(zipCodes.latitude/57.29578)*cos(39.056784/57.29578)*(zipCodes.longitude--84.343573)*(zipCodes.longitude--84.343573))/180) <= '100'
And zipCodes.longitude between -86.2099407074 and -82.4772052926
and zipCodes.latitude between 37.6075086377 and 40.5060593623
GROUP BY business.id
ORDER BY distance) As temp
Group By category_id
ORDER BY distance
LIMIT 18
代码运行和执行都很好,但只需要一秒多一点的时间就可以完成(通常大约 1.1 秒)。但是,有人告诉我在某些浏览器中页面加载缓慢。我已经测试过这是多个浏览器和这些浏览器的多个版本,但从未发现问题。但是,我想如果我能缩短执行时间,这两种方式都会有所帮助。问题是我不知道我还能做些什么来减少执行时间。邮政编码表已经带有我认为很好的预设索引(并且包含我在查询中使用的列)。我也为业务表添加了索引,尽管我对它们不太了解。但我已确保包含 Where
中使用的字段至少一个子句,也许还有几个。
如果我需要为这个问题添加索引,请告诉我。如果您在查询本身中看到一些我可以改进的地方,请告诉我。
谢谢, 詹姆斯
编辑
business
的表结构表:
CREATE TABLE IF NOT EXISTS `business` (
`id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
`active` tinyint(3) unsigned NOT NULL,
`featured` enum('yes','no') NOT NULL DEFAULT 'yes',
`topFeatured` tinyint(1) unsigned NOT NULL DEFAULT '0',
`category_id` smallint(5) NOT NULL DEFAULT '0',
`listZip` varchar(12) NOT NULL,
`name` tinytext NOT NULL,
`address` tinytext NOT NULL,
`city` varchar(128) NOT NULL,
`state` varchar(32) NOT NULL DEFAULT '',
`zip` varchar(12) NOT NULL,
`phone` tinytext NOT NULL,
`alt_phone` tinytext NOT NULL,
`website` tinytext NOT NULL,
`logo` tinytext NOT NULL,
`index_logo` tinytext NOT NULL,
`large_image` tinytext NOT NULL,
`description` text NOT NULL,
`views` int(5) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `featured` (`featured`,`topFeatured`,`category_id`,`listZip`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=3085 ;
SQL fiddle
http://sqlfiddle.com/#!2/2e26ff/1
编辑 2014-03-26 09:09
我已经更新了我的查询,但较短的查询实际上每次执行的时间要长大约 0.2 秒。
Select * From (
SELECT Distinct business.id, business.name, business.large_image, business.logo, business.address, business.city, business.state, business.zip, business.phone, business.alt_phone, business.website, business.description, zipCodes.longitude, zipCodes.latitude, (3956 * 2 * ASIN ( SQRT (POWER(SIN((zipCodes.latitude - 39.056784)*pi()/180 / 2),2) + COS(zipCodes.latitude* pi()/180) * COS(39.056784 *pi()/180) * POWER(SIN((zipCodes.longitude - -84.343573) *pi()/180 / 2), 2) ) )) as distance
FROM business
INNER JOIN zipCodes ON (business.listZip = zipCodes.zipCode)
Where business.active = 1
And zipCodes.longitude between -86.2099407074 and -82.4772052926
And zipCodes.latitude between 37.6075086377 and 40.5060593623
GROUP BY business.category_id
HAVING distance <= '50'
ORDER BY distance
) As temp LIMIT 18
邮政编码数据库中已经有一个关于邮政编码、纬度和经度字段的索引,它们都在一个索引中,并且各有自己的索引。这就是这张 table 在购买时的样子。
我昨天更新了 listZip 数据类型以匹配邮政编码表的 zip 数据类型。
我确实取出了 GROUP BY business.id
并将其替换为 DISTINCT
, 但留下了 GROUP BY business.category_id
因为每个类别我只想要一个业务。
此外,一旦我将查询更改为使用 HAVING
,我就开始获得 0.2 秒的执行差异。 WHERE
中的子句而不是数学公式条款。我确实尝试使用 WHERE distance <= 50
在外部查询中,但这也没有加快任何速度。此外,使用 50 英里而不是 100 英里似乎也不会影响此特定查询。
感谢您到目前为止的所有建议。
最佳答案
将索引放在 zipCodes.longitude
和 zipCodes.latitude
上。这应该会有很大帮助。
有关详细信息,请参见此处。 http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
编辑 您需要单独在 longitude
上或以 longitude
开头的 zipCodes 表中的索引。在我看来,您应该尝试在
(longitude, latitude, zipCode)
为了最好的结果。
让zipCodes.zipCode和business.listingZip的数据类型相同,join效率会更高。如果这些数据类型不同,MySQL 会在连接时将一种数据类型转换为另一种数据类型,因此连接效率会很低。确保 business.listingZip 有一个索引。
您误用了 GROUP BY
。 (你可能是说 SELECT DISTINCT
吗?)除非你也使用像 MAX()
这样的聚合函数,否则这没有任何意义。以类似的方式,看看你是否可以摆脱SELECT business.*
中的 *
,而是给出所需列的列表。
100 英里是一个非常宽的搜索半径。缩小它一点以加快速度。
您计算了两次大圆距离。您当然可以重新构造查询以执行一次。
关于mysql - 我怎样才能加快这个 MySQL 查询的速度,找到最接近给定纬度/经度的位置?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22644416/