如何将Oracle子类型转换为其父超类型?——以MDSYS.ST_POINT转MDSYS.ST_GEOMETRY为例
Oracle子类型转超类型:理解TREAT函数的作用与实际效果
你遇到的问题其实是对TREAT函数的作用有点误解——它并不会改变对象的实际类型,只是修改了表达式的声明类型,让子类型对象可以以超类型的“身份”被使用,比如满足类型兼容性要求、调用超类型的方法(如果未被子类型重写的话)。
为什么TREAT后ST_GeometryType()还是返回ST_POINT?
ST_GeometryType()方法返回的是对象的实际类型,这个类型在对象创建时就已经确定了(你用ST_POINT()构造的对象,实际类型永远是ST_POINT)。TREAT函数只是告诉Oracle编译器:“把这个对象当作它的超类型ST_GEOMETRY来处理”,但对象本身的本质并没有改变。
验证TREAT是否生效:查看声明类型
你可以用TYPE_NAME()函数来验证TREAT是否确实改变了表达式的声明类型,比如执行以下查询:
SELECT TYPE_NAME(mdsys.st_point(1, 2, 26917)) AS actual_object_type, TYPE_NAME(TREAT(mdsys.st_point(1, 2, 26917) AS mdsys.st_geometry)) AS declared_expression_type FROM dual;
这个查询会返回:
actual_object_type:ST_POINT(对象的实际类型,无法改变)declared_expression_type:ST_GEOMETRY(TREAT后的表达式声明类型,确实转换成功了)
什么时候需要用TREAT?
TREAT的核心价值在于类型兼容场景:
- 当你需要将子类型对象赋值给超类型变量时(虽然Oracle本身支持子类型到超类型的隐式转换,但显式使用TREAT可以让代码更清晰);
- 当你调用的方法/存储过程要求参数为超类型时,用TREAT可以明确转换声明类型,避免潜在的类型歧义;
- 如果你想调用超类型中未被子类型重写的方法,TREAT可以确保你调用的是超类型的实现(不过在空间类型中,大部分方法都被子类型重写了)。
如何得到实际类型为ST_GEOMETRY的对象?
如果你的需求是创建一个实际类型就是ST_GEOMETRY的对象(而不是子类型的实例),那你需要直接使用ST_GEOMETRY的构造方法,比如:
SELECT mdsys.st_geometry('POINT(1 2)', 26917) FROM dual;
这个对象的ST_GeometryType()会返回ST_GEOMETRY,因为它的实际类型就是超类型本身。
总结
- TREAT函数不改变对象的实际类型,只改变表达式的声明类型;
ST_GeometryType()返回的是对象的实际类型,所以不会因TREAT而改变;- 若需类型兼容(比如传递给超类型参数),TREAT已经满足需求;
- 若需实际类型为超类型的对象,直接用超类型的构造方法创建即可。
内容的提问来源于stack exchange,提问作者User1974
相关产品推荐
相关产品推荐

