MySQL空间函数详解,MySQL记录经纬度并进行计算

文章目录

一、空间数据类型

在 MySQL 空间数据类型中,GEOMETRY 是所有空间类型的"基类",其余(POINT、LINESTRING 等)都是 GEOMETRY 的具体子类,用于存储不同形态的地理/几何信息(如单个点、线、区域、多个点集合等)。这些类型的核心作用是结构化存储空间数据,配合空间函数(如距离计算、范围判断)和空间索引,实现高效的地理/几何运算。

1、GEOMETRY:核心基类

作用:

所有空间数据类型的父类型(抽象基类),本身不直接存储具体的空间形态,而是用于:

1.兼容所有子类(如 POINT、POLYGON 都可赋值给 GEOMETRY 类型字段);

2.统一处理混合类型的空间数据(如查询时返回任意类型的空间结果)。

关键特点:

不能直接用 INSERT 插入数据(无具体形态),需通过子类的 WKT/函数生成后赋值;

支持所有空间函数(如 ST_SRID()ST_AsText()),可接收任意子类的空间对象。

适用场景:

需存储"不确定形态"的空间数据(如同时存储点、线、面);

通用化的空间数据接口(如函数参数、存储过程返回值)。

sql 复制代码
-- 定义 GEOMETRY 类型字段(兼容所有空间类型)
CREATE TABLE spatial_data (
  id INT PRIMARY KEY AUTO_INCREMENT,
  geom GEOMETRY NOT NULL SRID 4326 COMMENT '兼容任意空间类型'
);

-- 插入 POINT 类型数据(自动兼容 GEOMETRY 字段)
INSERT INTO spatial_data (geom) VALUES (ST_MakePoint(116.4, 39.9));

-- 插入 POLYGON 类型数据(同样兼容)
INSERT INTO spatial_data (geom) VALUES (
  ST_GeomFromText('POLYGON((116.3 39.8, 116.5 39.8, 116.5 40.0, 116.3 40.0, 116.3 39.8))', 4326)
);

2、POINT:单个点(重要)

作用:

存储二维平面/地球表面的单个坐标点(核心用于经纬度、具体位置)。

关键特点:

由一对坐标 (X, Y) 组成(地理场景:X=经度,Y=纬度);

必须指定 SRID(如 4326 对应 WGS84 经纬度);

是经纬度存储的最优类型(之前对话重点讲过)。

实际场景:

存储 POI 坐标(如商场、景点、地址);

设备定位点(如手机GPS、车辆定位)。

sql 复制代码
-- WKT 格式:POINT(X Y)(经度在前,纬度在后  貌似我的版本正好是反过来的?很奇怪)
ST_GeomFromText('POINT(116.4042 39.9153)', 4326)

-- 简化函数(MySQL 8.0.12+):ST_MakePoint(X, Y)(自动继承字段 SRID)
ST_MakePoint(116.4042, 39.9153)

3、LINESTRING:线串(折线)

作用:

存储由多个有序点连接而成的连续线段(可理解为"折线",非曲线)。

关键特点:

由 2 个及以上 POINT 组成(点的顺序决定线的走向);

点之间是直线连接,可自交(但通常用于无自交的连续线);

坐标单位与 SRID 一致(4326 为度,3857 为米)。

实际场景:

道路、铁路、航线(如公交路线、飞机航线);

河流、管道、边界线(如省界的一段);

轨迹(如跑步路线、车辆行驶轨迹)。

sql 复制代码
-- WKT 格式:LINESTRING(点1 点2 点3 ...)(点之间用空格分隔)
-- 示例:北京到天津的航线(简化为3个途经点)
ST_GeomFromText('LINESTRING(116.4042 39.9153, 116.8 39.95, 117.2 39.13)', 4326)

4、POLYGON:多边形(闭合区域)

作用:

存储由闭合线串围成的区域(可包含"孔洞"),用于表示"面状"地理范围。

关键特点:

核心是"闭合线串":外边界的首尾点必须完全相同(否则 MySQL 会自动补全,但不推荐);

支持"孔洞":可包含多个线串,第一个是外边界,后续是内边界(孔洞,如区域内的湖泊);

点的顺序:外边界按"顺时针"或"逆时针"排列,内边界(孔洞)顺序相反(避免交叉)。

实际场景:

行政区域(如城市、区县、省份范围);

地块、园区(如工业园区、校园范围);

地理围栏(如"电子围栏内的设备报警")。

sql 复制代码
-- 1. 简单多边形(无孔洞):北京市东城区范围(简化)
ST_GeomFromText('POLYGON((116.35 39.88, 116.45 39.88, 116.45 39.95, 116.35 39.95, 116.35 39.88))', 4326)

-- 2. 带孔洞的多边形(区域内有湖泊)
ST_GeomFromText('POLYGON(
  (116.35 39.88, 116.45 39.88, 116.45 39.95, 116.35 39.95, 116.35 39.88),  -- 外边界
  (116.38 39.90, 116.42 39.90, 116.42 39.93, 116.38 39.93, 116.38 39.90)   -- 内边界(孔洞)
)', 4326)

5、复合类型(MULTI 开头):多个同类型几何对象

用于存储多个结构相同的空间对象(如多个点、多条线、多个多边形),且这些对象之间相互独立、不重叠(逻辑上是"集合")。

6、MULTIPOINT:多点集合

作用:

存储多个独立的 POINT(无关联,不连接)。

关键特点:

多个点之间无顺序要求,互不连接;

适合存储"一组离散的点"(无需每条记录存一个 POINT)。

实际场景:

多个POI集合(如同一品牌的所有门店坐标);

传感器部署点、监测点(如多个空气质量监测站)。

sql 复制代码
-- 示例:某城市的多个公交站点坐标
ST_GeomFromText('MULTIPOINT((116.40 39.91), (116.41 39.92), (116.42 39.90), (116.39 39.93))', 4326)

7、MULTILINESTRING:多线集合

作用:

存储多条独立的 LINESTRING(互不连接,无顺序要求)。

关键特点:

每条线串都是独立的(如多条不相交的道路);

避免重复创建多条 LINESTRING 记录,简化数据管理。

实际场景:

多条道路、水系(如一个区域内的所有河流);

多条航线、轨迹(如某航空公司的多条航线)。

sql 复制代码
-- 示例:某城市的3条主要道路
ST_GeomFromText('MULTILINESTRING(
  (116.35 39.88, 116.45 39.88),  -- 道路1(东西向)
  (116.40 39.88, 116.40 39.95),  -- 道路2(南北向)
  (116.38 39.90, 116.42 39.93)   -- 道路3(斜向)
)', 4326)

8、MULTIPOLYGON:多面集合

作用:

存储多个独立的 POLYGON(互不重叠、不相交)。

关键特点:

每个多边形都是独立的(如多个城市、多个地块);

适合存储"一组区域"(如省份包含的所有区县)。

实际场景:

行政区域集合(如省份、国家包含的所有下级区域);

多个地块、园区(如一个开发区内的多个工厂地块)。

sql 复制代码
-- 示例:京津冀地区的3个主要城市范围(简化)
ST_GeomFromText('MULTIPOLYGON(
  -- 北京
  ((116.3 39.8, 116.6 39.8, 116.6 40.1, 116.3 40.1, 116.3 39.8)),
  -- 天津
  ((117.1 39.0, 117.4 39.0, 117.4 39.3, 117.1 39.3, 117.1 39.0)),
  -- 石家庄
  ((114.2 38.0, 114.6 38.0, 114.6 38.4, 114.2 38.4, 114.2 38.0))
)', 4326)

9、混合集合:GEOMETRYCOLLECTION

作用:

存储多种不同类型的空间对象(如 POINT + LINESTRING + POLYGON),是最灵活的空间类型(但使用频率较低)。

关键特点:

可包含任意空间类型(单个或复合类型),如"一个点 + 一条线 + 一个多边形";

无类型限制,但可读性和查询效率较低(不推荐频繁使用);

需确保所有对象的 SRID 一致(否则空间函数会报错)。

实际场景:

复杂地理数据集(如一个区域内的所有地理要素:点、线、面);

临时数据存储(如批量导入的混合类型空间数据)。

sql 复制代码
-- 示例:某区域的"景点(点)+ 游览路线(线)+ 景区范围(面)"
ST_GeomFromText('GEOMETRYCOLLECTION(
  POINT(116.4042 39.9153),  -- 天安门(点)
  LINESTRING(116.40 39.91, 116.41 39.92),  -- 游览路线(线)
  POLYGON((116.39 39.90, 116.42 39.90, 116.42 39.93, 116.39 39.93, 116.39 39.90))  -- 景区范围(面)
)', 4326)

二、使用

1、POINT相关

(1)建表与索引

sql 复制代码
CREATE TABLE `poi` (
  `id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
  `name` VARCHAR(100) NOT NULL COMMENT '地点名称',
  `location` POINT SRID 4326 NOT NULL COMMENT '经纬度(POINT类型,WGS84坐标系)',
  `address` VARCHAR(255) COMMENT '详细地址',
  -- 创建空间索引(加速空间查询)
  SPATIAL INDEX `idx_spatial_location` (`location`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='POI地点表';

(2)ST_PointFromText():从 WKT 构造点(推荐,显式指定 SRID)

sql 复制代码
-- 语法:ST_PointFromText('POINT(纬度 经度)', SRID)
INSERT INTO poi (name, location, address)
VALUES (
  '天安门',
  ST_PointFromText('POINT(39.914885 116.403874)', 4326), -- 经度116.403874,纬度39.914885
  '北京市东城区东长安街'
), (
  '上海外滩',
  ST_PointFromText('POINT(31.235924 121.490171)', 4326), -- 经度121.490171,纬度31.235924
  '上海市黄浦区中山东一路'
);

(3)ST_GeomFromText():通用几何对象创建函数

sql 复制代码
INSERT INTO poi (name, location, address)
VALUES (
  '广州塔',
  ST_GeomFromText('POINT(23.113500 113.330430)', 4326),
  '广州市海珠区阅江西路222号'
);

(4)ST_SRID():通过对象创建

sql 复制代码
INSERT INTO poi (name, location, address)
VALUES (
  '深圳市民中心',
  ST_SRID(Point(114.057865, 22.543096), 4326), -- Point(经度, 纬度)
  '深圳市福田区福中三路'
);

(5)ST_X、ST_Y查询x、y坐标(不适用经纬度)

sql 复制代码
mysql> SELECT ST_X(Point(56.7, 53.34));
+--------------------------+
| ST_X(Point(56.7, 53.34)) |
+--------------------------+
|                     56.7 |
+--------------------------+
sql 复制代码
mysql> SELECT ST_Y(Point(56.7, 53.34));
+--------------------------+
| ST_Y(Point(56.7, 53.34)) |
+--------------------------+
|                    53.34 |
+--------------------------+

(6)ST_AsText(), ST_AsWKT():转换为字符串、WKT格式

sql 复制代码
mysql> SET @g = 'LineString(1 1,2 2,3 3)';
mysql> SELECT ST_AsText(ST_GeomFromText(@g));
+--------------------------------+
| ST_AsText(ST_GeomFromText(@g)) |
+--------------------------------+
| LINESTRING(1 1,2 2,3 3)        |
+--------------------------------+

(7)ST_Longitude()、ST_Latitude():查询经纬度

sql 复制代码
SELECT
  id,
  name,
  ST_Longitude(location) AS longitude, -- 提取经度
  ST_Latitude(location) AS latitude,  -- 提取纬度
  address
FROM poi;

(8)ST_Distance():点的平面距离(不适用经纬度)

sql 复制代码
mysql> SET @g1 = ST_GeomFromText('POINT(1 1)');
mysql> SET @g2 = ST_GeomFromText('POINT(2 2)');
mysql> SELECT ST_Distance(@g1, @g2);
+-----------------------+
| ST_Distance(@g1, @g2) |
+-----------------------+
|    1.4142135623730951 |
+-----------------------+

mysql> SET @g1 = ST_GeomFromText('POINT(1 1)', 4326);
mysql> SET @g2 = ST_GeomFromText('POINT(2 2)', 4326);
mysql> SELECT ST_Distance(@g1, @g2);
+-----------------------+
| ST_Distance(@g1, @g2) |
+-----------------------+
|     156874.3859490455 |
+-----------------------+
mysql> SELECT ST_Distance(@g1, @g2, 'metre');
+--------------------------------+
| ST_Distance(@g1, @g2, 'metre') |
+--------------------------------+
|              156874.3859490455 |
+--------------------------------+
mysql> SELECT ST_Distance(@g1, @g2, 'foot');
+-------------------------------+
| ST_Distance(@g1, @g2, 'foot') |
+-------------------------------+
|             514679.7439273146 |
+-------------------------------+

(9)ST_Distance_Sphere():基于球体(地球)计算两点直线距离

sql 复制代码
SELECT
  ST_Distance_Sphere ( ST_PointFromText ( 'POINT(39.914885 116.403874)', 4326 ), ST_PointFromText ( 'POINT(31.235924 121.490171)', 4326 ) ) AS distance_m;

2、实战

(1)查询范围内的点

sql 复制代码
-- 步骤1:构造目标点(天安门经纬度)
-- 步骤2:计算每个POI到目标点的距离(米)
-- 步骤3:筛选距离≤5000米的结果,按距离升序排序
SELECT
  id,
  name,
  address,
  ST_Longitude(location) AS longitude,
  ST_Latitude(location) AS latitude,
  -- 计算距离(米),保留2位小数
  ROUND(ST_Distance_Sphere(
    location,
    ST_PointFromText('POINT(39.914883 116.403873)', 4326) -- 目标点:天安门
  ), 2) AS distance_m
FROM poi
WHERE
  -- 先通过经纬度范围粗筛(减少计算量,优化性能)
  ST_Longitude(location) BETWEEN 116.403872 - 0.05 AND 116.403874 + 0.05 -- 经度范围(≈5.5公里)
  AND ST_Latitude(location) BETWEEN 39.914883 - 0.05 AND 39.914885 + 0.05 -- 纬度范围(≈5.5公里)
  -- 再精确筛选5公里内
  AND ST_Distance_Sphere(
    location,
    ST_PointFromText('POINT(39.914885 116.403874)', 4326)
  ) < 5000
ORDER BY distance_m ASC; -- 按距离从近到远排序

参考资料

https://dev.mysql.com/doc/refman/8.0/en/spatial-types.html
https://dev.mysql.com/doc/refman/8.0/en/spatial-function-reference.html

相关推荐
星环处相逢1 小时前
MySQL数据库管理从入门到精通:全流程实操指南
数据库·mysql
.豆鲨包1 小时前
【Android】Binder机制浅析
android·binder
计算机毕设小月哥1 小时前
【Hadoop+Spark+python毕设】中风患者数据可视化分析系统、计算机毕业设计、包括数据爬取、Spark、数据分析、数据可视化、Hadoop
后端·python·mysql
计算机毕设匠心工作室1 小时前
【python大数据毕设实战】强迫症特征与影响因素数据分析系统、Hadoop、计算机毕业设计、包括数据爬取、数据分析、数据可视化、机器学习、实战教学
后端·python·mysql
h***04771 小时前
SpringBoot集成Flink-CDC,实现对数据库数据的监听
数据库·spring boot·flink
源来猿往1 小时前
redis-架构解析
数据库·redis·缓存
河南博为智能科技有限公司1 小时前
高集成度国产八串口联网服务器:工业级多设备联网解决方案
大数据·运维·服务器·数据库·人工智能·物联网
Wang's Blog2 小时前
MongoDB小课堂: 深度诊断与优化——响应时间、内存压力及连接数故障全方位解决指南
数据库·mongodb
z***02602 小时前
MySQL--》如何通过选择合适的存储引擎提高查询效率?
数据库·mysql·adb