【发布时间】:2011-08-27 13:29:42
【问题描述】:
我有两个非常大的表要合并,所以我一直在尝试优化更新以提高速度。我注意到在 PHP 中进行部分更新会显着加快速度,所以我认为这意味着我没有正确执行 MySQL。
我已经简化了问题以尝试缩小范围...
GRID_TABLE POSTCODE_TABLE idNo, lat, lng, nearestPostcode 邮政编码, lat, lng ________________________________ _____________________ 1 57.1 -2.3 - AB12 3BA 56.3 -2.5 2 56.8 -1.9 - AB12 1YA 56.2 -2.3 . . . . . . (200 个条目)(35,000 个条目)我想使用纬度 (lat) 和经度 (lng) 使用 POSTCODE_TABLE 中最近的邮政编码来更新 GRID_TABLE,以找到离每个网格点最近的邮政编码...
update grid_table set nearestPostcode = (
select postcode from postcode_table
where lat > grid_table.lat -0.0037 and lat < grid_table.lat +0.0037
and lng > grid_table.lng -0.0068 and lng < grid_table.lng +0.0068
order by POW(lat - grid_table.lat,2) + POW((lng - grid_table.lng) *0.546,2)
limit 1
)
这个想法是“where”子句通过使用索引将集合缩小到几个候选者来加速搜索,然后“order by”子句在这个集合中找到最近的一个。
此 MySQL 更新需要 30 秒,但如果我改为在 PHP 中单独更新每个 GRID_TABLE 行,它会在眨眼间结束。
$queryStg = "select * from grid_table ;";
$sqlQuery1 = mysqli_query($mysqliLink, $queryStg);
while( $sqlRow = mysqli_fetch_assoc( $sqlQuery1 ) ) {
$idNo = $sqlRow['idNo'];
$lat = $sqlRow['lat'];
$lng = $sqlRow['lng'];
$queryStg = "
update grid_table
set nearestPostcode = (
SELECT postcode
FROM postcode_table
where
lat > " . ($lat - 0.0037) . " and
lat < " . ($lat + 0.0037) . " and
lng > " . ($lng - 0.0068) . " and
lng < " . ($lng + 0.0068) . "
ORDER BY
POW(lat - $lat, 2) +
POW((lng - $lng) * 0.546, 2)
ASC
limit 1
)
where idNo= $idNo;
";
$sqlQuery2 = mysqli_query($mysqliLink, $queryStg);
}
MySQL 版本肯定比 PHP 版本快吗?
这是表的 MySQL...
创建表`grid_table`( `idNo` INT(11) NOT NULL AUTO_INCREMENT, `lat` FLOAT(6,4) NOT NULL COMMENT '纬度', `lng` FLOAT(6,4) NOT NULL COMMENT '经度', `nearestPostcode` CHAR(8) NOT NULL, 主键(`idNo`), 索引`lat_lng`(`lat`,`lng`) ) 引擎=MyISAM ROW_FORMAT=默认 AUTO_INCREMENT=30047 创建表`postcode_table`( `postcode` CHAR(8) NOT NULL, `lat` FLOAT(6,4) NOT NULL COMMENT '纬度', `lng` FLOAT(6,4) NOT NULL COMMENT '经度', 主键(`邮政编码`), 索引`lat`(`lat`), 索引`lng`(`lng`), 索引`lat_lng`(`lat`,`lng`) ) 引擎=MyISAM ROW_FORMAT=默认MySQL 导入文件在这里... https://docs.google.com/leaf?id=0B93lksnTC7_cM2Y2ZDk1Y2YtMGQ3Yy00OTIxLTk0ZDAtZmE2NmQ3YTc1ZWRm&hl=en
(如果您运行 UPDATE,将添加 10 个最近的邮政编码)。
回答后更新...
我跑了这个...
explain extended
SELECT postcode FROM postcode_table
where lat > 57.0 and lat < 57.0074
and lng > -2.013 and lng < -2
ORDER BY POW(lat - 57.0, 2) + POW((lng - -2) * 0.546, 2) ASC
它返回了......
id,select_type,table,type,possible_keys,key,key_len,ref,rows,filtered,Extra 1,SIMPLE,postcode_table,range,lat,lng,lat_lng,lat_lng,8,NULL,65,100.00,使用where;使用文件排序删除“order by”的原因 -> 速度没有差异。
通过删除'lng'来简化'where'子句,即
grid_table.lat - 0.0037 和 grid_table.lat + 0.0037 之间的纬度 -> 更快:3 秒而不是 30 秒。使用空间列和索引(见下文)-> 慢得多(190 秒)。不确定我是否正确实现了这一点。
ALTER TABLE `grid_table` ADD COLUMN `coords` POINT NOT NULL; 更新 grid_table set coords = POINT(lat, lng); ALTER TABLE `grid_table` 添加空间索引 `coords` (`coords`); ALTER TABLE `postcode_table` ADD COLUMN `coords` POINT NOT NULL; 更新 postcode_table 设置 coords = POINT(lat, lng); ALTER TABLE `postcode_table` 添加空间索引 `coords` (`coords`); 分析表格grid_table; 优化表grid_table; 分析表 postcode_table; 优化表 postcode_table;更新 grid_table 设置最近的邮政编码 = ( 从 postcode_table 中选择邮政编码 WHERE MBRContains(GeomFromText(concat( '多边形((', grid_table.lat - 0.0037,'',grid_table.lng - 0.0068,',', grid_table.lat - 0.0037, ' ', grid_table.lng + 0.0068, ', ', grid_table.lat + 0.0037, ' ', grid_table.lng - 0.0068, ', ', grid_table.lat - 0.0037, ' ', grid_table.lng - 0.0068, '))')), postcode_table.coords) 按 POW(lat - grid_table.lat,2) + POW((lng - grid_table.lng) *0.546,2) 排序 限制 1 )
【问题讨论】:
-
需要在表
GRID_TABLE中的lng上的索引。查看查询计划(现在和添加此索引之后)。 -
嗨@ypercube。在 lng 上添加索引。速度没有差别。
-
查询在 PHP 中看起来非常快,可能是因为它正在被缓存。
-
嗨@John-Cartwright。你的意思是我读错了?
-
@spiderplant:我认为最好的解决方案是使用空间对象(
POINT)和空间索引,而不是存储lat和lng。请参阅 MySQL 空间扩展:dev.mysql.com/doc/refman/5.0/en/spatial-extensions.html
标签: php mysql query-optimization