【问题标题】:How to Fast Refresh on a Materialized View with Joins and Aggregates?如何使用连接和聚合快速刷新物化视图?
【发布时间】:2017-07-18 15:00:30
【问题描述】:

假设我有两个表jobbatch

CREATE TABLE batch
(
  batch_id   NUMBER(20) PRIMARY KEY,
  batch_type NUMBER(20),
  [some other values] ...
);

CREATE TABLE job
(
  job_id NUMBER(20) PRIMARY KEY,
  job_batch_id NUMBER(20),
  job_usr_id NUMBER(20),
  job_date DATE,
  [some other values] ...
  CONSTRAINT fk_job_batch
    FOREIGN KEY (job_batch_id) REFERENCES batch(batch_id),
  CONSTRAINT fk_job_usr
    FOREIGN KEY (job_usr_id) REFERENCES client(usr_id)
);

假设它们每个都包含大量数据(数百万行)。我想要做的是创建一个物化视图,以反映每个usr_id 为特定类型的批处理运行的第一个和最后一个作业。例如:

CREATE MATERIALIZED VIEW client_first_last_job
(usr_id, first_job_date, last_job_date)
AS
(
  SELECT
    job_usr_id    AS usr_id,
    MIN(job_date) AS first_job_date,
    MAX(job_date) AS last_job_date
  FROM job, batch
  WHERE job_batch_id=batch_id
    AND batch_type IN (1,3,5,9)
  GROUP BY job_usr_id
);

这一切都很好,但是由于记录太多,构建这个物化视图需要很长时间(远远超过每次需要刷新时处理的时间)。我的直接想法是使用物化视图日志进行增量更新。这些很容易创建。但是当我尝试构建 MV 以使用 REFRESH FAST ON DEMAND 时,我得到了一个 ORA-12015: cannot create a fast refresh materialized view from a complex query 错误,从一些谷歌搜索中我猜这是由于连接和聚合函数的共存。

还有其他方法可以做到这一点吗?请注意,对父表进行非规范化或其他更改是不可行的。

【问题讨论】:

  • 我很好奇非原子完全刷新是否会足够快地刷新以满足您的需求: exec dbms_mview.refresh('CLIENT_FIRST_LAST_JOB', 'C', atomic_refresh => false);以防万一您不知道它的限制,非原子刷新有一个显着的缺点,即在刷新过程中表可能会显示为空(甚至对于其他用户和会话)。但是,它会运行得更快,因为它执行 truncate + insert append;与执行删除 + 插入的常规完整刷新相反。

标签: oracle materialized-views


【解决方案1】:

您可以嵌套您的 mview,您可以从 docs 中了解这些内容:

CREATE MATERIALIZED VIEW joinmview
(usr_id, job_date)
REFRESH FORCE ON DEMAND
AS
(
  SELECT
    job_usr_id    AS usr_id,
    job_date
  FROM job, batch
  WHERE job_batch_id=batch_id
    AND batch_type IN (1,3,5,9)
);

CREATE MATERIALIZED VIEW LOG ON JOINMVIEW 
  WITH ROWID (usr_id, JOB_DATE) including new values;

CREATE MATERIALIZED VIEW client_first_last_job
(usr_id, first_job_date, last_job_date)
REFRESH FORCE ON DEMAND
AS
(
  SELECT
    usr_id,
    MIN(job_date) AS first_job_date,
    MAX(job_date) AS last_job_date
  FROM joinmview
  GROUP BY usr_id
);

验证两个 mview 都可以快速刷新:

exec dbms_mview.refresh('JOINMVIEW', 'C');
exec dbms_mview.refresh('JOINMVIEW', 'F');

exec dbms_mview.refresh('CLIENT_FIRST_LAST_JOB', 'C');    
exec dbms_mview.refresh('CLIENT_FIRST_LAST_JOB', 'F');

您可以将两个 mview 放入同一个刷新组 (docs),只需确保按依赖顺序添加它们即可。换句话说,在本例中,将 CLIENT_FIRST_LAST_JOB 添加到刷新组之前添加 JOINMVIEW。

【讨论】:

  • joinmview 将产生一些非常陡峭的存储影响,即大约 75% 的父表中的行数。我不知道我可以合理地承诺那么多。
  • @MikeCarpenter 由于从 Oracle 11 或 12 开始不推荐使用 Streams 复制,我不确定您在 Oracle Goldengate 之外还有其他解决方案,这是一个昂贵的额外成本选项(前提是您已经在使用 Enterprise版)。
  • 我明白了。我担心会是这样。
  • 批处理和作业表以及生成的 mview 有多大?以及构建 mview 需要多长时间?
  • @BobC 数亿行。问题中的视图只需 2 多小时即可构建。有机会我会尽快测试答案中的观点。
猜你喜欢
  • 2015-11-11
  • 2013-05-31
  • 1970-01-01
  • 2016-12-11
  • 2013-12-04
  • 2021-11-06
  • 2020-03-13
  • 2013-06-28
  • 2017-05-18
相关资源
最近更新 更多