Как ограничить вставку дочерних записей в N записей? - PullRequest
0 голосов
/ 23 марта 2020

Я использую Oracle базу данных. У меня есть две таблицы, скажем, Таблица A и Таблица B. Они имеют отношение 1: N между ними. Мне нужно ограничить 'N' указанным c числом, например 10. Мне также понадобится этот n-й номер для каждой вставленной записи.

Данные вставляются через приложение Java, которое развернут на нескольких узлах.

Таблицы B выглядят примерно так (ForeignId будет первичным ключом таблицы A):

+----+----------+----------+-----------+
| Id |   Data   | UniqueId | ForeignId |
+----+----------+----------+-----------+
|  1 | Somedata |        1 |         1 |
|  2 | Somedata |        2 |         1 |
|    |          |          |           |
+----+----------+----------+-----------+

Решение, которое я придумала:

  • Я создал составной ключ (UniqueId, ForeignId) с уникальным ограничением.
  • Включена автоматическая фиксация.
  • Значение UniqueId вычисляется оператором выбора при вставке данных.
  • Если мы получаем исключение из уникального ограничения, мы повторяем попытку .
  • Если значение UniqueId> 10, мы удаляем строку и выполняем откат.

Это решение до сих пор работало, но я считаю, что должен быть какой-то лучший способ добиться этого .

Я переусердствовал в этом?

Ответы [ 2 ]

0 голосов
/ 23 марта 2020

У меня не может быть значения 11. Это должно быть 1-10

. Вы можете использовать проверочное ограничение, чтобы предотвратить добавление значения выше 10; как набросок (например, вам может потребоваться ограничиться целыми числами и использовать более значимые имена ...):

create table a (
  id number,
  constraint a_pk primary key (id)
);

create table b (
  id number,
  data varchar2(10),
  foreign_id number not null,
  unique_id number not null,
  constraint b_pk primary key (id),
  constraint b_fk foreign key (foreign_id) references a (id),
  constraint b_uk unique (foreign_id, unique_id),
  constraint b_ch check (unique_id between 1 and 10)
);

insert into a (id) values (101);
insert into a (id) values (102);

Затем вы можете вставить до 10:

insert into b (id, data, foreign_id, unique_id)
select 1, 'SomeData', 101, nvl(max(unique_id), 0) + 1
from b
where foreign_id = 101;

insert into b (id, data, foreign_id, unique_id)
select 2, 'SomeData', 101, nvl(max(unique_id), 0) + 1
from b
where foreign_id = 101;

...

insert into b (id, data, foreign_id, unique_id)
select 10, 'SomeData', 101, nvl(max(unique_id), 0) + 1
from b
where foreign_id = 101;

но попытка вставить 11-е значение не удалась:

insert into b (id, data, foreign_id, unique_id)
select 11, 'SomeData', 101, nvl(max(unique_id), 0) + 1
from b
where foreign_id = 101;

ORA-02290: check constraint (MY_SCHEMA.B_CH) violated

Попытка вставить дубликат также все равно не удалась:

insert into b (id, data, foreign_id, unique_id)
values (11, 'SomeData', 101, 1);

ORA-00001: unique constraint (MYSCHEMA.B_UK) violated

Но у вас есть один и тот же «уникальный» идентификатор для разных внешние ключи:

insert into b (id, data, foreign_id, unique_id)
select 11, 'SomeData', 102, nvl(max(unique_id), 0) + 1
from b
where foreign_id = 102;

select * from b;

        ID DATA       FOREIGN_ID  UNIQUE_ID
---------- ---------- ---------- ----------
         1 SomeData          101          1
         2 SomeData          101          2
         3 SomeData          101          3
         4 SomeData          101          4
         5 SomeData          101          5
         6 SomeData          101          6
         7 SomeData          101          7
         8 SomeData          101          8
         9 SomeData          101          9
        10 SomeData          101         10
        11 SomeData          102          1

То, как я получаю следующий уникальный идентификатор для каждого внешнего ключа, означает, что если вы удаляете строку, вы не можете повторно использовать ее идентификатор, если только не удаляется тот, который вы удалили. быть самым высоким значением в любом случае. Вы можете сделать вставку более сложной, если вы хотите заполнить пробелы, но вы еще не показали, как вы рассчитываете это, поэтому вы можете не захотеть. Грубый способ, опять же:

delete from b where id = 3;

insert into b (id, data, foreign_id, unique_id)
values (12, 'SomeData', 101,
  (
    select min(n) from (select level as n from dual connect by level <= 10) x
    where not exists (
      select null from b where b.foreign_id = 101 and b.unique_id = x.n
    )
  )
);

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

db < > fiddle


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

0 голосов
/ 23 марта 2020

Это сложно реализовать. Один из методов:

  • Добавить столбец к A, который является числом B с.
  • Поддерживать этот столбец с помощью триггеров вставки / обновления / удаления на B .
  • Добавьте проверочное ограничение к A, чтобы число никогда не превышало 10.

Другой метод заключается в предварительном заполнении B. Каждый раз, когда вы вставляете A, создайте 10 B с. Убедитесь, что у вас есть столбец "in_use" в B. Затем ваши "вставки" в B через обновления, а не insert.

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