none
INSERT 時に同じデータを登録させない方法について RRS feed

  • 質問

  • いつもお世話になります。INSERT 時の競合についてアドバイスいただけますでしょうか。

    VS2010(C#) + SQL Server で Windows フォーム アプリケーションを作成しています。

    アプリケーションは、TabelAdapterManager を使ってフォーム上の DataGridView に下記 TableA のデータを表示させ、UpdateAll メソッドで更新(UPDATE または INSERT)する内容です。

    今、SQL Server に TableA があり、no、status、type という 3 列があります。(インデックスキーは別途 ID 列(オートナンバー)です。)

    TableA へデータを挿入(INSERT) する際の仕様は以下となっています。

    ・no、status は任意入力(空値でもOK)。ただし、type は必須入力

    ・no が入力されている場合、TableA に、同じ no と type のレコードがあれば追加させない。

    ・status が入力されている場合、TableA に、同じ status と type のレコードがあれば追加させない

    ーー

    私は、アプリ側で INSERT する際の制御をしたくなかったので、no と type に一意であるインデックス。そして status と type にも一意であるインデックスを作成したかったのですが、上記仕様のため断念しました。

    具体例で申しますと、以下の4行は登録(INSERT)する必要があるため、私が考えた一意であるインデックスを作成できませんでした。

    no | status | type

    ----------------------

    10 | 空 | 99  

    11 | 空 | 99    ← 1 行目の status 、type 値と同じですが、status が空なので登録させる必要があります。

    空 |  1  | 99

    空 |  2 | 99 ← 3 行目の no 、type 値と同じですが、no が空なので登録させる必要があります。

    <ご質問>

    (前提) 

    フォームロード時に、TableAdapter の Fill メソッドで TableA のデータを DataSetに読み込み、DataGridView とバインドしています。

    ーー

    アプリケーションの[登録] ボタン処理は以下となっています。

    (1) TableA に登録するデータ(no、status、type)を 各TextBox に入力させ、ロード時に読み込んだ DataSet の中に以下2つの条件に合致するデータがないかをループで精査し、もしなければ datarow を作成し、TableADataTable に Add します。

      ・no が入力されていて、かつ no と type の同じデータが DataTable に存在しない

      ・status が入力されていて、かつ status と type の同じデータがDataTable に存在しない

    (2)  TableAdapterManager の UpdateAll メソッドで更新します。

    ただ、A さんが以下データを入力して登録する直前に、B さんが以下データを登録していた場合、上記アプリケーションのロジックでは A さんも以下データを登録できてしまうと思います。 

    no | status | type

    ----------------------

    10 | 空 | 99  

    Aさんがアプリを実行した時はまだ B さんは登録しておらず、A さんの DataGridView 上には表示されていませんが、A さんが登録するまでの間に B さんが Aさんと同じデータを登録してしまっているので、A さんの登録を中断させたいです。

    この場合、上記登録処理のロジックで、DataRow を作成して DataTable に Add する前に、1. で読み込んだ DataTable の精査プラス、別途、最新の TableA のデータも検索(精査)する処理を追加するべきでしょうか。

    どんな些細なことでもかまいませんので、ご指摘、アドバイスいただけまですでしょうか。何卒、よろしくお願いいたします。



    • 編集済み yumi08 2016年5月15日 13:33
    2016年5月15日 13:28

回答

  • SQL Serverのバージョンにもよると思いますが、Filtered Indexを使うのではだめでしょうか。

    CREATE TABLE [dbo].[TableA](
    	[id] [bigint] IDENTITY(1,1) NOT NULL,
    	[no] [int] NULL,
    	[status] [int] NULL,
    	[type] [int] NOT NULL
    ) ON [PRIMARY];
    
    CREATE UNIQUE INDEX uqNoType ON TableA([no],[type]) WHERE [no] is NOT NULL;
    CREATE UNIQUE INDEX uqStatusType ON TableA([status],[type]) WHERE [status] is NOT NULL;
    
    GO


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク yumi08 2016年5月16日 11:53
    2016年5月15日 15:27

すべての返信

  • SQL Serverのバージョンにもよると思いますが、Filtered Indexを使うのではだめでしょうか。

    CREATE TABLE [dbo].[TableA](
    	[id] [bigint] IDENTITY(1,1) NOT NULL,
    	[no] [int] NULL,
    	[status] [int] NULL,
    	[type] [int] NOT NULL
    ) ON [PRIMARY];
    
    CREATE UNIQUE INDEX uqNoType ON TableA([no],[type]) WHERE [no] is NOT NULL;
    CREATE UNIQUE INDEX uqStatusType ON TableA([status],[type]) WHERE [status] is NOT NULL;
    
    GO


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク yumi08 2016年5月16日 11:53
    2016年5月15日 15:27
  • > 私は、アプリ側で INSERT する際の制御をしたくなかったので、

    そこは依然として変わってない(SQL Server 側だけで処理したい)ということですか? (書き方が過去形なので)それにはこだわらないということですか?

    質問する際は、一番最初に、何を作っているのか(Windows Forms アプリらしいということは想像できますが)、ご自分の環境(OS, .NET, Visual Studio のバージョン、DB サーバーとそのバージョンなど)に関する情報を書いていただけませんか?
     
    適切に情報が提供されていれば、回答者が質問者さんの状況を的確に把握でき、タイムリーで的を得た回答が得られるということで、質問者さんにもメリットがあります。ガイドラインも出ていますので目を通していただければと思います。
     
    フォーラムのご利用方法(質問の投稿)について
    https://social.msdn.microsoft.com/Forums/ja-JP/b2074c04-2e91-414d-8e9e-d634be311e31

    【追伸】

    何を作っているか(Windows フォーム アプリケーション)、VS のバージョン(2010)は書いてありましたね。すみません。

    • 編集済み SurferOnWww 2016年5月16日 2:13 【追伸】追記
    2016年5月16日 1:18
  • gekka さん、早々にご回答いただきありがとうございました。まさに gekka さんの内容が私の期待していた事です。

    早速ご教示いただいたインデックスを作成して動作確認した結果、私の期待通りの動作となりました。まだ設計段階でしたので本当に助かりました。重ねてお礼申し上げます。

    そして SurferOnWww さん、いつもありがとうございます。OS や SQL Server のバージョンが記載されていませんでした。次回以降は、きちんと環境面もお伝えするようにいたします。

    2016年5月16日 11:53