2008-12-10 5 views
1

У меня есть две таблицы, мы будем называть их Foo и Bar, с отношением один ко многим, где Foo является родителем Bar. Первичным ключом Foo является целое число, автоматически генерируемое с помощью последовательности.Идентификаторы в отношениях друг к другу

Поскольку Bar полностью зависит от Foo как бы я настроить первичный ключ Bar с учетом следующих ограничений:

  • записи для бара программно генерируется так, вводимые пользователем, не может быть полагаться для идентификатора ,
  • Множественные процессы генерации Bar записи, так что ничего вовлекая Select Max(), чтобы генерировать ID бы представить состояние гонки.

Я пришел с двумя возможными решениями, которые я не доволен:

  • Лечить таблицы, как будто они являются многие ко многим отношения с третьей таблицы, которая отображает их записи вместе и имеют приложение , в котором записаны записи кода, поэтому , что отображение между записями создано правильно. Мне не нравится , так как это делает дизайн базы данных вводящим в заблуждение и ошибки в приложении код может привести к недействительным данным.
  • Дайте Bar два colunms: FooID и FooBarID и генерировать значение для FooBarID, выбрав max(FooBarID)+1 для некоторого FooID, но как уже отмечалось ранее это создает условие гонки.

Я ценю любые идеи для альтернативного макета таблицы.

ответ

5

Дайте Bar автоматический первичный ключ так же, как и с Foo. Добавьте столбец FooID внешнего ключа в Bar.

Если мне что-то не хватает, по-видимому, нет причин, по которым это не сработает.

0

из вашего описания Я предполагаю, что ваша база данных не поддерживает поля идентификатора автоматического увеличения (MS SQL делает, у Oracle есть «последовательности», которые так же хороши, если не лучше, я не помню, что у MySql есть).

Если это так, то все, что вам нужно, это автоинкрементным FooId и автоинкрементным BARID и бар также имеет FooId в качестве внешнего ключа

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

create table SystemCounter 
( 
    SystemCounterId int identity not null, 
    BarIdAllocator int 
) 
--initialize SystemCounter to have one record with SystemCounterId = 1 
--and BarIdAllocator = 0 
insert into SystemCounter values (1,0) 
--id allocator procedure 
create procedure GetNextBarId (@BarId int output) AS 
    SET NOCOUNT ON 
    begin tran 
     update SystemCounter set 
      @BarId = BarIdAllocator = BarIdAllocator + 1 
     where SystemCounterId = 1 
    commit 
GO 

к сведению, что, если база данных не поддерживает синтаксис

@BarId = BarIdAllocator = BarIdAllocator + 1 

, то вам нужно сделать это таким образом, вместо

begin tran 
    update SystemCounter set 
     BarIdAllocator = BarIdAllocator + 1 
    where SystemCounterId = 1 
    select 
     @BarId = BarIdAllocator 
    from SystemCounter 
    where SystemCounterId = 1 
commit 

EDIT: Я пропустил тег Oracle первоначально, поэтому решение Билла - это все, что необходимо. Я оставляю этот ответ в качестве примера того, как это сделать, если кто-то использует базу данных, которая не поддерживает конструкции идентификации или последовательности.

+0

Вы заметили, что ОП отметил этот вопрос «оракул»? IDENTITY не поддерживается Oracle, а только SEQUENCE. – 2008-12-10 18:35:37

+0

@ [Билл Карвин]: нет, пропустил это! последовательности в oracle могут использоваться для реализации идентификаторов автоматического инкремента, хотя – 2008-12-10 18:41:23

3

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

INSERT INTO Foo (foo_id, othercolumn) 
    VALUES (FooSeq.NextVal(), 'yadda yadda'); 

INSERT INTO Bar (bar_id, foo_id, extracolumn) 
    VALUES (BarSeq.NextVal(), FooSeq.CurrVal(), 'blah blah'); 
INSERT INTO Bar (bar_id, foo_id, extracolumn) 
    VALUES (BarSeq.NextVal(), FooSeq.CurrVal(), 'bling bling'); 
INSERT INTO Bar (bar_id, foo_id, extracolumn) 
    VALUES (BarSeq.NextVal(), FooSeq.CurrVal(), 'baz baz'); 

CURRVAL() функция последовательности только возвращает последнее значение, сгенерированное этой последовательности во время текущей сессии. Другое одновременное использование этой последовательности не влияет на то, что возвращает CURRVAL() в вашей сессии.

0

Я тоже не вижу ничего, как в случае с Ant P и другими ответами, почему только генерация уникального идентификатора для бара и сброс идентификатора Foo не будут работать. Но предположим, что вы находитесь в ситуации, когда идентификаторы автоинкрементные не доступны, то есть два решения, которые не связаны с выбором Мах (BARID) +1

  1. Предварительно генерировать таблицу уникальных идентификаторов и использовать транзакцию вытащить следующий доступный идентификатор из таблицы и удалить его (как атомную операцию). Это отлично работает, но имеет тот недостаток, что вам нужно, чтобы таблица была заполнена.

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

Смежные вопросы