Как добавить ограничение, которое проверяет сумму в Oracle SQL? - PullRequest
1 голос
/ 27 марта 2019

В настоящее время я занимаюсь школьным проектом, в котором нам нужно создать базу данных для управляющей компании.У нас есть таблица OWNER, таблица BUILDING и таблица OWNERSHIP.

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

CREATE TABLE Building (
    buildingID          NUMBER (10) NOT NULL PRIMARY KEY,
    qtyUnits            NUMBER (3) NOT NULL,
    landValue           NUMBER (15) NOT NULL,
    purchasePrice       NUMBER (15) NOT NULL
);

CREATE TABLE Owners (
    ownerID          NUMBER (5) NOT NULL PRIMARY KEY,
    lastName         VARCHAR2 (50) NOT NULL,
    firstName        VARCHAR2 (50) NOT NULL,
    telephone        VARCHAR2(50) NOT NULL,
    email            VARCHAR2(10) NOT NULL
);

CREATE TABLE Ownership (
    ownerID             NUMBER (5) NOT NULL,
    buildingID          NUMBER (5) NOT NULL,
    ownershipStake      NUMBER (5,2) NOT NULL,
    CONSTRAINT PK_Ownership PRIMARY KEY (ownerID,buildingID)
);

Ответы [ 3 ]

4 голосов
/ 27 марта 2019

Все решения, связанные с триггерами, имеют одну общую проблему: если у вас в системе несколько пользователей, их становится недостаточно, чтобы гарантировать соблюдение ограничения. Например, если сеанс A вставляет ownershipshare из 51%, а сеанс B вставляет ownershipshare из 51%, обе эти вставки будут успешными, поскольку ни один сеанс не зафиксирован. Затем оба сеанса фиксируются, и у вас общая доля владельцев составляет 102%.

Один из способов обойти это - материализованное представление ON COMMIT с ограничением. К сожалению, я думаю, что материализованные представления - это функция, доступная только в Oracle Enterprise Edition, но не Standard или Express. У меня нет экземпляра EE для тестирования, но я думаю, что он делает то, что вы хотите:

create materialized view log on ownership
with primary key, rowid, sequence
( ownershipstake )
including new values;

create materialized view mv_ownership
refresh fast on commit
as
select buildingid, sum(ownershipstake) as total_ownershipstake, count(*) as count_ownershipstake
from ownership
group by buildingid;

alter materialized view mv_ownership add (
    constraint ck_100 check ( total_ownershipstake <= 100 )
);    

Я пошел на небольшую дополнительную работу, чтобы сделать материализованное представление быстро обновляемым, так что не нужно перестраивать все это при каждом коммите, а только поврежденный buildingid.

0 голосов
/ 27 марта 2019

Редактировать : Первоначально удалили это, когда я поняла, что этого недостаточно, если задействовано более одного сеанса.Отмена удаления, чтобы показать пример решения, которое не отображает проблему «таблицы мутаций».Вам придется заблокировать таблицу, чтобы сначала на нее мог влиять только один сеанс.

Вы можете сделать это с помощью триггера AFTER STATEMENT.Это выполняется один раз в insert, update или delete после завершения всего оператора.Это немного неаккуратно, потому что он проверяет все строки в таблице, даже те, которые не были затронуты, но для ваших целей это, вероятно, достаточно хорошо.

create or replace trigger trig1 
after insert or update on ownership
declare
    l_count number;
begin
    select count(*) into l_count from (
      select buildingid, sum(ownershipstake)
        from ownership
      group by buildingid
      having sum(ownershipstake) > 100
    );

    if l_count > 0 then
        raise_application_error( -20001, 'Totals cant be over 100' );
    end if;
end;
/

insert into ownership values ( 1, 1, 99 );

insert into ownership values ( 2, 1, 2 );

Error starting at line : 24 in command -
insert into ownership values ( 2, 1, 2 )
Error report -
ORA-20001: Totals cant be over 100

Как я уже сказал, это проверяет всю таблицу , хотя я только вставил строку, которая затронула 1 здание здесь.Поэтому, если у вас миллион зданий, он без необходимости проверяет 999 999 строк и может оказать значительное влияние на производительность.

Улучшенный способ сделать это - составной триггер, где в момент времени before each row вы бы записалиидентификатор здания изменяемой строки.Затем в момент времени after statement вы будете проверять только те здания, которые были изменены.

0 голосов
/ 27 марта 2019

Прежде всего - вы можете использовать интерфейс для управления этим в отдельном запросе (т. Е. Ограничить максимальную ставку оставшейся суммой).

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

РЕДАКТИРОВАНИЕ: добавление более подробной информации Итак, может быть, вы уже обнаружили, что триггер встретится с «мутирующей таблицей» и задаетесь вопросом «о чем говорит этот парень?» Хорошо, позвольте мне объяснить: это не полный ответ на проблему.

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

  1. Добавить дополнительное поле в таблицу Ownership - назовем его owned_pct
  2. Добавить проверочное ограничение для этого поля, которое говорит: owned_pct <= 100
  3. Создайте триггер AFTER на уровне строк, который обновит это значение, например, для вставки: update Ownership set owned_pct= nvl(owned_pct,0)+:new.ownership_pct where building_id = :new.building_id;
  4. Обратите внимание, что для случаев INSERT / DELETE / UPDATE будут немного отличаться запросы на обновление, поэтому обязательно протестируйте все эти

Этот процесс попытается обновить столбец owned_pct и вызвать нарушение ограничения, что приведет к откату транзакции, включая начальный оператор DML.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...