MySQL 的 ST_Distance() 适合计算同一平面坐标系下两个 geometry 的距离,返回单位取决于坐标系。经纬度是球面坐标,直接用 ST_Distance() 再除以固定系数(例如 0.0111)只是粗略近似,纬度越高误差越明显。

如果要计算两组经纬度之间的近似地球表面距离,更建议使用 ST_Distance_Sphere()。它返回单位为米的距离。注意 POINT(x, y)x 是经度(longitude),y 是纬度(latitude),不要写反。

下面示例没有强制设置 SRID。如果你的 MySQL 8 表字段使用了 SRID 4326 约束,查询中的目标点也要使用相同 SRID,例如 ST_GeomFromText('POINT(103.0 31.0)', 4326),避免不同 SRID 的 geometry 混用。

情况一:数据库中已有 POINT 类型的 location 字段

数据库:有 POINT 类型的 location 字段
实体类:有经纬度字段(double

1
2
3
4
5
6
7
8
9
10
11
SELECT
regionId,
provinceName,
cityName,
districtName,
lon,
lat,
ST_Distance_Sphere(location, POINT(103.0, 31.0)) AS distance_meters
FROM t_region
WHERE deleted = 0
ORDER BY distance_meters ASC;

情况二:数据库中只有经度和纬度字段

数据库:有经度、纬度字段,但是没有 POINT 字段
实体类:有经纬度字段(double

1
2
3
4
5
6
7
8
9
10
11
SELECT
regionId,
provinceName,
cityName,
districtName,
lon,
lat,
ST_Distance_Sphere(POINT(lon, lat), POINT(103.0, 31.0)) AS distance_meters
FROM t_region
WHERE deleted = 0
ORDER BY distance_meters ASC;

如果需要按公里展示,可以在查询结果外层除以 1000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SELECT
t.*,
ROUND(t.distance_meters / 1000, 3) AS distance_km
FROM (
SELECT
regionId,
provinceName,
cityName,
districtName,
lon,
lat,
ST_Distance_Sphere(POINT(lon, lat), POINT(103.0, 31.0)) AS distance_meters
FROM t_region
WHERE deleted = 0
) AS t
ORDER BY t.distance_meters ASC;

附:建表语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CREATE TABLE `t_region`  (
`id` bigint NOT NULL AUTO_INCREMENT,
`regionId` int NOT NULL DEFAULT 0 COMMENT '编号规则6位有符号整数,例如110000。',
`provinceName` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '省编号',
`cityName` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '市编号',
`districtName` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '区县编号',
`lon` double NOT NULL DEFAULT 0 COMMENT '经度',
`lat` double NOT NULL DEFAULT 0 COMMENT '纬度',
`location` point NOT NULL COMMENT '经纬度 POINT,POINT(x,y) 中 x 为经度、y 为纬度',
`parentRegionId` int NOT NULL DEFAULT 0 COMMENT '父节点',
`createTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`createUser` varchar(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT 'sys' COMMENT '创建人',
`updateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`updateUser` varchar(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT 'sys' COMMENT '修改人',
`deleted` int NOT NULL DEFAULT 0 COMMENT '删除状态 0 正常 1 已删除',
`version` int NOT NULL DEFAULT 0 COMMENT '修改序号',
PRIMARY KEY (`id`) USING BTREE,
SPATIAL INDEX `idx_location` (`location`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
1
2
3
4
5
6
7
INSERT INTO `t_region` VALUES (
1, 510000, '四川省', '', '',
104.081703, 30.65722,
ST_GeomFromText('POINT(104.081703 30.65722)'),
0, '2017-03-15 15:06:55', 'sys',
'2022-05-05 13:50:07', 'sys', 0, 0
);