Вопрос

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

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

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

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

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

Буду признателен за любые идеи альтернативной раскладки стола.

Это было полезно?

Решение

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

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

Другие советы

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

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() возвращается в ваш сеанс.

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

Если да, то все, что вам нужно, это автоинкрементный 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, поэтому решение Билла — это все, что нужно.Я оставляю этот ответ как пример того, как это сделать, если кто-то использует базу данных, которая не поддерживает конструкции идентификации или последовательности.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top