文章目录
- 一、空间数据类型
-
- 1、GEOMETRY:核心基类
- 2、POINT:单个点(重要)
- 3、LINESTRING:线串(折线)
- 4、POLYGON:多边形(闭合区域)
- [5、复合类型(MULTI 开头):多个同类型几何对象](#5、复合类型(MULTI 开头):多个同类型几何对象)
- 6、MULTIPOINT:多点集合
- 7、MULTILINESTRING:多线集合
- 8、MULTIPOLYGON:多面集合
- 9、混合集合:GEOMETRYCOLLECTION
- 二、使用
-
- 1、POINT相关
-
- (1)建表与索引
- [(2)ST_PointFromText():从 WKT 构造点(推荐,显式指定 SRID)](#(2)ST_PointFromText():从 WKT 构造点(推荐,显式指定 SRID))
- (3)ST_GeomFromText():通用几何对象创建函数
- (4)ST_SRID():通过对象创建
- (5)ST_X、ST_Y查询x、y坐标(不适用经纬度)
- [(6)ST_AsText(), ST_AsWKT():转换为字符串、WKT格式](#(6)ST_AsText(), ST_AsWKT():转换为字符串、WKT格式)
- (7)ST_Longitude()、ST_Latitude():查询经纬度
- (8)ST_Distance():点的平面距离(不适用经纬度)
- (9)ST_Distance_Sphere():基于球体(地球)计算两点直线距离
- 2、实战
- 参考资料
一、空间数据类型
在 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