我已将表缩短为仅显示此查询的相关列。它需要两个表,查询需要很长时间,我们甚至没有进入400多万个查询和一个3000多万条记录的日志文件或一个包含100多万条记录的用户表。它让我重新考虑这个......我需要一些指导和建议:
这是表格:
// an abreviated users table
CREATE TABLE IF NOT EXISTS `users` (
`userid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`type` tinyint(1) NOT NULL COMMENT '1=biz, 2=apt, 3=condo, 4=home',
`distance` decimal(12,7) NOT NULL DEFAULT '1.0000000' COMMENT 'distance away to recv stuff',
`lat` decimal(12,7) NOT NULL,
`lon` decimal(12,7) NOT NULL,
`location` point NOT NULL COMMENT 'GeomFromText',
UNIQUE KEY `userid` (`userid`),
KEY `distance` (`distance`),
KEY `lat` (`lat`),
KEY `lon` (`lon`),
SPATIAL KEY `location` (`location`),
KEY `idx_user_type` (`type`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=501 ;
这是日志表。
// pretty much the full log table
CREATE TABLE IF NOT EXISTS `some_log` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'record num',
`userid` int(11) unsigned NOT NULL COMMENT 'user id receiving alert',
`trackid` bigint(20) unsigned NOT NULL COMMENT 'id of msg from message table',
`sent` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'when msg created',
PRIMARY KEY (`id`),
KEY `idx_msg_log_userid` (`userid`),
KEY `idx_msg_log_trackid` (`trackid`)
) ENGINE=MyISAM DEFAULT CHARSET=ascii COMMENT='log of all of some stuff' AUTO_INCREMENT=62232;
日志文件的一些示例数据
INSERT INTO `some_log` (`id`, `userid`, `trackid`, `sent`) VALUES
(1, 1, 4, '2011-07-14 18:14:25'),
(2, 2, 4, '2011-07-14 18:14:25'),
(3, 13, 6, '2011-07-25 23:05:54'),
(4, 44, 7, '2011-08-09 16:20:02'),
(5, 12, 17, '2011-08-16 07:35:01'),
(6, 43, 17, '2011-08-16 07:35:01'),
(7, 45, 17, '2011-08-16 07:35:01'),
(8, 12, 18, '2011-08-16 08:05:01'),
(9, 43, 18, '2011-08-16 08:05:01'),
(10, 45, 18, '2011-08-16 08:05:01');
这是查询。
// the query = $distance can be from 1/10th mile to 5 miles
SELECT *,(((acos(sin(($lat *pi()/180)) * sin((`lat`*pi()/180))+cos(($lat *pi()/180)) * cos((`lat`*pi()/180))* cos((($lon - `lon`)*pi()/180))))*180/pi())*60*1.1515) AS dist_x
FROM `users`
WHERE userid NOT IN (
SELECT userid
FROM some_log AS L
WHERE L.trackid='$trackid')
HAVING dist_x<='$distance' AND dist_x<=`distance`
ORDER BY dist_x ASC";
这是另一个查询。这个很慢。
// the above query is pretty quick given the test data
// this query is dog crap slow...
// we added in type and 4 is the most common type of user
SELECT *,(((acos(sin(($lat *pi()/180)) * sin((`lat`*pi()/180))+cos(($lat *pi()/180)) * cos((`lat`*pi()/180))* cos((($lon - `lon`)*pi()/180))))*180/pi())*60*1.1515) AS dist_x
FROM `users`
WHERE type='4' AND userid NOT IN (
SELECT userid
FROM some_log AS L
WHERE L.trackid='$trackid')
HAVING dist_x<='$distance' AND dist_x<=`distance`
ORDER BY dist_x ASC";
一个问题是:是否存在使用GeomFromText / POINT字段与纬度/经度搜索的半径/圆搜索?
另一个问题:有没有更好的方法来检查some_log表中这个$ userid已经有$ trackid的条目?
答案 0 :(得分:2)
忘记表格中的空间索引和空间列。他们不会帮助你进行lat-lon计算。
您可以使用lat索引从hasrsine计算中排除整组点对。利用这一事实:每纬度约有69个法定里程,60海里或111.045千米。 (这不完全正确,但它非常接近)。
因此,您可以为查询添加几个条件。这些将在你的纬度指数上添加一个范围扫描,这比你的HAVING
条件更快批次。
WHERE ....
AND $lat >= lat - ($distance/69.0)
AND $lat <= lat + ($distance/69.0)
...
这将排除所有距离太远的北方或太远的点,以包含在您的半身距离计算中。这将节省大量时间。
您也可以为lon执行此操作,但经度和距离之间的关系因纬度而异。经度线越接近极点越靠近。因此,公式比较棘手。
最后,float
是lat和lon非常好的数据类型。除非您是土木工程师,否则您不需要高精度十进制数据,除非您关心地球的真实形状是大地水准面,而不是球形。如果您关心这一点,您最好使用比半胱氨酸更精确的距离公式。但我们在谈论这里的厘米差异 - 停车场的大水坑,但对于商店发现者来说没有问题。