建表腳本
CREATE TABLE `z_gis` ( `id` bigint(20) NOT NULL, `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '姓名', `gis` geometry NOT NULL COMMENT '空間位置信息', `geohash` varchar(20) GENERATED ALWAYS AS (st_geohash(`gis`,8)) VIRTUAL, `item_id` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`), SPATIAL KEY `idx_gis` (`gis`), KEY `name` (`name`), KEY `idx_geohash` (`geohash`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='空間位置信息';
此時創建了一張位置信息表,每個人對應的經緯度都會以geometry類型存在表中,geohash字段是把坐標系分成很多小方格,然后將經緯度轉化成字符串,其原理可自行百度,在這里就不多說了。
geometry類型好像不能為null,所以建表時必須為not null。
插入表數據
insert into z_gis(id,name,gis,item_id) values (880000,'張三',st_geomfromtext('point(108.9498710632 34.2588125935)'), 100), (890000,'李四',st_geomfromtext('point(108.9465236664 34.2598766768)'), 101);
1. 查詢張三的經緯度信息
#496 張三 POINT(108.9465236664 34.2598766768) select id, name, st_astext(gis) gis from z_gis where name = '張三';
注:st_astext()函數是將geometry類型轉化為字符串
2. 修改張三的位置信息
update z_gis set gis = st_geomfromtext('point(108.9465236664 34.2598766768)') where name = '張三';
3. 查詢張三和李四之間的距離
注:st_distance_sphere()函數是計算兩點之間距離,所以傳兩個參數,都是geometry類型的,floor()函數是把計算出的距離取整,以米為單位。
-- 計算廈門到安溪的距離,單位米
select floor(st_distance_sphere( POINT(118.03394,24.48405), -- 廈門經緯度
POINT(118.18685,25.05544) -- 安溪經緯度
)) distance -- 查詢張三和李四之間的距離
select floor(st_distance_sphere( (select gis from z_gis where name= '張三'), gis )) distance from z_gis where name= '李四';
4. 查詢距離張三500米內的所有人
張三數據信息:(id, name, gis, geohash)(183 張三 POINT(120.259718 30.138463) wtme2wd3 53) -- POINT(120.259718,30.138463) 張三的經緯度 -- wtme2w 張三的geohash值 -- 183 張三的主鍵id
select name, --推薦用經緯度傳入,不要子查詢
-- floor(st_distance_sphere((SELECT gis FROM z_gis WHERE name = '張三'), gis)) distance,
floor(st_distance_sphere(point(120.259718,30.138463), gis)) distance, st_astext(gis) point from z_gis where geohash like 'wtme2w%' and st_distance_sphere(point(120.259718,30.138463), gis) < 5000 and id<>183;
前面說過geohash是把經緯度轉成字符串,建表的時候我定義讓它轉成8位字符,當兩個點離得越近時,它生成的geohash字符串前面相同的位數越多,所以我在這里先用left()截取前6位字符,前6位相同的誤差在±600米左右,然后模糊查詢,查出大概符合條件的數據,最后再精確比較,下面是geohash官方文檔對geohash長度和距離誤差的說明:
注意:用geohash 查詢會有邊界問題,所以查詢出來的結果又可能不准確,可以用程序(例如java代碼)先查出當前點周圍8個范圍的geohash值,然后再匹配這9個范圍的所有數據,這樣就解決了geohash 的邊界問題。