所以我有一个餐桌村:
CREATE TABLE village (
building_id integer PRIMARY KEY,
name VARCHAR2(30),
visitors integer,
building SDO_GEOMETRY
);
还有餐桌访客:
create table visitors(
id integer,
position SDO_GEOMETRY
);
以下是插入内容:
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)
)
);
INSERT INTO visitors VALUES (1,
SDO_GEOMETRY(
2001,
NULL,
SDO_POINT_TYPE(110, 110, NULL),
NULL,
NULL
)
);
出于某种原因,当我尝试获取“KircheV2”内的所有访问者时,SQL 语句始终返回零记录:
SELECT * FROM visitors,village WHERE village.name like 'KircheV2' and (SDO_INSIDE(village.building,visitors.POSITION) = 'TRUE');
这背后的原因可能是什么?坐标110;110实际上应该在建筑物的中间,所以它应该在建筑物内部。
您的数据不正确。你可以这样验证:
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)
)
);
但是select仍然没有返回任何结果。这是为什么?
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';
一些补充意见:
我想你的例子纯粹是人为的?在现实生活中,您的多边形将来自某些 GIS 系统 - 例如加载到数据库中的 ESRI 形状文件,或从某些 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)
。 如果错误是
ORA-13348: polygon boundary is not closed
,可以使用修复
SQL> Update village set SDO_UTIL.RECTIFY_GEOMETRY(building,0.005) where building_id=2;
此功能有助于修复 Oracle 上损坏的几何图形。