在 Oracle 11g 中使用 SDO 几何创建物化视图时无法使用 ORDER BY

And*_*ers 6 sql sorting oracle oracle-spatial oracle11g

我在客户端上使用带有 Spatial 和 Oracle SQL Developer 的 Oracle 11g 2.0.1.0。我有一个Places带有主键的表ID和一个Coordinates带有 tw 列的视图:ID引用中的帖子Places和 SDO geometry 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
Run Code Online (Sandbox Code Playgroud)

这给了我这个错误:

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

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

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

Alb*_*ind 7

关键是物化视图中的 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;
Run Code Online (Sandbox Code Playgroud)

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

CREATE TABLE PlaceCoordinates (
  ID NUMBER PRIMARY KEY,
  Point SDO_GEOMETRY
);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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