我遇到的情况是我正在为数据库使用Oracle物化视图。我正在执行基于计划的物化视图刷新,以获取源数据库中发生的所有更改。
为了识别发生的变化,我在物化视图上使用触发器。因此,当我刷新视图时,视图将发生新的插入/更新。这些添加/更新/删除触发器将在日志表中插入/更新相同的记录。 示例 - DB中的Employee和相关表。在单独的位置,Employee_view和相关视图。在DB中添加,更新或删除Employee时,刷新时,Employee_view将更新。将为Employee_view执行触发器。对于一个emplyee ID 1,一行插入LOG表。现在因为多个表正在更新,它们都会在员工ID 1的日志表中触发插入(如果不存在)和更新(如果存在)。
我应该担心线程安全情况。例如。 Empoyee 1名称已更改且年龄发生变化。 Bothare不同的表。两者都将在日志表中插入新记录,因为它们是并行的。
答案 0 :(得分:0)
我重新考虑您的设计,因为您所描述的内容明确反对Oracle对创建触发器的文档限制:
注意:如果在物化视图的基表上创建触发器, 那么你必须确保在刷新期间不触发触发器 物化视图。在刷新期间,DBMS_MVIEW过程 I_AM_A_REFRESH返回TRUE。
请参阅:https://docs.oracle.com/database/121/LNPLS/create_trigger.htm#LNPLS01374
以下是评论中提到的替代想法的充实版本。我不知道是否支持针对物化视图更新日志的直接DELETE
/一个好主意。但它适用于12c。
-- Create a base table (you'd already have these...)
CREATE TABLE matt_base (id number not null PRIMARY KEY, d VARCHAR2(30));
-- Create materialized view logs (you should already have these if you're
-- using FAST refresh, which you must be or else your question makes no
-- sense...)
CREATE MATERIALIZED VIEW LOG ON matt_base
WITH PRIMARY KEY (d) INCLUDING NEW VALUES
FOR FAST REFRESH;
-- Create the materialized view (you have this -- this is the MV you wanted
-- to create a trigger on)
-- The key addition here is the REPLICATED_FLAG column.
CREATE MATERIALIZED VIEW matt_mv USING INDEX REFRESH FAST ON DEMAND
WITH PRIMARY KEY FOR UPDATE AS
SELECT id, d, 'N' replicated_flag FROM matt_base;
-- Now for the process. Put some data into the table and refresh MV.
insert into matt_base values (1, 'A');
insert into matt_base values (2, 'B');
BEGIN DBMS_MVIEW.refresh (list => 'MATT_MV', method => 'F'); END;
-- To replicate data, select everything from the MV that has not been
-- replicated
SELECT * FROM matt_mv WHERE replicated_flag = 'N';
-- Mark the rows as replicated. (This is sloppy... in real life you'd want
-- to ensure you update the same set of rows that you replicated).
UPDATE matt_mv SET replicated_flag = 'Y';
-- Now, here's the weird part. Oracle made a note of the rows affected
-- in the MV by the above update. We have to make Oracle forget about
-- them! You can get the update log's name (USLOG$_MATT_MV, below) by
-- querying DBA_SNAPSHOTS.
-- CAVEAT: I don't know if this is safe / a good idea!!! Ask Oracle Support.
DELETE FROM uslog$_matt_mv;
COMMIT;
-- Now do some updates on the base table to simulate additional activity to
-- replicate.
update matt_base set d = 'A_NEW' where id = 1;
insert into matt_base values (3, 'C');
COMMIT;
-- Now refresh your materialized view again to pick up the new activity
BEGIN DBMS_MVIEW.refresh (list => 'MATT_MV', method => 'F'); END;
-- Look at your materialized view.
-- You'll see that only the recently changed rows are flagged as
-- not replicated. ID = 2 should be flagged as replicated because
-- it was not changed.
SELECT * FROM matt_mv;