所以我有一个桌村:
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实际上应该在建筑物的中间,因此它应该在建筑物内部。
答案 0 :(得分:0)
您的数据不正确。您可以这样验证:
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';
其他一些评论:
我想,你的例子纯粹是人为的?在现实生活中,您的多边形将来自某些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)
。