我的数据库中有一个邮政编码表,它与业务表一起使用,用于查找与指定邮政编码最接近的符合某些条件的企业。我要做的第一件事是获取经纬度因为它在页面上的几个地方使用。我使用:
$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小提琴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
在邮政编码数据库中已经有一个关于邮政编码、纬度和经度字段的索引,它们都在一个索引中,每个都有自己的索引。这张桌子刚买的时候就是这样的。
昨天我已经更新了listZip数据类型以匹配邮政编码表的zip数据类型。
我确实取出了GROUP BY business.id
并用DISTINCT
代替它,但留下了GROUP BY business.category_id
,因为我每个类别只想要一个业务。
同样,当我将查询更改为使用HAVING
子句而不是WHERE
子句中的数学公式时,我开始得到0.2秒的执行差异。我确实尝试在外部查询中使用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的数据类型。邮编和商业。listingZip相同,因此连接将更有效。如果这些数据类型不同,MySQL在进行连接时将一种数据类型转换为另一种数据类型,因此连接将是低效的。确保生意。listingZip有一个索引。
你在滥用GROUP BY
。(你是说SELECT DISTINCT
吗?)这是没有意义的,除非您还使用像MAX()
这样的聚合函数。以类似的方式,看看您是否可以在SELECT business.*
中去掉*
,而是给出您需要的列的列表。
100英里是一个非常宽的搜索半径。把它收窄一点,加快速度。
你计算了两次大圆距离。您当然可以重铸查询来完成一次。