您的数据不正确。你可以这样验证:
SQL> select sdo_geom.validate_geometry_with_context (building,0.005) from village;
SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT(BUILDING,0.005)
-------------------------------------------------------------------------------
13348 [Element <1>] [Ring <1>]
1 row selected.
那个错误意味着:
ORA-13348: polygon boundary is not closed
在 Oracle(实际上是所有存储系统)中,根据 OGC 规则,多边形必须闭合,即第一个顶点必须作为最后一个顶点重复。所以:
INSERT INTO village VALUES(2,'KircheV2', 4,
SDO_GEOMETRY(
2003,
NULL,
NULL,
SDO_ELEM_INFO_ARRAY(1,1003,1),
SDO_ORDINATE_ARRAY(100,100, 100,120, 120,100, 120,120, 100,100)
)
);
但是选择仍然无法返回任何结果。这是为什么呢?
SQL> select sdo_geom.validate_geometry_with_context (building,0.005) from village;
SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT(BUILDING,0.005)
-------------------------------------------------------------------------------
13349 [Element <1>] [Ring <1>][Edge <2>][Edge <4>]
1 row selected.
这个错误意味着:
ORA-13349: polygon boundary crosses itself
这是有道理的:顶点显然形成了蝴蝶形状:
100,100, 100,120, 120,100, 120,120, 100,100
假设你想形成一个简单的矩形,那么正确的形状是:
100,100, 100,120, 120,120, 120,100, 100,100
INSERT INTO village VALUES(2,'KircheV2', 4,
SDO_GEOMETRY(
2003,
NULL,
NULL,
SDO_ELEM_INFO_ARRAY(1,1003,1),
SDO_ORDINATE_ARRAY(100,100, 100,120, 120,120, 120,100, 100,100)
)
);
还是没有结果。为什么?
SQL> select sdo_geom.validate_geometry_with_context (building,0.005) from village;
SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT(BUILDING,0.005)
-------------------------------------------------------------------------------
13367 [Element <1>] [Ring <1>]
1 row selected.
这意味着:
ORA-13367: wrong orientation for interior/exterior rings
多边形中的环必须正确定向。外圈必须逆时针,内圈(孔)必须顺时针。所以你需要这样写:
100,100, 120,100, 120,120, 100,120, 100,100
INSERT INTO village VALUES(2,'KircheV2', 4,
SDO_GEOMETRY(
2003,
NULL,
NULL,
SDO_ELEM_INFO_ARRAY(1,1003,1),
SDO_ORDINATE_ARRAY(100,100, 120,100, 120,120, 100,120, 100,100)
)
);
还是没有结果!但那是因为您的查询表述不正确。 SDO_INSIDE(a,b) 查找完全在 B 内部的所有 A。在您的情况下,这就像询问访客内部的建筑物。显然你想要反过来,所以要么说:SDO_INSIDE(visitor, building) 或 SDO_CONTAINS (building,visitor),像这样:
SELECT * FROM visitors,village WHERE village.name like 'KircheV2' and SDO_INSIDE(visitors.POSITION,village.building) = 'TRUE';
SELECT * FROM visitors,village WHERE village.name like 'KircheV2' and SDO_CONTAINS(village.building,visitors.POSITION) = 'TRUE';
一些额外的 cmets:
你的例子纯属人为,我想?在现实生活中,您的多边形将来自某些 GIS 系统 - 例如您加载到数据库中的 ESRI shapefile,或从某些 GIS 工具捕获的形状。无论哪种方式,两者都会产生正确的形状,因为所有工具都适用于形状闭合和方向的 OGC 规则。如果不是,验证函数会告诉你错误,sdo_util.rectify_geometry 函数会纠正基本错误。
您还需要了解空间运算符(INSIDE 与 CONTAINS 等)及其效果。该文档解释了它们的含义。请注意,SDO_xxx 运算符集与 OGC 定义的 ST_xxx 函数略有不同。
您没有指定任何坐标系(SDO_SRID 为 NULL)。虽然这在您的人工示例中有效,但在现实生活中您应该始终使用正确的坐标系。特别是如果您的形状是大地测量的(长/纬度),您必须使用正确的 SRID:4326 来说明。这保证了所有计算都是在地球的椭圆体形状的范围内完成的。如果您要执行基于距离的查询或测量长度、距离或面积,这一点尤其重要。对投影数据使用正确的 SRID 同样重要。无论使用何种坐标系,它都可以让您执行查询:例如,查找 GPS 点所在的地块(在本地投影中)。
性能和索引。确保您有空间索引。虽然 Oracle 12.2 允许您在不定义索引的情况下进行查询,但以前的版本总是需要一个(如果不存在则失败)。如果该表甚至中等大,则对没有空间索引的表执行查询可能会非常慢。例如,以您的村庄和游客为例。假设您想从包含 1000 万访客的表格中找出特定建筑物中的所有访客。在访问者表上没有任何索引的情况下,数据库将需要将每个访问者与所选建筑物进行比较。这在 CPU 上会很昂贵(I/O 不是真正的问题)。
最后,正确编写查询很重要。在像F(a,b) 这样的运算符中,b 用于搜索a,因此b 应该是较小的集合。例如,查找此建筑物中的所有访客必须写为SDO_INSIDE(visitors, buildings)。反面(这个客户在哪栋楼?)写成SDO_CONTAINS(buildings, visitors)。