2009-07-30 1 views
4

Неправильно ли использовать триггеры или скрипты для поддержания целостности данных, которые Oracle не предназначен для обеспечения соблюдения, или это знак Я плохо моделирую свои данные?Использование скриптов для обеспечения целостности данных в Oracle

От ответов на предыдущий пост (Implementing User Defined Fields), я решил, что я хочу, чтобы двигаться вперед проектирование с соединением Class и Concrete наследования. Я хочу, чтобы один базовый класс для всех SAMPLE составлял конкретную таблицу для каждого уникального набора атрибутов.

Хотя я могу обеспечить, чтобы каждая конкретная таблица имела родительскую запись в SAMPLE, сделав SAMPLE.sample_id первичный ключ с ограничением внешнего ключа. Тем не менее, я не знаю, как обеспечить, чтобы запись SAMPLE имела ровно один, поскольку запись ребенка может быть в любом количестве таблиц.

Как я могу обеспечить соблюдение этого? Если это триггеры INSERT, UPDATE и DELETE, считается ли это плохой практикой?

+0

Какие сценарии? Какая база данных? Я никогда не слышал об использовании сценариев для этого. –

+0

@John: см. Сценарии языка манипулирования данными (DML) –

+0

@rexem: Я знаю, что такое DML, но не так, как это связано с сохранением целостности данных (что само по себе не является). –

ответ

3

Я думаю, вы можете решить эту проблему, используя материализованное представление, которое объединяет все TABLEA, TABLEB и TABLEC + groub по идентификатору основной таблицы. Вы должны создать материализованные журналы просмотра, чтобы сделать это быстрым обновляемым видом материализации. И вы добавили ограничение проверки, которое выдает ошибку, когда в материализованном представлении на идентификаторе главной таблицы больше, чем в строке.

Rob van Wijk объясняет здесь http://rwijk.blogspot.com/2009/07/fast-refreshable-materialized-view.html много о быстрорежиме. Роб ван Вейк часто присутствует здесь и в stackoverflow.

Здесь вы можете прочитать об использовании проверочных ограничений на материализованных представлениях: http://technology.amis.nl/blog/475/introducing-materialized-views-as-mechanism-for-business-rule-implementation-complex-declarative-constraints

Использования быстрого refresizable мв означает, что проверка целостности делаются во время совершения, а не во время вставки или updateting данных.

Я очень устал, я не могу проверить его сам, и я не могу представить настоящего примера.

edit1: Вот пример:

Он работает при создании быстрого обновления мв с проверочным ограничением и уникальной функцией на основе индекса.

Сначала мы создаем таблицы:

SQL> create table mastertable (id number(10) not null primary key); 

SQL> create table tablea 
(id number(10) not null primary key 
, master_id number(10) not null references mastertable (id)); 

SQL> create table tableb 
(id number(10) not null primary key 
, master_id number(10) not null references mastertable (id)); 

SQL> create table tablec 
(id number(10) not null primary key 
, master_id number(10) not null references mastertable (id)); 

Затем мы создаем мв журналы: (! Столбец umarker действительно необходимы)

SQL> create materialized view log on tablea with rowid (master_id) 
    including new values; 

SQL> create materialized view log on tableb with rowid (master_id) 
    including new values; 

SQL> create materialized view log on tablec with rowid (master_id) 
    including new values; 

мв:

SQL> create materialized view table_abc 
    refresh fast with rowid on commit 
    as 
    select master_id,count(*) master_count, 'A' umarker 
    from tablea 
    group by master_id 
    union all 
    select master_id,count(*) master_count, 'B' umarker 
    from tableb 
    group by master_id 
    union all 
    select master_id,count(*) master_count, 'C' umarker 
    from tablec 
    group by master_id 
    /

Теперь добавим ограничение проверки на это mv, чтобы убедиться, что вы не можете дважды вставлять в одну и ту же таблицу подробностей на master_id:

SQL> alter table table_abc add check (master_count in (0,1)); 

И мы добавим уникальный индекс на основе функции для этого мв, чтобы убедиться, что вы не можете вставить в таблицу А и таблицу Ь с тем же master_id:

SQL> create unique index table_abc_ufbi1 on table_abc 
    (case when master_count = 1 then master_id else null end); 

Test 1 (счастливый путь):

SQL> вставить в основные значения (1);

1 rij is aangemaakt.

SQL> insert into tablea values ​​(1,1);

1 rij is aangemaakt.

SQL> commit;

Commit voltooid.

Тест 2 (одна вставка в таблице А, и одна вставка в таблице б с таким же master_id)

SQL> вставить в mastertable значений (2);

1 rij is aangemaakt.

SQL> insert into tablea values ​​(2,2);

1 rij is aangemaakt.

SQL> insert into tableb values ​​(3,2);

1 rij is aangemaakt.

SQL> фиксации; совершить * ОШИБКА в строке 1: .ORA-12008: Ошибка в пути, чтобы обновить снимок. ОР-00001: Нарушение ограничения UNIQUE (TESTT.TABLE_ABC_UFBI1).

Тест 3 (вставка таблицы в два раза с той же master_id)

SQL> вставить в мастер-таблицы значений (3);

первая строка была создана.

SQL> вставить в значение TABLEA (4.3);

первая строка была создана.

SQL> вставить в значение TABLEA (5.3);

первая строка была создана.

SQL> фиксации; совершить * ОШИБКА в строке 1: .ORA-12008: Ошибка в пути, чтобы обновить снимок. ORA-02290: проверочное ограничение (TESTT.SYS_C0015406) ​​была нарушена.

2

Скажите, что ваша «главная» таблица называется TableA, а первичный ключ называется «ID». Создайте вторую таблицу, скажем TableB, также с первичным ключом с именем «ID». Теперь определите TableB (ID) в качестве внешнего ключа для TableA (A).

Имея TableB (ID) в качестве внешнего ключа, он может иметь только значение, если оно существует в TableA (ID), и имея его в качестве первичного ключа, означает, что он не может иметь значение более одного раза.

+0

Я обновил свой ответ, чтобы отразить вашу новую информацию. –

+0

Ну, у меня ТАБЛИЦА, затем TABLE_A, TABLE_B и TABLE_C. Каждая запись в TABLE должна иметь ТОЧНО ОДНУ запись в EABLE TABLE_A, TABLE_B или TABLE_C. Ограничение FK на A, B, C выполняет только половину. Таблица A и таблица B могли бы тогда иметь один и тот же родитель (чего я не хочу). – Steven

+0

Зачем вам три детских стола? Не могли бы вы поместить все дочерние записи в одну таблицу с полем TYPE (A, B или C) и установить ограничение No-Duplicates на Key и Type? –

0

Ну, это зависит от типа целостности, которую вы ищете.

База данных предназначена для ссылочной целостности. Поэтому, если это то, что вы ищете, используйте структуры базы данных. Не сворачивайте себя.

Если вы пытаетесь сохранить другие типы целостности (например, MAC для строк), то триггеры вполне приемлемы.

1

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

Фактически, теперь, когда я думаю об этом, нет необходимости проверять корпус INSERT. Вы вставляете в одну транзакцию строку SAMPLE и CONCRETE_1. Не может быть строки CONCRETE_2 с тем же PK, поскольку строка SAMPLE ранее не существовала.

+0

Как обеспечить, чтобы запись SAMPLE не создавалась без дочернего элемента? – Steven

+0

Не беспокойтесь, если вы не позволяете случайным пользователям вставлять строки в эту таблицу. Если необходимо, добавьте триггер AFTER INSERT, чтобы проверить все таблицы CONCRETE_n. –

+0

Использует триггеры так, как вы описали, считается плохой практикой? – Steven

1

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

Том Кайт объясняет, почему: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:599808600346047256

Проблема заключается в том, что транзакция не может видеть то, что другие неподтвержденные операции делают.

Чтобы сделать простой пример, транзакции A и B могут одновременно вставлять в таблицу одно и то же значение «X» и могут обе проверять, что такое значение еще не существует. Транзакция A совершает после транзакция B проверяет существующие значения «X». Транзакция B не находит X, поэтому она также вставляет свой собственный X и фиксирует. Теперь требование уникальности побеждено.

Единственный способ избежать этого - это весь процесс проверки существующего значения, его вставки и передачи сериализации вставки.

Добавьте к этому проблему, что триггер не может видеть содержимое таблицы она выстреливает против, потому что он мутирует ...

+0

Боюсь, я не понимаю, где он это объясняет. Возможно, вы могли бы перефразировать его аргумент? –

0

После комментариев в некоторых из ответов здесь, я чувствую себя вынужденным показать пример о том, как триггеры могут использоваться для обеспечения соблюдения правил, выходящих за рамки основного RI, предоставляемого СУРБД.

Из нашей системы у нас есть несколько таблиц, все из которых имеют внешние ключи, указывающие на одну и ту же таблицу («MasterTable»). Со временем строки в этих таблицах будут удалены; после удаления последней дочерней строки мы хотим удалить родительскую строку и предпринять некоторые действия. Мы применяем это правило, создав в родительской таблице столбец «ChildCount», который указывает количество строк, относящихся к этому (другими словами, счетчик ссылок).

В вставки триггеров всех различных дочерних таблиц, у нас есть код, который выглядит следующим образом:

 SELECT ChildCount 
     INTO numrows 
     FROM MasterTable mt 
     WHERE :new.MasterTableId = MasterTable.MasterTableId 
     FOR UPDATE; 

     UPDATE MasterTable 
     SET ChildCount = ChildCount + 1 
     WHERE :new.MasterTableId = MasterTable.MasterTableId; 

В поле Удалять триггеров дочерних таблиц, мы имеем следующее:

 SELECT ChildCount 
     INTO numrows 
     FROM MasterTable 
     WHERE :old.MasterTableId = MasterTable.MasterTableId 
     FOR UPDATE; 

     DELETE MasterTable 
     WHERE ChildCount = 1 
     AND :old.MasterTableId = MasterTable.MasterTableId; 

    IF Sql%RowCount = 0 THEN 
     UPDATE MasterTable 
      SET ChildCount = ChildCount - 1 
      WHERE :old.MasterTableId = MasterTable.MasterTableId; 
    END IF; 

Триггеры обновления содержат оба бита кода.

Ключевым битом в этой логике является использование отдельного оператора select с предложением FOR UPDATE вместо того, чтобы просто обновлять столбец с помощью одного оператора. Это гарантирует, что одновременные транзакции будут сериализованы правильно.

Поскольку MasterTable уже объявила правила исключения-каскада для всех дочерних таблиц, приведенный выше код вызовет ошибки ORA-04091 (mutating table), когда он будет выполняться в контексте удаления строки MasterTable с существующими дочерними элементами, поэтому эти операторы выполняются в контексте блока EXCEPTION, который улавливает и игнорирует эту ошибку.

И наконец, приведенный выше код генерируется для нас автоматически с помощью инструмента CASE, используемого для моделирования данных (ERWin). ERWin позволяет создавать «пользовательские свойства» (UDP), и у него есть язык макросов, который можно использовать для генерации практически любого кода, который вам нужен на основе вашей схемы, поэтому все, что нам нужно сделать, чтобы включить эту функцию, - это добавить ChildCount столбца в соответствующую родительскую таблицу и установите для UDP для «Ref-Counted Relationship» значение true.

Как я уже отмечал выше, триггеры не могут использоваться для замены объявленного RI полностью, так как вы не можете использовать FOR UPDATE для правильной работы каскадных правил. Но это здорово для дополнительных правил, подобных этому.

Примечание: этот код существует уже 11 лет - он был разработан, когда мы все еще использовали Oracle 7. Если у кого-то есть более современный способ сделать это с использованием встроенных функций Oracle, я бы заинтересованы в том, чтобы услышать об этом.

0

Лучший способ гарантировать, что родительская запись не может быть вставлена ​​без ребенка использовать INSERT ALL синтаксиса. Это позволяет вставлять записи в нескольких таблиц то же самое заявление.

 
    INSERT ALL 
     INTO parent 
      (pk_col, val1, val2) 
     INTO child1 
      (pk_col, val3, val4) 
    SELECT some_seq.nextval as pk_col 
      , val1 
      , val2 
      , val3 
      , val4 
    FROM where_ever 

WHERE_EVER Стол может быть промежуточной таблицы (возможно, один внешний). В вашем случае это будет DUAL с столбцами, параметрами VAL от подписи вашей хранимой процедуры.

Вы не будете загрузочными для preventinfo мошенника написания кода разработчика Как много последователей вставляют запись без РОДИТЕЛЕЙ РЕБЕНКА п записи. Точно так же вы не можете остановить вставку записи в ребенка2 используя первичный ключ РОДИТЕЛЕЙ Сколько последователей уже есть запись child1. Для этого я боюсь, что вам нужно обзоры кода.

+0

> Вы не можете использовать insert all с предложением where, которое ограничивает его использование. Fnord. Мы можем использовать предложение WHERE в инструкции SELECT. Но это определенно метод, который лучше всего работает, когда таблицы защищены API. – APC

+0

«Вы не можете использовать вставить все с предложением where, которое ограничивает его использование» Я стою исправлен. Я не знаю, как я получил эту информацию, но, очевидно, это было неправильно. Я напишу свой предыдущий комментарий в эфир и уменьшу дезинформацию. – nagul

1

Я думаю, что в данном конкретном случае, изменив модель БД, а не создание скриптов или триггеров, ответ.

Вы уже упоминалось в предыдущем комментарии:

Ну, у меня есть TABLE TABLE_A тогда, TABLE_B и TABLE_C. Каждая запись должна иметь в таблице ТОЧНО одна запись в ОБЕИМ TABLE_A, TABLE_B или TABLE_C. FK ограничение на A, B, C делает только половину. A Таблица А и таблица B может затем оба имеют один и тот же родитель (который я не).

Я хотел бы предложить следующее:

  1. Создать isABC столбец в таблице , который определяет тип оси A, B или C. еще лучше создать новую таблицу с ПК (первичный ключ) или и новый столбец isABC (с той же лошадиных сил и FK на TABLE.PK).
  2. Настроить (PK, isABC) как уникальное ограничение.
  3. Add (PK, isABC) столбцы TABLE_A, TABLE_B и TABLE_C. Сделайте это ограничение FK (внешний ключ) в тех же столбцах в ТАБЛИЦА (или новая таблица).
  4. На каждом из TABLE_A, TABLE_B и TABLE_C, создать проверочное ограничение на колонке isABC, который проверяет значение «А», «В» и «С» соответственно.

Такая конструкция создает дополнительный столбец isABC в TABLE_A, TABLE_B и TABLE_C как цена за соблюдение этого ограничения, но вы получите от волосатых реализаций с помощью скриптов, триггеров или процедур.

+0

Есть ли способ для Oracle заставить это isABC = 'A' для всех записей TABLE_A? Это будет почти постоянный стол или виртуальный столбец, где возвращаемое значение всегда «A», а не для хранения значения «A» для всех записей. – Steven

+0

Другими словами, могу ли я установить ограничение FK, которое проверяет столбец isABC записи PARENT, который соответствует PK? – Steven

+0

В качестве части определения столбца isABC на TABLE_A вы можете добавить ПРОВЕРКА КОНСТРУКЦИИ. например CONSTRAINT isABC_A CHECK (isABC = 'A'). Проверьте эту страницу Oracle для использования примера: http://download.oracle.com/docs/cd/B10501_01/appdev.920/a96590/adg05itg.htm#1704. Я вполне уверен, что вы также можете добавить такие ограничения с помощью команд alter table. – nagul