为何调用ST_PointN(1)需Treat()转换MDSYS.ST_GEOMETRY为ST_LINESTRING
MDSYS.ST_GEOMETRY; Oracle 18c:
如下查询可正常运行,能够从MDSYS.ST_GEOMETRY对象中提取第一个点:
with cte as ( select treat(mdsys.st_geometry.from_wkt('LINESTRING(10 10, 20 20)',26917) as mdsys.st_linestring) as shape from dual ) select (shape).st_pointn(1) as first_point from cte
执行返回结果:
MDSYS.ST_POINT(MDSYS.SDO_GEOMETRY(2001, 26917, MDSYS.SDO_POINT_TYPE(10, 10, NULL), NULL, NULL))
核心疑问
调用ST_PointN()提取点位时,必须使用Treat()将ST_GEOMETRY超类型转换为ST_LINESTRING子类型才能正常执行。
如果移除代码中的Treat(... as ST_LINESTRING)转换逻辑,执行如下SQL:
with cte as ( select mdsys.st_geometry.from_wkt('LINESTRING(10 10, 20 20)',26917) as shape from dual ) select (shape).st_pointn(1) as first_point from cte
会直接触发报错:
ORA-00904: "MDSYS"."ST_GEOMETRY"."ST_POINTN": invalid identifier
需要明确移除Treat()函数后触发该错误的具体原因。
报错原因
Oracle Spatial的ST_GEOMETRY采用面向对象的继承类型体系设计,报错本质是静态类型校验不通过,核心逻辑如下:
ST_GEOMETRY是所有几何类型的根超类型,仅定义了所有几何对象通用的方法(比如获取WKT、获取SRID等),本身并没有实现ST_POINTN()方法。ST_POINTN()是线性几何子类型(ST_LINESTRING、ST_MULTILINESTRING等)才有的专属方法,点、多边形这类非线类型本身不需要支持“提取第N个节点”的逻辑,自然不会在超类型中定义该方法。mdsys.st_geometry.from_wkt()方法的声明返回值固定为ST_GEOMETRY超类型,SQL在编译阶段只会按照字段声明的静态类型校验方法是否合法,不会动态识别运行时对象的实际子类型。
TREAT()函数的作用就是在编译阶段显式声明将该超类型对象按ST_LINESTRING子类型处理,编译器才会去ST_LINESTRING的方法列表中检索ST_POINTN(),校验通过后正常执行。如果不加TREAT(),编译器只会在ST_GEOMETRY超类型的方法列表中查找,找不到对应方法就会抛出ORA-00904无效标识符错误。
这个逻辑和Java、C#等常见面向对象语言的强转规则完全一致:父类引用指向子类实例时,无法直接调用子类独有的方法,必须显式向下转型为对应子类型,才能调用子类专属方法,该校验在编译阶段就会完成,不会留到运行时判断。
内容的提问来源于stack exchange,提问作者User1974
相关产品推荐
相关产品推荐

