1

SQLで独自のID(PK)列を維持する必要があるコードがいくつかあります。データを一括挿入するテーブルがありますが、一括挿入が完了する前に関連するテーブルにデータを追加するため、IDENTITY列を使用して値を事前に確認することはできません。

現在のコードは、フィールドのMAX値を選択し、それを1ずつインクリメントしています。アプリケーションの2つのインスタンスが同時に実行される可能性はほとんどありませんが、それでもスレッドセーフではありません(言うまでもなく、毎回データベースに移動します)。

ADO.netエンティティモデルを使用しています。使用するIDの範囲を「予約」するにはどうすればよいですか。その範囲がなくなったら、使用する新しいブロックを取得して、同じ範囲が使用されないことを保証します。

4

7 に答える 7

3

テーブルの構造を変更できる場合は、行生成コードで[SQL]または[C#]uniqueidentifierとともに、PKにaを使用することをお勧めします。newid()Guid.NewGuid()

Guid.NewGuid() docoから:

新しいGuidの値がすべてゼロまたは他のGuidと等しい可能性は非常に低いです。

于 2010-06-16T19:56:30.573 に答える
3
  • の代わりにUNIQUEIDENTIFIER( )のようなよりユニバーサルな一意の識別子データ型を使用します。この場合、基本的にクライアント側で作成し、に渡すことができ、心配する必要はありません。欠点は、もちろん、このフィールドのサイズです。UUIDINTEGERSQL
  • データベースに単純なテーブルを作成し、CREATE TABLE ID_GEN (ID INTEGER IDENTITY)これをとして使用しfactoryて識別子を指定します。理想的には、必要な数の識別子を渡すストアドプロシージャ(または関数)を作成します。ストアドプロシージャは、この数の行(空)をこのID_GENテーブルに挿入IDし、コードで使用できるすべての新しい行を返します。明らかに、元のテーブルにはもうありIDENTITYません。
  • 上記の独自のバリエーションを作成しますID_Factory

UUID他に制約がない場合は、単純さ()を選択します。

于 2010-06-16T20:02:01.387 に答える
2

なぜADO.netEntityFrameworkを使用して、ETLが機能しているように聞こえるのですか?(以下のADO.NET Entity FrameworkとORMの一般的な批評を参照してください。これは無料です)。

なぜintを使用するのですか?uniqueidentifierを使用すると、「実行中のアプリケーションの複数のインスタンス」の問題が解決されます。

列のデフォルトとしてuniqueidentifierを使用すると、intIDENTITYを使用するよりも遅くなります...intよりもguidの生成に時間がかかります。guidは、int(4バイト)よりも大きくなります(16バイト)。最初にこれを試して、許容できるパフォーマンスが得られる場合は、それを使用して実行してください。

各行でGUIDを生成することによって発生する遅延が許容できない場合は、GUIDをまとめて(または別のサーバーで)作成し、テーブルにキャッシュします。

サンプルTSQLコード:

CREATE TABLE testinsert
 (
  date_generated datetime   NOT NULL DEFAULT GETDATE(), 
  guid   uniqueidentifier NOT NULL, 
  TheValue  nvarchar(255)  NULL
 )
GO

CREATE TABLE guids 
 (
  guid   uniqueidentifier NOT NULL DEFAULT newid(), 
  used   bit     NOT NULL DEFAULT 0, 
  date_generated datetime   NOT NULL DEFAULT GETDATE(), 
  date_used  datetime   NULL
 )
GO

CREATE PROCEDURE GetGuid
 @guid uniqueidentifier OUTPUT
AS
BEGIN
 SET NOCOUNT ON
 DECLARE @return int = 0

 BEGIN TRY
  BEGIN TRANSACTION
   SELECT TOP 1 @guid = guid FROM guids WHERE used = 0

   IF @guid IS NOT NULL
    UPDATE guids
    SET 
     used = 1, 
     date_used = GETDATE()
    WHERE guid = @guid
   ELSE
    BEGIN
     SET @return = -1
     PRINT 'GetGuid Error: No Unused guids are available'
    END
  COMMIT TRANSACTION
 END TRY

 BEGIN CATCH
  SET @return = ERROR_NUMBER() -- some error occurred
  SET @guid = NULL
  PRINT 'GetGuid Error: ' + CAST(ERROR_NUMBER() as varchar) + CHAR(13) + CHAR(10) + ERROR_MESSAGE()
  ROLLBACK
 END CATCH

 RETURN @return
END
GO

CREATE PROCEDURE InsertIntoTestInsert
 @TheValue nvarchar(255)
AS
 BEGIN
  SET NOCOUNT ON
  DECLARE @return int = 0

  DECLARE @guid uniqueidentifier
  DECLARE @getguid_return int

  EXEC @getguid_return = GetGuid @guid OUTPUT

  IF @getguid_return = 0 
   BEGIN
    INSERT INTO testinsert(guid, TheValue) VALUES (@guid, @TheValue)
   END
  ELSE
   SET @return = -1

  RETURN @return
 END
GO

-- generate the guids
INSERT INTO guids(used) VALUES (0)
INSERT INTO guids(used) VALUES (0)

--Insert data through the stored proc
EXEC InsertIntoTestInsert N'Foo 1'
EXEC InsertIntoTestInsert N'Foo 2'
EXEC InsertIntoTestInsert N'Foo 3' -- will fail, only two guids were created

-- look at the inserted data
SELECT * FROM testinsert

-- look at the guids table
SELECT * FROM guids

楽しい質問は...これをADO.NetのEntityFrameworkにどのようにマッピングしますか?

これは、ORM(Object Relational Mapping)の初期に始まった古典的な問題です。

リレーショナルデータベースのベストプラクティスを使用する場合(ベーステーブルへの直接アクセスを許可せず、ビューとストアドプロシージャを介したデータ操作のみを許可する)、人数を追加します(データベーススキーマだけでなく、すべてのビューを作成できる人。 APIを形成するストアドプロシージャ)およびプロジェクトに遅延(実際にこのようなものを書き込む時間)を導入します。

したがって、誰もがこれを削減し、人々は正規化されたデータベースに対して直接クエリを記述しますが、それは理解できません...したがって、ORM、この場合はADO.NETEntityFrameworkが必要です。

ORMは私を怖がらせます。ORMツールがひどく非効率的なクエリを生成し、それがなければパフォーマンスの高いデータベースサーバーをひざまずくのを見てきました。プログラマーの生産性で得られたものは、エンドユーザーの待機とDBAのフラストレーションで失われました。

于 2010-06-16T21:11:17.783 に答える
0

2つのクライアントが同じIDブロックを予約できます。

ロックしてインサートをシリアル化する以外に解決策はありません。

MSDNの「ロックのヒント」を参照してください。

于 2010-06-16T20:01:22.587 に答える
0

PKを変更したくない子テーブルがたくさんあると思います。整数filedsaをPLusすると、結合でのパフォーマンスが向上する可能性があります。ただし、GUIDフィールドを追加して、事前に生成された値を一括挿入に入力することもできます。次に、ID挿入をそのままにして(ほとんどの場合、オフにするのは悪い考えです)、事前に生成したGUID値を使用して、子テーブルに挿入するために挿入したID値を取り戻すことができます。

一括挿入の代わりに通常のセットベースの挿入(values句の代わりにselect句を使用するもの)を使用する場合、SQL Server 2008を使用している場合は、output句を使用して行のIDを取得できます。

于 2010-06-16T20:08:58.957 に答える
0

Hi /Loアルゴリズムはあなたにとって興味深いかもしれません:

Hi / Loアルゴリズムとは何ですか?

于 2010-06-16T20:12:19.463 に答える
0

最も一般的な解決策は、データベース識別子とは決して異なるクライアント識別子を生成することです。通常は負の値であり、挿入時にデータベースによって生成された識別子で識別子を更新します。

この方法は、多くのユーザーがデータを同時に挿入するアプリケーションで安全に使用できます。GUID以外の方法は、マルチユーザーセーフではありません。

ただし、エンティティをデータベースに保存する前にエンティティの主キーを知る必要があり、GUIDを使用できないというまれなケースがある場合は、識別子の重複を防ぐ識別子生成アルゴリズムを使用できます。最も簡単なのは、接続されているクライアントごとに一意の識別子プレフィックスを割り当て、このクライアントによって生成された各識別子の前にプレフィックスを付けることです。

ADO.NET Entity Frameworkを使用している場合は、識別子の生成について心配する必要はありません。EFはそれ自体で識別子を生成し、エンティティの主キーをIsDbGenerated=trueとしてマークするだけです。

厳密に言うと、他のORMはオブジェクトの識別子を必要としないため、エンティティフレームワークはまだデータベースに保存されていません。新しいエンティティを正しく操作するには、オブジェクト参照で十分です。実際の主キー値は、エンティティの更新/削除、および新しいエンティティを参照するエンティティの更新/削除/挿入時にのみ必要です。つまり、実際の主キー値がデータベースに書き込まれる場合に必要です。エンティティが新しい場合、新しいエンティティがデータベースに保存されない限り、新しいエンティティを参照する他のエンティティを保存することはできません。ORMは、参照マップを考慮して保存するエンティティの特定の順序を維持します。

于 2010-06-16T20:32:33.927 に答える