【问题标题】:SDO_INSIDE returns zero recordsSDO_INSIDE 返回零记录
【发布时间】:2018-09-15 15:00:31
【问题描述】:

所以我有一个餐桌村:

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 实际上应该在建筑物的中间,所以它应该在建筑物内部。

【问题讨论】:

  • 请更新您的标签。它是 MySQL 或 Oracle。它们有很大的不同。

标签: oracle oracle-spatial


【解决方案1】:

您的数据不正确。你可以这样验证:

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:

  1. 你的例子纯属人为,我想?在现实生活中,您的多边形将来自某些 GIS 系统 - 例如您加载到数据库中的 ESRI shapefile,或从某些 GIS 工具捕获的形状。无论哪种方式,两者都会产生正确的形状,因为所有工具都适用于形状闭合和方向的 OGC 规则。如果不是,验证函数会告诉你错误,sdo_util.rectify_geometry 函数会纠正基本错误。

  2. 您还需要了解空间运算符(INSIDE 与 CONTAINS 等)及其效果。该文档解释了它们的含义。请注意,SDO_xxx 运算符集与 OGC 定义的 ST_xxx 函数略有不同。

  3. 您没有指定任何坐标系(SDO_SRID 为 NULL)。虽然这在您的人工示例中有效,但在现实生活中您应该始终使用正确的坐标系。特别是如果您的形状是大地测量的(长/纬度),您必须使用正确的 SRID:4326 来说明。这保证了所有计算都是在地球的椭圆体形状的范围内完成的。如果您要执行基于距离的查询或测量长度、距离或面积,这一点尤其重要。对投影数据使用正确的 SRID 同样重要。无论使用何种坐标系,它都可以让您执行查询:例如,查找 GPS 点所在的地块(在本地投影中)。

  4. 性能和索引。确保您有空间索引。虽然 Oracle 12.2 允许您在不定义索引的情况下进行查询,但以前的版本总是需要一个(如果不存在则失败)。如果该表甚至中等大,则对没有空间索引的表执行查询可能会非常慢。例如,以您的村庄和游客为例。假设您想从包含 1000 万访客的表格中找出特定建筑物中的所有访客。在访问者表上没有任何索引的情况下,数据库将需要将每个访问者与所选建筑物进行比较。这在 CPU 上会很昂贵(I/O 不是真正的问题)。

  5. 最后,正确编写查询很重要。在像F(a,b) 这样的运算符中,b 用于搜索a,因此b 应该是较小的集合。例如,查找此建筑物中的所有访客必须写为SDO_INSIDE(visitors, buildings)。反面(这个客户在哪栋楼?)写成SDO_CONTAINS(buildings, visitors)

【讨论】:

  • 当我执行查询时出现此错误:13226. 00000 - “没有空间索引不支持接口”
  • 嗯,是的。这意味着在您搜索的表上必须使用空间索引才能应用空间谓词。这是 12.2 之前的严格要求:12.2 空间索引已成为可选 - 但除非您处理简单数据的小型数据集(100 或更少),否则您将需要一个以获得良好的性能。
猜你喜欢
  • 1970-01-01
  • 2012-05-20
  • 2015-10-22
  • 1970-01-01
  • 2021-11-24
  • 2013-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多