我有两个非常大的表需要合并,所以我一直在尝试优化更新速度。我注意到部分使用PHP进行更新可以显著加快速度,因此我认为这意味着我可能没有正确地使用MySQL。
我已经简化了问题,以便缩小范围...
GRID_TABLE POSTCODE_TABLE idNo, lat, lng, nearestPostcode postcode, 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中获取nearestPostcode更新GRID_TABLE。
这个想法是使用索引缩小候选集合,然后使用“order by”子句在该集合中找到最近的一个,从而加快搜索速度。
这个 MySQL 更新需要 30 秒,但如果我在 PHP 中逐个更新每个 GRID_TABLE 行,则可以在眨眼之间完成。
“MySQL版本肯定比PHP版本更快吧?”这是MySQL的表格信息:
``` CREATE TABLE `grid_table` ( `idNo` INT(11) NOT NULL AUTO_INCREMENT, `lat` FLOAT(6,4) NOT NULL COMMENT 'latitude', `lng` FLOAT(6,4) NOT NULL COMMENT 'longitude', `nearestPostcode` CHAR(8) NOT NULL, PRIMARY KEY (`idNo`), INDEX `lat_lng` (`lat`, `lng`) ) ENGINE=MyISAM ROW_FORMAT=DEFAULT AUTO_INCREMENT=30047
CREATE TABLE `postcode_table` ( `postcode` CHAR(8) NOT NULL, `lat` FLOAT(6,4) NOT NULL COMMENT 'latitude', `lng` FLOAT(6,4) NOT NULL COMMENT 'longitude', PRIMARY KEY (`postcode`), INDEX `lat` (`lat`), INDEX `lng` (`lng`), INDEX `lat_lng` (`lat`, `lng`) ) ENGINE=MyISAM ROW_FORMAT=DEFAULT ```
MySQL导入文件在这里... https://docs.google.com/leaf?id=0B93lksnTC7_cM2Y2ZDk1Y2YtMGQ3Yy00OTIxLTk0ZDAtZmE2NmQ3YTc1ZWRm&hl=en (如果你运行UPDATE,将会添加10个最近的邮政编码)。
回答后更新...
我运行了这个...
我已经简化了问题,以便缩小范围...
GRID_TABLE POSTCODE_TABLE idNo, lat, lng, nearestPostcode postcode, 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中获取nearestPostcode更新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
)
这个想法是使用索引缩小候选集合,然后使用“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的表格信息:
``` CREATE TABLE `grid_table` ( `idNo` INT(11) NOT NULL AUTO_INCREMENT, `lat` FLOAT(6,4) NOT NULL COMMENT 'latitude', `lng` FLOAT(6,4) NOT NULL COMMENT 'longitude', `nearestPostcode` CHAR(8) NOT NULL, PRIMARY KEY (`idNo`), INDEX `lat_lng` (`lat`, `lng`) ) ENGINE=MyISAM ROW_FORMAT=DEFAULT AUTO_INCREMENT=30047
CREATE TABLE `postcode_table` ( `postcode` CHAR(8) NOT NULL, `lat` FLOAT(6,4) NOT NULL COMMENT 'latitude', `lng` FLOAT(6,4) NOT NULL COMMENT 'longitude', PRIMARY KEY (`postcode`), INDEX `lat` (`lat`), INDEX `lng` (`lng`), INDEX `lat_lng` (`lat`, `lng`) ) ENGINE=MyISAM ROW_FORMAT=DEFAULT ```
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; 使用filesort
移除 'order by' 子句 -> 速度没有变化。
简化 'where' 子句,移除 'lng',即
where lat between grid_table.lat - 0.0037 and grid_table.lat + 0.0037-> 更快:3秒而不是30秒。
使用空间列和索引(见下文)-> 更慢(190秒)。不确定我是否正确实现了这个。
ALTER TABLE `grid_table` ADD COLUMN `coords` POINT NOT NULL; update grid_table set coords = POINT(lat, lng); ALTER TABLE `grid_table` ADD SPATIAL INDEX `coords` (`coords`);
ALTER TABLE `postcode_table` ADD COLUMN `coords` POINT NOT NULL; update postcode_table set coords = POINT(lat, lng); ALTER TABLE `postcode_table` ADD SPATIAL INDEX `coords` (`coords`);
analyze table grid_table; optimize table grid_table; analyze table postcode_table; optimize table postcode_table;
更新grid_table表中的nearestPostcode字段为( 从postcode_table表中选择邮政编码 WHERE MBRContains(GeomFromText(concat( 'POLYGON((', 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ᵀᴹPOINT
)和空间索引,而不是存储lat
和lng
。参见 MySQL 空间扩展:http://dev.mysql.com/doc/refman/5.0/en/spatial-extensions.html - ypercubeᵀᴹ