【问题标题】:Can not use ORDER BY when creating materialized view with SDO geometry in Oracle 11g在 Oracle 11g 中使用 SDO 几何创建物化视图时无法使用 ORDER BY
【发布时间】:2015-07-14 14:50:15
【问题描述】:

我在客户端上使用带有 Spatial 和 Oracle SQL Developer 的 Oracle 11g 2.0.1.0。我有一个表 Places 主键 ID 和一个视图 Coordinates 两列:ID 引用 Places 中的帖子,以及 SDO 几何 Point

我想用下面的 SQL 创建一个物化视图:

CREATE MATERIALIZED VIEW PlaceCoordinates
NOCACHE NOPARALLEL BUILD IMMEDIATE
USING INDEX
REFRESH ON DEMAND COMPLETE 
DISABLE QUERY REWRITE  AS
SELECT Places.ID, Coordinates.Point
FROM Places
LEFT OUTER JOIN Coordinates ON Places.ID = Coordinates.ID
ORDER BY Places.ID

这给了我这个错误:

ORA-30373:此上下文不支持对象数据类型

无论我按什么排序(即使只是像1 这样的傻事)我都会得到同样的错误。但是,如果我删除 ORDER BY 语句,它就可以正常工作。如果我只做一个普通的SELECT 而不创建物化视图,它也可以很好地进行排序。

为什么我不能排序?反正有这个问题吗?

【问题讨论】:

  • 当你 order by 1 时,你仍然引用 places.ID,只是 by position。它是否让您order by ID 没有表前缀?
  • 另见 MOS 文档 1939208.1,它适用于 12c,但这可能是相关的;或 1067173.1。您是在订购 for physical clustering 还是为了避免在查询 MV 时使用 order by - 这不能保证有效?
  • 感谢您的评论。你知道为什么我不能引用Places.ID吗?明天回去工作时,我将不带表前缀进行测试。
  • 如果位置参考没有,我怀疑它是否会像ID 一样工作,但也许值得一试。除了我提到的那两个文档之外,不,恐怕不是。
  • Obligatory link to Tom Kytes blog。即使它今天在查询 MV 时似乎没有 order by 也能工作,但它可能不会有一天。

标签: sql oracle sorting oracle11g oracle-spatial


【解决方案1】:

关键是物化视图中的 ORDER BY 没有意义。

在幕后,物化视图实际上只是一个表,当它所基于的表更新时,它会自动更新。但作为一张桌子意味着永远无法保证订购。即使初始 MV 以所需的顺序存储,也不能保证在应用更新后它会保持不变。确保以正确顺序获得结果的唯一方法是在从 MV 中进行选择时使用显式 ORDER BY。

您可以将 ORDER BY 包含在视图(不是实体化视图)中,并且在您使用该视图时应用:从视图中选择然后不需要任何 ORDER BY。但这是一种非常糟糕的做法。这意味着应用程序可能在不知不觉中依赖于视图提供的某些假定顺序 - 直到有人决定从视图中删除 ORDER BY 并且一切都变得松散。

关键是:如果应用程序需要特定顺序的结果,那么它必须在它发出的 SELECT 中这样说,包括 ORDER BY。

也就是说,查看您的 MV 定义,看起来它永远不会随着基表(位置和坐标)上的更改而更新:您说它是“按需完成刷新”。换句话说,您(或某些自动过程)会定期触发完全刷新。这与创建新表完全相同。你不妨这样做:

CREATE TABLE PlaceCoordinates AS
SELECT Places.ID, Coordinates.Point
FROM Places
LEFT OUTER JOIN Coordinates ON Places.ID = Coordinates.ID;

并在每次您想要刷新您的 PLACECOORDINATES 表时运行此程序(删除旧表后)。它将比 MV 机械更简单、更高效。另一种方法是创建一次表,然后在必要时截断并填充它:

CREATE TABLE PlaceCoordinates (
  ID NUMBER PRIMARY KEY,
  Point SDO_GEOMETRY
);

TRUNCATE TABLE PlaceCoordinates;
INSERT INTO PlaceCoordinates (ID, Point)
SELECT Places.ID, Coordinates.Point
FROM Places
LEFT OUTER JOIN Coordinates ON Places.ID = Coordinates.ID;

这让您可以指定 ID 是主键 - 总是一个好主意。当然,不要忘记在 POINT 列上定义正确的空间索引(假设您想在地图上显示点或查询它们)。好的做法是在刷新内容之前先删除该索引,然后再重新创建它(对于 MV 方法,您也需要这样做)。

无论选择何种方法(您指定的 MV 或表),PLACECOORDINATES 都不会反映 PLACES 和 COORDINATES 表的实时状态。它只会反映您上次手动完全刷新 MV 或重新加载表时的状态。如果这是可以接受的,那么你就准备好了。

如果您希望 PLACECOORDINATES 更接近其他两个表的状态,而不必完全刷新/重新加载它,比如每分钟,那么您需要定义 MV,以便仅从源中的更改刷新它表。这意味着您需要在将记录更改以应用于 MV 的那些表上使用 MATERIALIZED VIEW LOG。但是,这只会在您指定的时间间隔或您手动请求刷新时发生。但合理地不超过每分钟。当然不是每一秒。

如果 PLACECOORDINATES 必须反映 PLACES 和 COORDINATES 中发生的所有更改(=“实时”),那么保证这一点的唯一方法是使其成为一个表,并在 PLACES 和 COORDINATES 上设置触发器自动将更改应用于这些更改PLACECOORDINATES 上的表格。

在这种情况下,您最好直接从基表中读取数据。

【讨论】:

  • 感谢您的详尽回复。你是对的,MV 将按计划更新,并不总是反映表格的当前状态,但这对我来说没问题。我想我会在每次查询 MV 时而不是在创建时简单地对性能进行排序 - 这似乎是正确的做法。
  • “这与创建新表完全相同” - 除了删除和重新创建表会使引用它的任何内容无效,您必须重新创建授权和索引,以及任何试图查看的内容它在重建时会出错。文档还说您可以在初始创建期间订购,但刷新时会忽略它,所以不太确定那是什么。 (并且保证视图中的 order by 总是会在您查询时反映出来?)
  • 我的意思是,拥有一个始终按需完全刷新的 MV 与拥有一张普通的桌子没有什么不同。没有任何运营利益。至于在删除/创建后重新创建索引,这可能比通过删除/插入更新索引更快(这是完全刷新本质上所做的)。再一次,一个 truncate+insert 保留所有索引(和所有其他依赖项)。
  • 视图中的排序将始终针对简单的选择(SELECT ... FROM ... WHERE)执行。令人惊讶的是,它也反映在简单的连接中。但话又说回来:不要那样做。不要在视图中包含 ORDER BY。始终在查询中显式使用 ORDER BY。
猜你喜欢
  • 2013-06-28
  • 1970-01-01
  • 2013-05-04
  • 2013-09-14
  • 1970-01-01
  • 2011-11-22
  • 2016-10-23
  • 2014-04-25
相关资源
最近更新 更多