Когда в базе данных допустимы циркулярные ссылки?
Теоретическая и практическая, любая помощь приветствуется.
Когда в базе данных допустимы циркулярные ссылки?
Теоретическая и практическая, любая помощь приветствуется.
Записи, указывающие на другие записи, полезны в базе данных. Иногда эти записи образуют цикл. Это может быть полезно. Единственное реальное раздражение на практике заключается в том, чтобы избежать нарушения ограничений.
Например, если у вас есть таблица пользователя и транзакции, пользователь может иметь указатель на свою последнюю транзакцию. Сначала необходимо вставить транзакцию, а затем обновить last_transaction_id
до правильного значения. Хотя обе эти записи существуют, вы не можете их стереть, потому что user.last_transaction_id
указывает на transaction.id
и transaction.user_id
указывает на user.id
. Это означает, что пользователь без транзакций имеет значение null last_transaction_id
. Это также означает, что вы должны удалить это поле, прежде чем сможете удалить транзакцию.
Управление этими ограничениями внешнего ключа - это боль, но это, безусловно, возможно. Могут возникнуть проблемы, возникающие при добавлении ограничений в базу данных позже, которые вводят новые круговые зависимости. Вы должны быть осторожны в этой ситуации. Однако, если одна из записей в цикле имеет поле с нулевым внешним ключом, цикл может быть нарушен, и записи могут быть удалены. Обновления обычно не являются проблемой, если вы вставляете записи в правильном порядке.
Рассмотрим города и штаты. Каждый город существует в государстве. в каждом штате есть столица.
CREATE TABLE city (
city VARCHAR(32),
state VARCHAR(32) NOT NULL,
PRIMARY KEY (city),
FOREIGN KEY (state) REFERENCES state (state)
);
CREATE TABLE state (
state VARCHAR(32),
capital_city VARCHAR(32),
PRIMARY KEY (state),
FOREIGN KEY (capital_city) REFERENCES city (city)
);
Первая проблема - вы не можете создать эти таблицы, как показано, потому что внешний ключ не может ссылаться на столбец в таблице, которая еще не существует. Решение состоит в том, чтобы создать их без внешних ключей, а затем добавить внешние ключи.
Вторая проблема - вы не можете вставить строки ни в одну из таблиц, так как для каждой вставки потребуется предварительно существующая строка в другой таблице. Решение состоит в том, чтобы установить один из столбцы внешнего ключа должны быть NULL и вставлять эти данные в два этапа. например,
--Create state record
INSERT INTO state (state, capital_city) VALUES ('Florida', NULL);
--Create various city records
INSERT INTO city (city, state) VALUES ('Miami', 'Florida');
INSERT INTO city (city, state) VALUES ('Tallahassee', 'Florida');
INSERT INTO city (city, state) VALUES ('Orlando', 'Florida');
--Set one of the cities as the capital
UPDATE state SET capital_city = 'Tallahassee' WHERE state = 'Florida';
Одно из последних дополнений к синтаксису иерархического запроса Oracle - ключевое слово NOCYCLE
- было сделано специально для этой цели - для обработки циркулярных ссылок в данных. Я не вижу в этом ничего плохого, и раньше мне приходилось иметь дело с подобной моделью. Это не слишком сложно, особенно в Oracle, который поддерживает отложенные ограничения.
Это технически возможно, но это может вызвать всевозможные проблемы при удалении записей, поскольку это порождает проблемы с курицей и яйцом. Эти проблемы часто принимают решительные меры, такие как ручное удаление FK и удаление разрешающих элементов для решения.
Если у вас есть отношения вроде:
create table foo_master (
foo_master_id int not null primary key
,current_foo_id int
)
create table foo_detail (
foo_detail_id int not null primary key
foo_master_id int not null
)
alter table foo_master
add constraint fk_foo_current_detail
foreign key (current_foo_id)
references foo_detail
alter table foo_detail
add constraint fk_foo_master
foreign key (foo_master_id)
references foo_master
Тогда удаление записи может вызвать такую проблему с курицей и агг из-за круговых зависимостей.
Лучшая схема для этого выглядит так:
create table foo_master (
foo_master_id int not null primary key
)
create table foo_detail (
foo_detail_id int not null primary key
foo_master_id int not null
is_current char (1)
)
alter table foo_detail
add constraint fk_foo_master
foreign key (foo_master_id)
references foo_master
Это означает, что отношение не является циклическим, и текущая запись foo_detail все еще может быть идентифицирована.
Следует избегать циркулярных ссылок, подобных чуме. Можно установить двухсторонние отношения или даже отношения к себе (если бы вы были таблицей), но круговая зависимость просто требует неприятностей.
Я видел круговые ссылки, сделанные по соображениям производительности. Это выглядит уродливым, но производительность может быть незначительной.
Пример: некоторые доски объявлений (я думаю, phpBB делает это) имеют lastpostid в таблице категорий, которая является ярлыком к последнему сообщению в потоке.
Это создает круг, в котором последнее сообщение имеет FK в таблице категорий, а таблица категорий имеет FK до последнего сообщения.
Как я уже сказал, мне это не очень нравится, но я это видел.
редко я сталкиваюсь с соотношением 1:1, которое необходимо и налагает круговые отношения
обратите внимание, что поля внешнего ключа в такой связи должны быть обнуляемы, иначе вы никогда не сможете удалить строки из таблиц
Я думаю, это не проблема, если вы используете базу данных только для записи. Если вы планируете использовать часть RUD CRUD, вы, вероятно, столкнетесь с (обычно избегаемыми) сложными проблемами при работе с ними.