存储物化视图中的数据
Store the data from Materialized Views
我有一个实体化视图 (MV),它会在 23:00 每天刷新。它将 select 来自大型事务 table(例如 1 亿条记录)并汇总用于报告目的的数据。
MV非常简单,只有4列7条记录。每次用户生成报告时,它总是显示如MV中的数据。现在用户要求能够查看去年的数据。由于我的MV总是替换已有的数据,所以无法达到用户的要求
我的问题
1. 是否可以将MV中的数据自动存储在一个持久化的table中?
2. 每次MV刷新完成后,是否可以创建触发器将MV中的数据插入另一个table?
物化视图没有触发器。然而,Updatable 物化视图确实有触发器,但它有一个问题,它必须基于单个 table.
基于多个table
CREATE MATERIALIZED VIEW LOG ON EMP;
CREATE MATERIALIZED VIEW mv_test
REFRESH FAST WITH PRIMARY KEY
FOR UPDATE
AS
SELECT *
FROM emp em JOIN DEPT de ON EM.DEPTNO = DE.DEPTNO;
ORA-12013: updatable materialized views must be simple enough to do fast refresh
基于单个table
CREATE MATERIALIZED VIEW mv_test
REFRESH FAST WITH PRIMARY KEY
FOR UPDATE
AS
SELECT * FROM emp;
Materialized View created.
触发器
CREATE OR REPLACE TRIGGER test_tg
BEFORE INSERT OR UPDATE OF ENAME, MGR
ON MV_TEST
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
DECLARE
tmpVar NUMBER;
BEGIN
tmpVar := 0;
NULL;
-- do as per the logic
EXCEPTION
WHEN OTHERS
THEN
NULL;
-- Consider logging the error and then re-raise
RAISE;
END test_tg;
Trigger created.
如果需要历史数据,为什么不考虑使用可以使用调度程序作业执行的存储过程来实现标准事务性 table 数据持久化。
正如你所引用的查询一个拥有 1 亿条记录的大型 table,我的估计是使用 FOLL ALL
或 BULK COLLECT
或者考虑批处理,不用说;这是一个不同的主题。
以下是程序和调度程序作业的伪代码,请根据需要进行更改。使用 INSERT 或 MERGE
使用 INSERT 的过程
CREATE OR REPLACE PROCEDURE historical_records (p_emp_no emp.empno%TYPE)
IS
BEGIN
FOR rec IN ( SELECT ename, mgr, SUM (sal) tot_sal
FROM scott.emp
WHERE empno = p_emp_no
GROUP BY ename, mgr)
LOOP
INSERT INTO hist_table (empno,
ename,
mgr,
sal_tot)
VALUES (rec.empno,
rec.ename,
rec.mgr,
rec.tot_sal);
END LOOP;
END;
使用 MERGE 的过程
CREATE OR REPLACE PROCEDURE historical_records (p_emp_no emp.empno%TYPE)
IS
BEGIN
MERGE INTO hist_table trg
USING ( SELECT ename, mgr, SUM (sal) tot_sal
FROM scott.emp
WHERE empno = p_emp_no
GROUP BY ename, mgr) src
ON (trg.empno = src.empno)
WHEN MATCHED
THEN
UPDATE SET trg.ename = src.ename, trg.mgr = src.mgr
WHEN NOT MATCHED
THEN
INSERT (trg.empno, trg.ename, trg.sal_tot)
VALUES (src.empno, src.ename, src.tot_sal);
END;
调度程序作业
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'HIST_PROC_JOB',
job_type => 'PLSQL_BLOCK',
JOB_ACTION => 'BEGIN historical_records; END;',
start_date => SYSDATE,
repeat_interval => 'FREQ=DAILY;BYHOUR=23;BYMINUTE=05',
end_date => NULL,
enabled => TRUE,
comments => 'Historical data insertion');
END;
/
我有一个实体化视图 (MV),它会在 23:00 每天刷新。它将 select 来自大型事务 table(例如 1 亿条记录)并汇总用于报告目的的数据。
MV非常简单,只有4列7条记录。每次用户生成报告时,它总是显示如MV中的数据。现在用户要求能够查看去年的数据。由于我的MV总是替换已有的数据,所以无法达到用户的要求
我的问题 1. 是否可以将MV中的数据自动存储在一个持久化的table中? 2. 每次MV刷新完成后,是否可以创建触发器将MV中的数据插入另一个table?
物化视图没有触发器。然而,Updatable 物化视图确实有触发器,但它有一个问题,它必须基于单个 table.
基于多个table
CREATE MATERIALIZED VIEW LOG ON EMP;
CREATE MATERIALIZED VIEW mv_test
REFRESH FAST WITH PRIMARY KEY
FOR UPDATE
AS
SELECT *
FROM emp em JOIN DEPT de ON EM.DEPTNO = DE.DEPTNO;
ORA-12013: updatable materialized views must be simple enough to do fast refresh
基于单个table
CREATE MATERIALIZED VIEW mv_test
REFRESH FAST WITH PRIMARY KEY
FOR UPDATE
AS
SELECT * FROM emp;
Materialized View created.
触发器
CREATE OR REPLACE TRIGGER test_tg
BEFORE INSERT OR UPDATE OF ENAME, MGR
ON MV_TEST
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
DECLARE
tmpVar NUMBER;
BEGIN
tmpVar := 0;
NULL;
-- do as per the logic
EXCEPTION
WHEN OTHERS
THEN
NULL;
-- Consider logging the error and then re-raise
RAISE;
END test_tg;
Trigger created.
如果需要历史数据,为什么不考虑使用可以使用调度程序作业执行的存储过程来实现标准事务性 table 数据持久化。
正如你所引用的查询一个拥有 1 亿条记录的大型 table,我的估计是使用 FOLL ALL
或 BULK COLLECT
或者考虑批处理,不用说;这是一个不同的主题。
以下是程序和调度程序作业的伪代码,请根据需要进行更改。使用 INSERT 或 MERGE
使用 INSERT 的过程
CREATE OR REPLACE PROCEDURE historical_records (p_emp_no emp.empno%TYPE)
IS
BEGIN
FOR rec IN ( SELECT ename, mgr, SUM (sal) tot_sal
FROM scott.emp
WHERE empno = p_emp_no
GROUP BY ename, mgr)
LOOP
INSERT INTO hist_table (empno,
ename,
mgr,
sal_tot)
VALUES (rec.empno,
rec.ename,
rec.mgr,
rec.tot_sal);
END LOOP;
END;
使用 MERGE 的过程
CREATE OR REPLACE PROCEDURE historical_records (p_emp_no emp.empno%TYPE)
IS
BEGIN
MERGE INTO hist_table trg
USING ( SELECT ename, mgr, SUM (sal) tot_sal
FROM scott.emp
WHERE empno = p_emp_no
GROUP BY ename, mgr) src
ON (trg.empno = src.empno)
WHEN MATCHED
THEN
UPDATE SET trg.ename = src.ename, trg.mgr = src.mgr
WHEN NOT MATCHED
THEN
INSERT (trg.empno, trg.ename, trg.sal_tot)
VALUES (src.empno, src.ename, src.tot_sal);
END;
调度程序作业
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'HIST_PROC_JOB',
job_type => 'PLSQL_BLOCK',
JOB_ACTION => 'BEGIN historical_records; END;',
start_date => SYSDATE,
repeat_interval => 'FREQ=DAILY;BYHOUR=23;BYMINUTE=05',
end_date => NULL,
enabled => TRUE,
comments => 'Historical data insertion');
END;
/