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

Can not use ORDER BY when creating materialized view with SDO geometry in Oracle 11g

我在客户端上使用带有 Spatial 和 Oracle SQL Developer 的 Oracle 11g 2.0.1.0。我有一个带主键 ID 的 table Places 和一个带两列的视图 CoordinatesID 引用 [=11= 中的 post ],以及 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: object data types are not supported in this context

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

为什么我无法排序?反正有这个问题吗?

关键是实体化视图中的 ORDER BY 没有任何意义。

在幕后,实体化视图实际上只是一个 table,当它所基于的 table 更新时,它会自动更新。但是作为 table 意味着无法保证任何顺序。即使初始 MV 以所需的顺序存储,也不能保证在应用更新后它会保持不变。确保以正确顺序获得结果的唯一方法是在从 MV select 时使用显式 ORDER BY。

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

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

就是说,看你的 MV 定义,它看起来永远不会更新,因为在基础 tables(PLACES 和 COORDINATES)上发生了变化:你说它是 "REFRESH ON DEMAND COMPLETE".换句话说,您(或某些自动过程)会定期触发完全刷新。这与创建一个新的 table 完全相同。你不妨这样做:

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

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

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,或 table),PLACECOORDINATES 都不会反映 PLACES 和 COORDINATES table 的实时状态。它只会反映您上次手动完全刷新 MV 或重新加载 table 时的状态。如果这是acceptable,那么你就完成了。

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

如果 PLACECOORDINATES 必须反映 PLACES 和坐标发生的所有变化(= 在 "real time" 中),那么保证这一点的唯一方法是使其成为 table 并在 PLACES 上触发和 COORDINATES 会在 PLACECOORDINATES 上的 table 上自动应用更改。

也许在那种情况下你最好直接从基础 tables 中读取。