Другое

Решение ошибки ORA-12015: Руководство по быстрому обновлению материализованных представлений

Полное руководство по устранению ошибки ORA-12015 при создании материализованных представлений с быстрым обновлением и соединениями в Oracle. Изучите проверенные решения и лучшие практики.

ORA-12015: невозможно создать материализованное представление с быстрой перезагрузкой из сложного запроса с соединениями

Я столкнулся со следующей ошибкой при попытке создать материализованное представление в Oracle с FAST REFRESH:

ORA-12015: невозможно создать материализованное представление с быстрой перезагрузкой из сложного запроса

Вот материализованное представление, которое я пытаюсь создать:

CREATE MATERIALIZED VIEW anon_flag_mv
REFRESH FAST ON DEMAND
AS
SELECT t2.id AS t2_id,
t3.cfg_id AS cfg_id,
t3.parent_ref_id AS parent_ref_id,
t1.category_ref_id AS category_ref_id,
t1.is_on_demand AS is_on_demand,
t3.id AS t3_id,
t1.id AS t1_id
FROM table_one t1
JOIN table_two t2
ON t2.id = t1.category_ref_id
JOIN table_three t3
ON t1.id = t3.parent_ref_id;

Я уже создал необходимые журналы материализованных представлений:

CREATE MATERIALIZED VIEW LOG ON table_one
WITH PRIMARY KEY, SEQUENCE
(category_ref_id, is_on_demand)
INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW LOG ON table_two
WITH PRIMARY KEY, SEQUENCE
(cfg_id, parent_ref_id)
INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW LOG ON table_three
WITH PRIMARY KEY, SEQUENCE
INCLUDING NEW VALUES;

Как мне изменить определение моего материализованного представления, чтобы включить быструю перезагрузку, или какой альтернативный подход следует использовать для достижения возможности быстрой перезагрузки с этим сложным запросом, включающим несколько соединений?

Ошибка ORA-12015 возникает из-за того, что механизм быстрого обновления Oracle не может обрабатывать сложные запросы с соединениями, использующими стандартный синтаксис ANSI, и без правильного включения ROWID. Для решения этой проблемы необходимо изменить определение материализованного представления, используя синтаксис соединений, специфичный для Oracle, и включить столбцы ROWID из всех соединенных таблиц.

Содержание

Понимание ошибки ORA-12015

Ошибка ORA-12015 возникает при попытке создать материализованное представление с быстрым обновлением, использующее сложный запрос, который не соответствует строгим требованиям Oracle для возможности быстрого обновления. Как объясняется в документации Oracle, основная проблема заключается в следующем:

“ROWID и ограничения первичных ключей не поддерживаются для сложных запросов”

Ошибка появляется, когда запрос материализованного представления включает:

  • Множественные соединения с использованием синтаксиса ANSI (INNER JOIN, LEFT JOIN и т.д.)
  • Сложные подзапросы
  • Агрегатные функции в сочетании с соединениями
  • Типы объектов или сложные типы данных

В вашем случае проблема заключается в использовании синтаксиса JOIN ANSI и отсутствии требуемых столбцов ROWID из всех соединенных таблиц в списке SELECT.

Основные требования для материализованных представлений с быстрым обновлением

Чтобы материализованное представление поддерживало быстрое обновление со сложными соединениями, оно должно соответствовать этим основным требованиям:

  1. Включение ROWID: Все таблицы в предложении FROM должны иметь свои столбцы ROWID, включенные в список SELECT
  2. Синтаксис соединений Oracle: Должен использоваться старый синтаксис (+) вместо ANSI соединений
  3. Правильные журналы материализованных представлений: Журналы должны быть настроены с ROWID и соответствующими столбцами
  4. Ограничения первичных ключей: Базовые таблицы должны иметь ограничения первичных ключей

Как отмечено в результатах исследований с Stack Overflow:

“ROWID всех таблиц в списке FROM должны появиться в списке SELECT запроса”


Решение 1: Преобразование в синтаксис соединений, специфичный для Oracle, с ROWID

Наиболее прямое решение - переписать ваше материализованное представление, используя синтаксис соединений, специфичный для Oracle, и включить столбцы ROWID из всех таблиц.

Шаг 1: Обновление журналов материализованных представлений

Сначала убедитесь, что ваши журналы материализованных представлений включают ROWID и все необходимые столбцы:

sql
-- Обновление существующих журналов для включения ROWID
CREATE MATERIALIZED VIEW LOG ON table_one
    WITH ROWID, PRIMARY KEY, SEQUENCE
    (category_ref_id, is_on_demand)
    INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW LOG ON table_two
    WITH ROWID, PRIMARY KEY, SEQUENCE
    (cfg_id, parent_ref_id)
    INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW LOG ON table_three
    WITH ROWID, PRIMARY KEY, SEQUENCE
    INCLUDING NEW VALUES;

Шаг 2: Переписывание материализованного представления

Преобразуйте ваши ANSI соединения в синтаксис Oracle (+) и включите столбцы ROWID:

sql
CREATE MATERIALIZED VIEW anon_flag_mv
    REFRESH FAST ON DEMAND
AS
SELECT t2.id               AS t2_id,
       t3.cfg_id           AS cfg_id,
       t3.parent_ref_id    AS parent_ref_id,
       t1.category_ref_id  AS category_ref_id,
       t1.is_on_demand     AS is_on_demand,
       t3.id               AS t3_id,
       t1.id               AS t1_id,
       t1.ROWID            AS t1_rowid,
       t2.ROWID            AS t2_rowid,
       t3.ROWID            AS t3_rowid
FROM table_one t1,
     table_two t2,
     table_three t3
WHERE t2.id = t1.category_ref_id(+)
  AND t1.id = t3.parent_ref_id(+);

Ключевые изменения:

  • Заменен JOIN на список таблиц, разделенных запятыми, в предложении FROM
  • Условия ON изменены на WHERE с обозначением (+)
  • Добавлены столбцы ROWID для всех трех таблиц

Примечание: Синтаксис (+) создает внешние соединения. Если вам нужны внутренние соединения, соответствующим образом скорректируйте условия.


Решение 2: Использование REFRESH FORCE как запасного варианта

Если вы не можете изменить синтаксис соединений, вы можете использовать REFRESH FORCE, который пытается выполнить быстрое обновление, но возвращается к полному обновлению при необходимости:

sql
CREATE MATERIALIZED VIEW anon_flag_mv
    REFRESH FORCE ON DEMAND
AS
SELECT t2.id               AS t2_id,
       t3.cfg_id           AS cfg_id,
       t3.parent_ref_id    AS parent_ref_id,
       t1.category_ref_id  AS category_ref_id,
       t1.is_on_demand     AS is_on_demand,
       t3.id               AS t3_id,
       t1.id               AS t1_id
FROM table_one t1
         JOIN table_two t2
              ON t2.id = t1.category_ref_id
         JOIN table_three t3
              ON t1.id = t3.parent_ref_id;

Компромиссы:

  • Плюсы: Сохраняет исходную структуру запроса и ANSI соединения
  • Минусы: Возвращается к полному обновлению, когда быстрое обновление невозможно, что медленнее

Решение 3: Переструктурирование запроса

Для более сложных сценариев рассмотрите возможность разбиения материализованного представления на более простые компоненты:

Шаг 1: Создание промежуточных материализованных представлений

sql
-- Создание первого материализованного представления с простым соединением
CREATE MATERIALIZED VIEW mv_t1_t2
    REFRESH FAST ON DEMAND
AS
SELECT t1.ROWID AS t1_rowid,
       t2.ROWID AS t2_rowid,
       t1.id AS t1_id,
       t2.id AS t2_id,
       t1.category_ref_id,
       t1.is_on_demand
FROM table_one t1,
     table_two t2
WHERE t2.id = t1.category_ref_id(+);

-- Создание журнала материализованного представления для промежуточного МВ
CREATE MATERIALIZED VIEW LOG ON mv_t1_t2
    WITH ROWID, PRIMARY KEY, SEQUENCE
    (t1_id, t2_id, category_ref_id, is_on_demand)
    INCLUDING NEW VALUES;

-- Создание конечного материализованного представления
CREATE MATERIALIZED VIEW anon_flag_mv
    REFRESH FAST ON DEMAND
AS
SELECT m.t2_id,
       t3.cfg_id,
       t3.parent_ref_id,
       m.category_ref_id,
       m.is_on_demand,
       t3.id AS t3_id,
       m.t1_id,
       m.t1_rowid,
       m.t2_rowid,
       t3.ROWID AS t3_rowid
FROM mv_t1_t2 m,
     table_three t3
WHERE m.t1_id = t3.parent_ref_id(+);

Этот подход позволяет вам наращивать сложность постепенно, сохраняя возможность быстрого обновления.


Лучшие практики для сложных материализованных представлений

1. Всегда включайте столбцы ROWID

Как подчеркивается в документации Oracle, убедитесь, что все столбцы ROWID включены:

sql
SELECT table1.ROWID AS t1_rowid,
       table2.ROWID AS t2_rowid,
       ...

2. Используйте правильную конфигурацию журнала материализованного представления

sql
CREATE MATERIALIZED VIEW LOG ON table_name
    WITH ROWID, PRIMARY KEY, SEQUENCE
    (column1, column2, ...)
    INCLUDING NEW VALUES;

3. Тестируйте сначала с простыми запросами

Начните с простого материализованного представления для одной таблицы, затем постепенно добавляйте сложность:

sql
-- Сначала тест с одной таблицей
CREATE MATERIALIZED VIEW test_mv
    REFRESH FAST
AS SELECT * FROM table_one;

-- Затем добавляйте соединения постепенно

4. Мониторьте производительность обновления

sql
-- Проверьте метод обновления
SELECT mview_name, last_refresh_type, last_refresh_date
FROM all_mviews
WHERE mview_name = 'ANON_FLAG_MV';

-- Проверьте, возможно ли быстрое обновление
SELECT mview_name, can_use_fast_refresh
FROM all_mview_refresh_times
WHERE mview_name = 'ANON_FLAG_MV';

Альтернативные подходы

1. Используйте агрегаты в материализованных представлениях

Если ваш запрос включает агрегаты, рассмотрите возможность создания отдельных материализованных представлений:

sql
-- Создайте базовое материализованное представление без агрегатов
CREATE MATERIALIZED VIEW mv_base
    REFRESH FAST
AS SELECT t1.id, t2.id, t3.id, ...
   FROM table_one t1, table_two t2, table_three t3
   WHERE ...;

-- Затем создайте представление с агрегатами поверх него
CREATE MATERIALIZED VIEW mv_aggregate
    REFRESH FAST ON COMMIT
AS SELECT id, COUNT(*) as cnt
   FROM mv_base
   GROUP BY id;

2. Планируйте регулярные полные обновления

Если быстрое обновление не является критичным, планируйте полные обновления в периоды низкой нагрузки:

sql
-- Создайте с полным обновлением
CREATE MATERIALIZED VIEW anon_flag_mv
    REFRESH COMPLETE ON COMMIT
AS SELECT ...;

-- Или планируйте обновление
BEGIN
    DBMS_MVIEW.REFRESH('anon_flag_mv', 'C', '', FALSE, FALSE, 0, '', 0);
END;
/

3. Используйте кэширование на уровне приложения

Для сценариев с большим количеством операций чтения рассмотрите возможность использования кэширования на уровне приложения вместо материализованных представлений.

Источники

  1. Stack Overflow - ORA-12015 cannot create a fast refresh materialized view from a complex query
  2. Oracle Support - Diagnosing ORA-12015 Fast Refresh Materialized View / Complex Queries
  3. Connor McDonald - Complex materialized views and fast refresh
  4. Oracle Documentation - Materialized View Concepts and Architecture
  5. Stack Overflow - How to create a materialized view with FAST REFRESH and JOINS
  6. Discngine - How to create an Oracle materialized view that performs well on fast refresh

Заключение

Ошибка ORA-12015 - это распространенная проблема при работе со сложными материализованными представлениями в Oracle. Следуя этим ключевым стратегиям, вы можете успешно создавать материализованные представления с быстрым обновлением и соединениями:

  1. Преобразуйте в синтаксис Oracle (+) и включите столбцы ROWID из всех соединенных таблиц
  2. Убедитесь в правильной конфигурации журнала материализованного представления с поддержкой ROWID и последовательности
  3. Рассмотрите возможность использования REFRESH FORCE как запасного варианта, когда изменения синтаксиса невозможны
  4. Разбивайте сложные запросы на более простые компоненты с использованием промежуточных материализованных представлений
  5. Тестируйте постепенно, чтобы выявить конкретные проблемы совместимости

Наиболее надежное решение для вашей конкретной ситуации - переписать ваше материализованное представление, используя синтаксис соединений, специфичный для Oracle, и включить все столбцы ROWID. Этот подход сохраняет возможность быстрого обновления, сохраняя при этом ту же структуру данных и отношения, что и в вашем исходном запросе.

Авторы
Проверено модерацией
Модерация