一、Oracle几何空间数据对象和其他数据库的差异

和MySQL、PostgreSQL等数据库相比,Oracle数据库的地理几何空间更难学习和上手使用,我总结的一点原因是Oracle Spatial文档中阐述了太多的和其他数据库不通用的几何对象构建、查询方法,需要单独深入学习研究Oracle Spatial文档,知识碎片化严重,实现一个相同功能可能有N个功能相近的函数可以给你调用(茴香豆的茴字的N种写法),学习成本太高,还稍不注意就容易踩坑。

WKT(Well Known Text)是通用的几何空间对象的文本表示方法,相比于同样通用的GeoJSON文本,WKT更简单,易于理解也容易生成,重要的是几乎所有支持地理几何空间的数据库都支持WKT文本。

  • MySQLPostgreSQL中可用ST_GeomFromText('wkt',SRID)来构造几何对象
  • SQLServer中可用geometry::STGeomFromText('wkt',SRID)来构造几何对象
  • Oracle中可用SDO_GEOMETRY('wkt',SRID)来构造几何对象,但此处有坑

SQL语句中手写的单个文本字符串长度,在Oracle中也有限制,测试发现SQLPlus中最长的手写单个文本长度3000左右,SQL Developer中32767左右,超过了长度SQL就没法执行,直接报语法解析错误,这在其他数据库中是闻所未闻的。复杂的一个地理坐标边界图形的WKT可能有上百KB甚至上MB的超长文本,这在Oracle中单纯的使用SQL语句将会是一个很大的挑战。

本地测试所使用的数据库版本:Oracle Database 21c Express Edition Release 21.0.0.0.0

在线测试所使用的Live SQL版本:Live SQL 23.3.1, running Oracle Database 19c EE Extreme Perf - 19.17.0.0.0 (也有坑)

Oracle Spatial参考文档地址:https://docs.oracle.com/en/database/oracle/oracle-database/21/spatl/index.html

开源省市区坐标边界数据(可导入Oracle):https://github.com/xiangyuecn/AreaCity-JsSpider-StatsGov github可换成gitee

二、Oracle查询一个经纬度坐标是否在边界内部

2.1 查询条件

  • 边界几何图形:POLYGON ((53 20, 52 20, 52 23, 57 23, 57 20, 56 20, 56 22, 53 22, 53 20)),类似一个倒过来的凹字
  • 坐标点:POINT (55 21),这个点位于凹进去的里面,但不在凹字内部

查询这个坐标点是否在图形内部,结果应当是不在内部。

2.2 查询结果错误,似乎是仅做了MBR匹配

编写查询语句,这里直接给出相同一个图形的顺时针和逆时针两种写法:

declare
-- 定义坐标点
p SDO_GEOMETRY:=SDO_GEOMETRY('POINT (55 21)',4326);
-- 定义边界几何图形(顺时针),如果你的WKT是这种,那放到Oracle里面就惨了
g1 SDO_GEOMETRY:=SDO_GEOMETRY('POLYGON ((53 20, 52 20, 52 23, 57 23, 57 20, 56 20, 56 22, 53 22, 53 20))',4326);
-- 定义边界几何图形(逆时针),符合Oracle坐标顺序要求
g2 SDO_GEOMETRY:=SDO_GEOMETRY('POLYGON ((53 20, 53 22, 56 22, 56 20, 57 20, 57 23, 52 23, 52 20, 53 20))',4326);
v1 clob; v2 clob;
begin
-- 计算坐标点和2个边界的位置关系
select SDO_ANYINTERACT(g1,p),SDO_ANYINTERACT(g2,p) into v1,v2 from dual;
dbms_output.put_line('g1:'||v1 ||' '|| 'g2:'||v2); -- 检查2个边界是否有效(ST_IsValid)
dbms_output.put_line('g1 IsValid:'||SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT(g1,0.0000001));
dbms_output.put_line('g2 IsValid:'||SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT(g2,0.0000001));
end;

SQL Developer中执行后输出结果(和Live SQL中测试结果相同):

g1:TRUE  g2:FALSE
g1 IsValid:13367 [Element <1>] [Ring <1>]
g2 IsValid:TRUE

可以看到顺时针表示的边界WKT g1 居然包含了这个坐标点:g1:TRUE ;并且检查g1的有效性,显示图形是无效的。

尝试将坐标点改成POINT (55 10),此坐标完全在凹字外面,查询结果就是正确的,因此可以得出:边界g1和坐标p的位置计算只进行了外接矩形(MBR)的匹配,没有进行精确的计算,p坐标点在g1的MBR内部就直接返回了匹配,导致结果错误。

或者去掉SRID(删掉,4326,即SRID=NULL),查询结果也是正确的,这个就很奇葩了,代码正在以奇怪的方式正确运行。。。很有迷惑性。

相同的边界图形WKT 和 坐标点,在MySQL、PostgreSQL、SQLServer中测试均结果正确,也不存在图形无效的问题,仅仅是Oracle上有问题。

2.3 错误原因

这个错误结果困扰了一个多礼拜,SDO_ANYINTERACTSDO_RELATE的文档中没有找到关于图形坐标方向的说明,一度以为是提供了SRID的问题(只因SRID=NULL时查询结果又是正确的),还好后面在 SDO_ELEM_INFORecommendations for Loading and Validating Spatial Data文档中得到了确切的答案:

You should specify 3 only if you do not know if the simple polygon is exterior or interior, and you should then upgrade the table or layer to the current format using the SDO_MIGRATE.TO_CURRENT procedure, described in SDO_MIGRATE Package (Upgrading) .

.......

1005: exterior polygon ring (must be specified in counterclockwise order)

2005: interior polygon ring (must be specified in clockwise order)

For any geometries with the wrong orientation or an invalid ETYPE or GTYPE value, use SDO_MIGRATE.TO_CURRENT on these invalid geometries to fix them.

意思就是Oracle里面边界图形的外环必须逆时针的。但SDO_ANYINTERACT查询的时候,对于顺时针的错误图形,Oracle既不报错还返回错误结果,这就很离谱,这点的原因还没翻到文档说明。注:其他几个数据库并不要求环的方向顺序。

2.4 解决办法

根据文档提示,使用 SDO_MIGRATE.TO_CURRENT 方法对SDO_GEOMETRY构造出来的边界几何图形坐标的顺序进行处理,达到方向顺序要求

	-- 在原来的基础上,套一层TO_CURRENT()调用,提供一个dim数组即可
g1 SDO_GEOMETRY:=SDO_MIGRATE.TO_CURRENT(SDO_GEOMETRY('POLYGON ((53 20, 52 20, 52 23, 57 23, 57 20, 56 20, 56 22, 53 22, 53 20))',4326),SDO_DIM_ARRAY(SDO_DIM_ELEMENT('X', -180, 180, 0.0000001),SDO_DIM_ELEMENT('Y', -90, 90, 0.0000001)));

套了一层TO_CURRENT()后,对g1的查询就对了,图形的有效性也是正确的。

三、SQL中WKT超长文本在Oracle中如何编写

3.1 Oracle中执行含超长文本的SQL报错

修改并运行以下代码:

-- 字符串里面放50k的字符串,可浏览器控制台里面执行js得到长文本: new Array(50001).join("a")
select '这里放50000个字符......' from dual;

SQL Developer 中直接报错(Live SQL支持更差):

ORA-01704: 字符串文字太长
01704. 00000 - "string literal too long"
*Cause: The string literal is longer than 4000 characters.
*Action: Use a string literal of at most 4000 characters.
Longer values may only be entered using bind variables.

好家伙,第一次见限制SQL语句中字符串长度的,翻阅文档找到了 PL/SQL Program Limitssize of a string literal (bytes): 32767,其他数据库从来没有这种问题。Longer values may only be entered using bind variables.提示的这个解决办法目测是针对编程环境下的PreparedStatement,我都写SQL了,SQL文件里面绑个锤子的变量。

开头也说过了复杂地理坐标边界图形的WKT可能有上百KB甚至几MB的超长文本,其他数据库直接简单的通过类似insert tab values('100kb wkt')即可完成插入,现在Oracle由于这个书写字符串长度的限制,势必要额外编写非常规的代码。

3.2 使用CLOB无限拼接得到超长文本

Oracle的CLOB类型能放下4GB的文本,因此可以将超长的WKT文本切分成一小段一小段的文本,拼接进clob变量里面,避免单个字符串过长,即可解决这个问题。

declare txt clob:='';begin
txt:=txt||'POLYGON(( .... WKT切分的2KB字符串';
txt:=txt||'.... WKT切分的2KB字符串';
txt:=txt||'.... WKT切分的2KB字符串';
-- ...... 剩余的2KB字符串 dbms_output.put_line('txt: '||dbms_lob.getlength(txt)); -- 把超长WKT文本转成几何空间对象,修正坐标方向,插入数据库
-- INSERT INTO tableName(polygon) VALUES( SDO_MIGRATE.TO_CURRENT(SDO_GEOMETRY(txt,4326),SDO_DIM_ARRAY(SDO_DIM_ELEMENT('X', -180, 180, 0.0000001),SDO_DIM_ELEMENT('Y', -90, 90, 0.0000001))) );
end;
/

Oracle不同终端(SQLPlus、SQL Developer)里面SQL能书写的单个字符串长度混乱,3k-32k不等,因此长字符串采取2KB长度分段拼接进行支持,获得最大兼容;CLOB直接用||拼接即可,无需使用to_clobdbms_lob.append

注意:Live SQL中测试时,如果SQL语句的总长度超过32KB(非单个字符串),查询将会报错,或不压根返回结果,和SQLPlus、SQL Developer表现的完全不一样;可以理解为目前Live SQL中无法进行携带任何超长文本的测试。

3.3 导入开源的省市区坐标边界WKT数据

从开源库:AreaCity-JsSpider-StatsGov 里面的文档中获取到最新的省市区三级或乡镇级数据,有shp、geojson、sql格式支持,只需选择导出Oracle格式的SQL文件即可,在SQLPlus、或SQL Developer里面执行这个SQL文件即可完成导入:

@"D:/xxx/xxx.sql"; --改成实际的文件路径

SQL文件中的超长WKT文本按照上面2KB一段的长度进行了切分,使用CLOB拼接。此SQL文件结尾会自动创建索引,或者根据下文手动创建索引,建了索引后 SDO_ANYINTERACT 查询速度快100倍。

四、Oracle数据库的空间索引和查询返回WKT超长文本

4.1 给查询加速:Oracle数据库的空间索引

空间索引文档:Indexing and Querying Spatial Data,涉及user_sdo_geom_metadata这个表,这个表很重要,虽然里面只存了经纬度范围和SRID!

如果user_sdo_geom_metadata里面配置的SRID和导入到表里的数据SRID不一致,将会导致空间查询时直接报错。数据的SRID=NULL时,metadata里面的SRID也必须是NULL,其他SRID必须是一个存在的SRID值(通过 select * from MDSYS.CS_SRS order by srid 查询所有SRID)。

然后创建正常创建索引即可:

-- 先往user_sdo_geom_metadata里面插入配置数据
insert into user_sdo_geom_metadata(TABLE_NAME,COLUMN_NAME,DIMINFO,SRID)VALUES (upper('tableName'),upper('polygon'),SDO_DIM_ARRAY(SDO_DIM_ELEMENT('X', -180, 180, 0.0000001),SDO_DIM_ELEMENT('Y', -90, 90, 0.0000001)),NULL); -- SRID=NULL 或 一个具体值 -- 创建空间索引
create index indexName ON tableName(polygon) INDEXTYPE IS MDSYS.SPATIAL_INDEX_V2;

测试发现:修改表名会自动修改metadata里面的表名,删除表不会删除metadata里面的此表的相关配置。

表中空间数据量比较多的时候,加空间索引对查询速度的优化效果非常明细,上面导入的省市区三级的边界数据3600多条,不加索引时要7秒查询一个坐标,加了索引后只需0.06秒查询一次,查询性能提高了100倍。

4.2 如何查询返回SDO_GEOMETRY的WKT超长文本?

超长的WKT文本通过CLOB拼接的SQL语句插入数据库后,仅通过SQL查询语句从边界几何对象中读取出WKT文本似乎又是一个难题。

select SDO_GEOMETRY.GET_WKT(polygon) from tableName;

GET_WKT方法经常动不动就报错(偶尔又能正常查出来):

ORA-13199: wk buffer merge failure
ORA-06512: 在 "MDSYS.SDO_UTIL", line 857
ORA-06512: 在 "MDSYS.SDO_UTIL", line 896
ORA-06512: 在 "MDSYS.SDO_GEOMETRY", line 36

目前暂无办法稳定的查询出SDO_GEOMETRY的WKT超长文本,Oracle难搞哦。

注:以上问题(疑难杂症)MySQL、PostgreSQL、SQLServer 均无,这样说,你应该知道怎么选了吧=。=

【完】

Oracle数据库经纬度坐标查询优化与结果错误原因分析、SQL中WKT超长文本字符串处理的更多相关文章

  1. Oracle数据库排序后分页查询数据错误问题解决

    一.问题描述:根据更新时间倒序排序然后分页查询数据,但是点击分页操作的时候,会出现数据重复看似没有操作的情况 二.问题错误原因分析 分页查询的SQL语句: select * FROM (select ...

  2. undo丢失恢复异常恢复,运维DBA反映Oracle数据库无法启动报错ORA-01157 ORA-01110,分析原因为Oracle数据库坏块导致

    本文转自 惜纷飞 大师. 模拟基表事务未提交数据库crash,undo丢失恢复异常恢复,运维DBA反映Oracle数据库无法启动报错ORA-01157 ORA-01110,分析原因为Oracle数据库 ...

  3. 查询oracle数据库,返回的数据是乱码。 PL/SQL正常。

    查询oracle数据库,返回的数据是乱码. PL/SQL正常. 解决方案如下:

  4. loadFileSystems error & ExceptionUtils错误原因分析

    loadFileSystems error & ExceptionUtils错误原因分析 一见 2014/5/7 C/C++程序通过hdfs.h访问HDFS,运行时遇到如下错误,会是什么原因了 ...

  5. “undefined reference to JNI_GetCreatedJavaVM”和“File format not recognized”错误原因分析

    "undefined reference to JNI_GetCreatedJavaVM"和"File format not recognized"错误原因分析 ...

  6. 本机未装Oracle数据库时Navicat for Oracle 报错:Cannot create oci environment 原因分析及解决方案

    因为要更新数据库加个表,远程桌面又无法连接...所以就远程到另外一台电脑,然后用navicat通过内网修改目标数据库. 一直用着navicat操作数据库,所以很速度的弄好然后新建连接进入数据库. 然而 ...

  7. Oracle数据库版本号定期检视与升级的必要性分析

    目 录 ▇1.ORACLE数据库版本号知识 ▇2.看看自己的数据库还有没有支持服务 ▇3.看11.2.0.3版本号各PSU的公布时间与解决BUG数量列表 ▇4.看11.2.0.4版本号各PSU的公布时 ...

  8. SQL Server 磁盘请求超时的833错误原因分析以及解决

    本文出处:http://www.cnblogs.com/wy123/p/6984885.html 最近遇到一个SQL Server服务器响应极度缓慢,并且出现客户端请求报错的情况,在数据库中的erro ...

  9. 微信的redirect_uri参数错误原因分析

    我们可以根据微信的开发者文档  http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html 网页授权获取用户验证这一章来 ...

  10. Entity Framework 数据并发访问错误原因分析与系统架构优化

    博客地址 http://blog.csdn.net/foxdave 本文主要记录近两天针对项目发生的数据访问问题的分析研究过程与系统架构优化,我喜欢说通俗的白话,高手轻拍 1. 发现问题 系统新模块上 ...

随机推荐

  1. 2021-01-28:redis使用过程中的注意事项有哪些?

    福哥答案2021-01-28: [答案1:](https://italk.mashibing.com/question/detail/ques_00005101)1.使用key值前缀来作命名空间虽然说 ...

  2. 2021-10-02:单词搜索。给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。单词必须按照字母

    2021-10-02:单词搜索.给定一个 m x n 二维字符网格 board 和一个字符串单词 word .如果 word 存在于网格中,返回 true :否则,返回 false .单词必须按照字母 ...

  3. [NISACTF 2022]bingdundun~

    [NISACTF 2022]bingdundun~ 考点 文件上传.Phar://伪协议 一.题目 打开题目,发现是一道文件上传的题目,因为提示了可以压缩包,所以尝试直接上传zip文件,然后利用PHP ...

  4. vue+iview 动态调整Table的列顺序

    需求:因table列太多,且每个部门关注的信息不一样,拖来拖去不方便观看,客户想让Table列可以拖动,且可以保存顺序. 但是搞动态拖动太难了,我不会,于是改为操作columns数据 思路: < ...

  5. INFINI Labs 产品更新 | Console 新增数据比对、新增数据看板表格组件及支持下钻功能等

    INFINI Labs 产品更新啦~,本次产品版本更新包括 Gateway v1.14.0.Console v1.2.0.Easysearch v1.1.1 等,其中 Console 在上一版基础上做 ...

  6. 单例bean与类加载过程

    构造单例bean的方式有很多种,我们来看一下其中一种,饿汉式 public class Singleton1 implements Serializable { //1.构造函数私有 private ...

  7. 顶级生物信息学 RSS 订阅源

    早在 2018 年的时候我在"生信草堂"的公众号上写过一篇关于 RSS 的文章<使用 RSS 打造你的科研资讯头条>,介绍了关于 RSS 的一些内容和如何使用 inor ...

  8. App性能测试之iTest

    本文主要介绍下App性能测试工具iTest_V4.7的使用. 功能简介 1.监控Andorid系统(支持手机,平板,电视,车机等智能终端设备)以及应用app的cpu.内存.流量.电池.帧率.页面耗时等 ...

  9. 【AGC】Connect API报错submit failed的相关问题

    ​[关键字] AGC.Connect API.Publishing API [问题描述] 开发者反馈在使用AGC的Connect API提交发布时,报出了[cds]submit failed, add ...

  10. [ARM 汇编]进阶篇—异常处理与中断—2.4.2 ARM处理器的异常向量表

    异常向量表简介 在ARM架构中,异常向量表是一组固定位置的内存地址,它们包含了处理器在遇到异常时需要跳转到的处理程序的入口地址.每个异常类型都有一个对应的向量地址.当异常发生时,处理器会自动跳转到对应 ...