1

私は 2 つのテーブルを持っています。それらFooを と と呼びます。1 対多Barの関係で、Fooは の親ですBar。Foo の主キーは、シーケンスで自動的に生成される整数です。

次の制約が与えられた場合、主キーをどのように設定するかによってBar完全に異なります。FooBar

  • Bar のレコードはプログラムによって生成されるため、ユーザー入力を識別子として信頼することはできません。
  • 複数のプロセスが Bar レコードを生成しているため、 Select Max()を生成するために を含むものはすべてID競合状態を示します。

私は満足していない2つの可能な解決策を思いつきました:

  • テーブルを、それらのレコードを一緒にマップする 3 番目のテーブルとの多対多の関係であるかのように扱い、レコード間のマッピングが正しく作成されるように、アプリケーション コードでレコードの挿入を処理します。データベースの設計が誤解を招き、アプリケーション コードのエラーによって無効なデータが発生する可能性があるため、これは好ましくありません。
  • Bar に 2 つの列: FooIDand を指定し、 for someを選択し FooBarIDて値を生成します が、前述のように、競合状態が発生します。FooBarIDmax(FooBarID)+1FooID

別のテーブル レイアウトのアイデアをいただければ幸いです。

4

4 に答える 4

6

Foo と同じように、Bar に自動主キーを与えます。外部キー FooID 列を Bar に追加します。

私が何かを見逃していない限り、それが機能しない理由はないようです。

于 2008-12-10T18:28:14.023 に答える
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()

于 2008-12-10T18:36:30.027 に答える
0

あなたの説明から、私はあなたのデータベースが自動インクリメント識別子フィールドをサポートしていないと仮定しています(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 タグを見逃していたので、必要なのは Bill のソリューションだけです。誰かがアイデンティティまたはシーケンス構造をサポートしていないデータベースを使用している場合の方法の例として、この回答を残しています

于 2008-12-10T18:33:09.537 に答える
0

Ant P やその他の回答によると、bar の一意の ID を生成して Foo の ID をドロップするだけではうまくいかない理由もよくわかりません。ただし、自動インクリメント ID が利用できない状況にあると仮定すると、max(barid)+1 の選択を伴わない 2 つの解決策があります。

  1. 一意の ID のテーブルを事前に生成し、トランザクションを使用してテーブルから次に使用可能な ID を取得し、それを (アトミック操作として) 削除します。これは正常に機能しますが、テーブルにデータを入力したままにしておく必要があるという欠点があります。

  2. 主キーとして UUID を生成します。UUID はこの用途には非効率的であるため、これは一般的には適切なオプションではありませんが、追加のインフラストラクチャ テーブルが必要ないという利点があります。UUID ジェネレーターは広く利用可能であり、一部のデータベースには組み込まれています。

于 2008-12-10T18:43:42.510 に答える