我在客户端上使用带有Spatial和Oracle SQL Developer的Oracle 11g 2.0.1.0。我有一个包含主键Places
的表ID
和一个包含tw列的视图Coordinates
:ID
引用Places
中的帖子,以及SDO几何Point
1}}。
我想使用以下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
,它也可以正常排序。
为什么我无法排序?反正这个问题呢?
答案 0 :(得分:3)
关键是物化视图中的ORDER BY毫无意义。
在幕后,物化视图实际上只是一个表,它在基于它的表得到更新时自动更新。但作为一张桌子意味着无法保证订购。即使初始MV以所需顺序存储,也不能保证在应用更新后它将保持不变。确保以正确顺序获得结果的唯一方法是在从MV中选择时使用显式ORDER BY。
您可以在视图(非物化视图)中包含ORDER BY,并且在您使用该视图时将应用ORDER BY:从视图中选择,然后不需要任何ORDER BY。但这是一种非常糟糕的做法。这意味着应用程序可能在不知不觉中依赖于视图提供的某些假定顺序 - 直到有人决定从视图中删除ORDER BY并且所有地狱都松散。
关键是:如果某个应用程序需要按特定顺序生成结果,那么必须在它发出的SELECT中这样说,包括ORDER BY。
那就是说,看看你的MV定义,它似乎永远不会更新,因为基表(PLACES和COORDINATES)发生了变化:你说它是"刷新需求完全" 。换句话说,您(或某些自动过程)会定期触发完全刷新。这与创建新表完全相同。你也可以这样做:
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,这样它只能从源代码中的更改中刷新表。这意味着您需要在这些表上需要一个MATERIALIZED VIEW LOG,其中将记录更改以便应用于MV。但是,这只会在您指定的时间间隔或手动请求刷新时发生。但合理的不是每分钟。当然不是每一秒。
如果PLACECOORDINATES必须反映所有位置和坐标的变化(="实时"),那么保证这一点的唯一方法是使其成为一个表并在地点和坐标上设置触发器当PLACECOORDINATES发生这些表时,会自动对这些表进行更改。
也许在这种情况下,你最好直接从基表读取。