Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL GEOMETRY类型

全面解析MySQL中冷门数据类型GEOMETRY的适用场景及避坑指南

作者:detayun

很多人写了好几年SQL,甚至不知道MySQL还内置了一套空间数据类型,空间类型里,POINT 还算偶尔有人听过,但 GEOMETRY 作为所有空间类型的父类,就属于典型的不常用但很有意思的数据类型,下面小编就为大家简单介绍一下它吧

前言

平时开发业务系统,大家打交道最多的就是 INT、VARCHAR、DECIMAL、DATETIME 这些类型。很多人写了好几年SQL,甚至不知道MySQL还内置了一套空间数据类型。

空间类型里,POINT 还算偶尔有人听过,但 GEOMETRY 作为所有空间类型的父类,就属于典型的不常用但很有意思的数据类型。今天就专门讲讲它。

什么是 GEOMETRY?

MySQL从5.6版本开始完善空间数据支持,8.0做了大量优化。

GEOMETRY 是一个通用几何类型,它可以存放任意一种空间几何对象:

简单一句话:POINT 只能存坐标点;而 GEOMETRY 字段,同一个表里的不同行,可以一会儿存点、一会儿存线、一会儿存多边形。

这就是它和其他空间类型最大的区别。

适用场景一句话总结:当一行数据不确定是点/线/面,多种几何图形混存的时候,才选 GEOMETRY。

基础建表 & 写入示例

创建一张包含GEOMETRY字段的表:

CREATE TABLE geo_test (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    geo_data GEOMETRY NOT NULL,
    -- 可选,创建空间索引(空间查询必备)
    SPATIAL INDEX idx_geo (geo_data)
);

插入一条点数据:

INSERT INTO geo_test(name, geo_data)
VALUES ('重庆某点位', ST_GeomFromText('POINT(106.55 29.56)'));

再插入一个多边形(区域),同一张表同一个字段,直接换类型:

INSERT INTO geo_test(name, geo_data)
VALUES ('某区域多边形', ST_GeomFromText('POLYGON((106 29,107 29,107 30,106 30,106 29))'));

查询 GEOMETRY 字段

直接SELECT拿到的是二进制存储 格式,人看不懂,要用 ST_AsText() 转成可读WKT文本:

SELECT id,name, ST_AsText(geo_data) FROM geo_test;

判断类型:用 ST_GeometryType(),就能看出当前这条记录存的是点还是多边形

SELECT 
  id,
  name,
  ST_GeometryType(geo_data) AS geom_type,
  ST_AsText(geo_data) AS wkt
FROM geo_test;

常用空间函数举例:

什么时候该用 GEOMETRY?什么时候别用?

适合场景

一套表里面,同时存储点、线段、多边形多种地理图形,类型不固定。比如地图后台,既存点位标记,又存围栏区域。

通用空间数据存储,统一入库,上层业务再判断几何类型做不同渲染。

不推荐场景(绝大多数业务)

只存经纬度坐标点:直接用 POINT,性能更好,约束更强,不应该上GEOMETRY。

高并发、超大表:空间索引开销不小,很多业务会把地理计算交给Redis/GIS服务(PostGIS、高德/百度地图API),不在MySQL里做复杂空间运算。

简单业务后台,没有地图需求:完全没必要引入,增加团队理解成本。

容易踩的坑

1.旧版本MySQL坑:5.6之前空间类型支持很差,不要在老版本线上库随便玩。推荐MySQL8.0。

2.SPATIAL索引限制:如果字段允许NULL,部分版本空间索引会有问题;且只有GEOMETRY、POINT这类空间类型支持空间索引,普通字段不行。

3.坐标系问题:默认是笛卡尔平面坐标,不是地理球面坐标!

4.如果要做真实地球经纬度距离计算,需要指定SRID(8.0支持SRID,推荐SRID=4326,也就是WGS84,GPS通用坐标系)。

-- 带SRID的写法
ST_GeomFromText('POINT(106.55 29.56)',4326)

不要拿GEOMETRY替代VARCHAR存WKT字符串!

很多新手图省事直接把WKT文本放进VARCHAR,就失去了空间索引、空间函数能力;反过来,如果你不需要空间计算,单纯存文本,也没必要用GEOMETRY。

和 POINT 的直观对比

类型可存储几何对象使用场景
POINT仅坐标点经纬度点位,最常用的空间类型
GEOMETRY点、线、多边形、几何集合全部支持多种几何类型混存,通用GIS数据

实战:用ST_Contains判断坐标点是否在多边形围栏内

做地图相关业务,电子围栏是非常经典的需求:比如打卡校验、配送范围、区域告警,核心逻辑就是给定一个经纬度坐标,判断这个点是不是落在划定的多边形区域里面。

很多同学第一想法是在代码里写算法判断点是否在多边形内,逻辑复杂,还要自己处理边界;其实MySQL内置空间函数 ST_Contains 一行SQL就能搞定。

注意:本文使用 GEOMETRY 类型存储多边形围栏,就是上文聊到的冷门空间类型。

核心原理

ST_Contains(geometryA, geometryB)

含义:几何A是否包含几何B

放到围栏场景:

小提醒:参数顺序不能写反!ST_Contains(多边形, 点),写反结果永远是0。

准备表结构

我们建一张围栏表,用GEOMETRY存放多边形,同时设置SRID=4326(WGS84,GPS/高德百度通用坐标系)。

CREATE TABLE fence (
    id INT PRIMARY KEY AUTO_INCREMENT,
    fence_name VARCHAR(100) COMMENT '围栏名称',
    fence_poly GEOMETRY NOT NULL SRID 4326 COMMENT '多边形围栏',
    SPATIAL INDEX idx_fence (fence_poly) COMMENT '空间索引,提升查询性能'
);

SRID=4326 很关键,MySQL8.0支持绑定坐标系,避免平面坐标带来距离、包含关系计算偏差。

插入多边形围栏

WKT格式的多边形语法:POLYGON((lng1 lat1, lng2 lat2, lng3 lat3, lng1 lat1))

多边形必须首尾坐标闭合,起点和终点一致,否则会报错。

我们构造一个简易矩形区域,模拟一块业务围栏:

INSERT INTO fence(fence_name, fence_poly)
VALUES ('重庆渝北测试围栏',
ST_GeomFromText('POLYGON((106.50 29.55,106.60 29.55,106.60 29.62,106.50 29.62,106.50 29.55))',4326)
);

使用ST_Contains做点位判断

现在检测点位 POINT(106.55 29.58),这个坐标在上面的矩形围栏内部。

SELECT
    id,
    fence_name,
    ST_Contains(fence_poly, ST_GeomFromText('POINT(106.55 29.58)',4326)) AS is_inside
FROM fence;

执行结果:is_inside = 1,代表点在围栏内。

再测试一个围栏外的点 POINT(106.70 29.70):

SELECT
    id,
    fence_name,
    ST_Contains(fence_poly, ST_GeomFromText('POINT(106.70 29.70)',4326)) AS is_inside
FROM fence;

结果:is_inside = 0,点在围栏外面。

常用扩展写法

1. 只查询包含该点位的围栏(业务最常用)

SELECT *
FROM fence
WHERE ST_Contains(fence_poly, ST_GeomFromText('POINT(106.55 29.58)',4326)) = 1;

加上空间索引,当围栏表量大的时候,查询性能会明显提升。

2. 同时返回可读WKT,方便调试

SELECT
    id,
    fence_name,
    ST_AsText(fence_poly) AS polygon_wkt,
    ST_AsText(ST_GeomFromText('POINT(106.55 29.58)',4326)) AS point_wkt,
    ST_Contains(fence_poly, ST_GeomFromText('POINT(106.55 29.58)',4326)) AS is_inside
FROM fence;

写在最后

GEOMETRY 在普通业务开发里确实属于冷门选手。绝大多数项目,存经纬度用POINT,甚至直接两个DECIMAL字段分开存lng/lat就够用。

但如果你做地图 后台、电子围栏、地理信息相关系统,它就有发挥空间。不过实际生产中,很多GIS项目会单独使用PostgreSQL+PostGIS,而不是重度依赖MySQL的空间能力。

简单总结:知道有这个东西,看懂它能干啥;不到多种几何混存的场景,尽量不用。

到此这篇关于全面解析MySQL中冷门数据类型GEOMETRY的适用场景及避坑指南的文章就介绍到这了,更多相关MySQL GEOMETRY类型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文