Идентификаторы в отношении один ко многим - PullRequest
1 голос
/ 10 декабря 2008

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

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

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

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

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

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

Ответы [ 4 ]

5 голосов
/ 10 декабря 2008

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

Если я что-то упустил, кажется, нет причины, по которой это не сработает.

3 голосов
/ 10 декабря 2008

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

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 голосов
/ 10 декабря 2008

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

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

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

0 голосов
/ 10 декабря 2008

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

Если это так, то все, что вам нужно, это автоинкрементный FooId и автоинкрементный BarId, а у Bar также есть 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

РЕДАКТИРОВАТЬ: Я изначально пропустил тег Oracle, так что решение Билла это все, что необходимо. Оставляю этот ответ в качестве примера того, как это сделать, если кто-то использует базу данных, которая не поддерживает идентифицирующие или последовательные конструкции

...