SQL устанавливает ограничение на количество обращений к PK - PullRequest
1 голос
/ 12 мая 2019

Я создаю демонстрационную базу данных зоопарка для своего школьного проекта, и я столкнулся со следующей проблемой: у меня есть таблица Pavilion, в которой есть некоторый первичный ключ id_pavilion и емкость столбца (это информация о том, какое наибольшее числоживотные, которые могут жить в этом павильоне).

Допустим, в каждом павильоне может быть максимум 2 животных.

Павильон

id_pavilion   capacity
-----------------------
    1             2
    2             2
    3             2
    4             2

Животное

id_an-column2-column3    id_pavilion
---------------------------------------
   1                          2   
   2                          2   
   3                          2   
   4                          2   

(Это показывает, что я пытаюсь предотвратить)

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

У меня вопрос : как я могу добавить такое ограничение, чтобы на PK id_pavilion из Pavilion можно было ссылаться в таблице Animal только столько раз, сколько позволяет емкость?

Ответы [ 4 ]

2 голосов
/ 12 мая 2019

Глядя на данные вашего примера, можно утверждать, что в каждом павильоне могут разместиться 2 животных, верно? Можно также сказать, что «приспособления» должны быть на месте до содержания животных соответствующим образом. Таким образом, мы могли бы создать таблицу с именем ACCOMMODATION, в которой перечислены все доступные пробелы.

create table pavilion( id primary key, capacity )
as
select level, 2 from dual connect by level <= 4 ;

create table accommodation(
  id number generated always as identity start with 1000 primary key 
, pavilionid number references pavilion( id )
) ;

Генерация всех вариантов размещения

-- No "human intervention" here.  
-- Only the available spaces will be INSERTed.
insert into accommodation ( pavilionid )
select id
from pavilion P1, lateral ( 
  select 1 
  from dual 
  connect by level <= ( select capacity from pavilion where id = P1.id )
) ;

-- we can accommodate 8 animals ...
select count(*) from accommodation ; 

  COUNT(*)
----------
         8

-- accommodations and pavilions
SQL> select * from accommodation ;

        ID PAVILIONID
---------- ----------
      1000          1
      1001          1
      1002          2
      1003          2
      1004          3
      1005          3
      1006          4
      1007          4

8 rows selected.

Каждое животное должно находиться в одном (определенном) месте. Когда животное «добавляется» в зоопарк, оно может (физически) находиться только в одном месте / помещении. Мы можем использовать UNIQUE-ключ и FOREIGN-ключ (ссылающийся на ACCOMMODATION), чтобы обеспечить это.

-- the ANIMAL table will have more columns eg GENUS, SPECIES, NAME etc
create table animal( 
  id number generated always as identity start with 2000
-- , name varchar2( 64 ) 
, accommodation number 
) ;

alter table animal
add (
  constraint animal_pk primary key( id )
, constraint accommodation_unique unique( accommodation )
, constraint accommodation_fk 
    foreign key( accommodation ) references accommodation( id )
);

Тестирование

-- INSERTs will also affect the columns GENUS, SPECIES, NAME etc
-- when the final version of the ANIMAL table is in place. 
insert into animal( accommodation ) values ( 1001 )  ;

SQL> insert into animal( accommodation ) values ( 1000 )  ;

1 row inserted.

SQL> insert into animal( accommodation ) values ( 1001 )  ;

1 row inserted.

-- trying to INSERT into the same location again 
-- MUST fail (due to the unique constraint)
SQL> insert into animal( accommodation ) values ( 1000 );
Error starting at line : 1 in command -
insert into animal( accommodation ) values ( 1000 )
Error report -
ORA-00001: unique constraint (...ACCOMMODATION_UNIQUE) violated


SQL> insert into animal( accommodation ) values ( 1001 );
Error starting at line : 1 in command -
insert into animal( accommodation ) values ( 1001 )
Error report -
ORA-00001: unique constraint (...ACCOMMODATION_UNIQUE) violated

-- trying to INSERT into a location that does not exist
-- MUST fail (due to the foreign key constraint)
SQL> insert into animal( accommodation ) values ( 9999 )  ;
Error starting at line : 1 in command -
insert into animal( accommodation ) values ( 9999 )
Error report -
ORA-02291: integrity constraint (...ACCOMMODATION_FK) violated - parent key not found

Животные и ночлеги

select 
  A.id as animal
, P.id as pavilion
, AC.id as location --(accommodation)
from pavilion P
  join accommodation AC on P.id = AC.pavilionid
  join animal A on AC.id = A.accommodation
;

    ANIMAL   PAVILION   LOCATION
---------- ---------- ----------
      2000          1       1000
      2001          1       1001

DBfiddle здесь . Протестировано с Oracle 12c и 18c. (Для работы LATERAL join вам потребуется версия 12c +.)

0 голосов
/ 12 мая 2019

Вам нужен столбец (скажем, «NrOccupants»), который обновляется, когда животное помещается в каждый павильон или удаляется из него.Затем вы добавляете проверочное ограничение к этому столбцу, которое не позволяет коду приложения добавлять больше животных в павильон, чем разрешено правилом, которое обеспечивается проверочным ограничением.

Вот пример SQL DDL, которыйсделает это.

CREATE SCHEMA Pavilion
GO

CREATE TABLE Pavilion.Pavilion
(
    pavilionNr int NOT NULL,
    capacity tinyint CHECK (capacity IN (2)) NOT NULL,
    nrOccupants tinyint CHECK (nrOccupants IN (0, 2)) NOT NULL,
    CONSTRAINT Pavilion_PK PRIMARY KEY(pavilionNr)
)
GO


CREATE TABLE Pavilion.Animal
(
    animalNr int NOT NULL,
    name nchar(50) NOT NULL,
    pavilionNr int NOT NULL,
    type nchar(50) NOT NULL,
    weight smallint NOT NULL,
    CONSTRAINT Animal_PK PRIMARY KEY(animalNr)
)
GO


ALTER TABLE Pavilion.Animal ADD CONSTRAINT Animal_FK FOREIGN KEY (pavilionNr) REFERENCES Pavilion.Pavilion (pavilionNr) ON DELETE NO ACTION ON UPDATE NO ACTION

GO
0 голосов
/ 12 мая 2019

Ответ "не легко".Хотя идея сохранения "помещений" в павильонах в качестве отдельного стола является разумной, животных помещают в павильоны , а не помещения .Моделирование приспособлений усложняет перемещение животных.

Возможно, самый простой подход - использовать триггеры.Это начинается со столбца animal_count в pavilions.Этот столбец начинается с нуля и увеличивается или уменьшается по мере движения животных внутрь или наружу.Вы можете использовать ограничение check для проверки того, что павильон не перегружен.

К сожалению, для поддержки этого столбца требуются триггеры в таблице animals, один для insert, update иdelete.

В конце концов, триггер поддерживает счет, и если вы попытаетесь поместить животное в полный павильон, вы нарушите ограничение check.

0 голосов
/ 12 мая 2019

То, что вы пытаетесь применить на уровне базы данных, является скорее правилом «бизнес-логики», а не жестким ограничением данных.Вы не можете реализовать это непосредственно в дизайне ваших таблиц;даже если бы вы могли (как упоминает @serg в комментариях) для подсчета потребовалась бы очень дорогая (с точки зрения ЦП / ресурсов) блокировка таблицы.

Другой вариант, который бы достиг вашей целии отделить бизнес-логику от структуры данных, используя SQL Trigger .

Триггер может быть запущен до того, как данные будут вставлены в вашу таблицу;здесь вы можете проверить, сколько строк уже было вставлено для этой «сущности павильона», и отменить или разрешить вставку.

Комментарий для стороны «школьного проекта»: Это существоТем не менее, та логика, о которой вы говорите, гораздо лучше обслуживается вашим приложением-потребителем, а не базой данных (по моему мнению, другие могут не согласиться).Также, возможно, подумайте об определении предельного размера данных, чтобы у вас были павильоны разных размеров.

Примечания: Для тех, кто посещает этот вопрос в будущем, приведенная выше ссылка предназначена для оракула.триггер (как OP пометил вопрос для оракула).T его ссылка для триггеров Microsoft SQL Server .

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