SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

既存のRDBシステムに会員情報の名寄せ機能を追加する

名寄せに関連して必要になる処理
―ASP.NETでの実装方法

既存のRDBシステムに会員情報の名寄せ機能を追加する 第3回

  • X ポスト
  • このエントリーをはてなブックマークに追加

ロジックの定義

 「Unify.aspx.vb」にあり、プロシージャ構成は前回とまったく同じです。

 ただし、今回はトランザクション処理を利用する名前空間のインポート宣言として、冒頭の部分に「Imports System.Transactions」と記述する必要があります。

 また、前回と異なるのは「btnUnify_Click」プロシージャのみで、「トランザクション処理」と「関連テーブルの更新」のコードを追加します。

 ASP.NETでトランザクション処理を行う方法の一つとしてSqlTransactionを用いる「マニュアルトランザクション」があり、コーディングは以下のようになります。

リスト4 マニュアルトランザクションのコーディング例
Try
    sqlcon.Open() 'データベースの接続
    'トランザクションの開始
    Dim sqltran As SqlTransaction = sqlcon.BeginTransaction(IsolationLevel.Serializable)
    'SQLコマンドの実行
    sqlcmd = New SqlCommand("UPDATE Member ~", sqlcon) '(1)SQLコマンドの生成
    sqlcmd.Transaction = sqltran '(2)SQLコマンドをトランザクションに参加させる
    Dim affectedRow as Integer = sqlcmd.ExecuteNonQuery() '(3)SQLコマンドの実行
    If affectedRow = 0 Then '(4)実行結果が失敗だった場合
        sqltran.Rollback() 'ロールバック
    End If
    '上記(1)~(4)をSQLコマンドの数だけ繰り返す
    sqltran.Commit() 'ここまで来たら、すべてのSQLコマンドが成功したのでコミット
Finally
    sqlcon.Close() '接続を閉じる
End Try

 もう一つの方法として、TransactionScopeを用いる「自動トランザクション」があります。

 今回は以下の理由により、「自動トランザクション」による実装例を紹介します。

  1. TransactionScopeで囲むだけの、シンプルなコーディングが可能なため
  2. お使いのシステムへの実装で分散トランザクション(後述)が必要になった場合、マニュアルトランザクションでは対応できなくなるため
リスト5 btnUnify_Click(自動トランザクション処理部分)
'テーブルの更新処理
Try
    Using scope As New TransactionScope 'トランザクション処理
        Using SqlConnection As New SqlConnection(constr)
            '(会員テーブルの更新)
                '(1)新規レコードの追加
                '(2)新しい会員IDの取得
                '(3)旧レコードの更新(楽観的同時実行制御)
            '(関連テーブルの更新)
                '(4)名寄せ先対象会員の利用サービスIDの取得
                '(5)関連テーブルの名寄せ先の処理
                '(6)関連テーブル名寄せ元の処理
        End Using
        If lblErrMsg.Text = "" Then scope.Complete() '(7)コミット処理
    End Using
Catch ex As TransactionAbortedException
    lblErrMsg.Text &= "データベースの更新に失敗しました。"
Catch ex As Exception
    lblErrMsg.Text &= "処理に失敗しました。<br />" & ex.Message & "<br />" & strSQL
End Try

 トランザクション処理は、SystemTransaction名前空間のTransactionScopeクラスでRDB操作全体を囲むだけで実現できます。具体的には「Using scope As New TransactionScope」と「End Using」で囲んだ間が一連のトランザクションと見なされます。操作がすべて成功した場合は、Completeメソッドによりコミットできます。

 システムエラー発生時は自動的にロールバックが行われ、TransactionAbortedException例外が発生しますので、Try~Catch命令で捕捉して必要な処理を行います。

 また、業務エラーが発生した場合も、(7)のようにTransactionScope 内でCompleteメソッドを実行しないことにより、自動的にロールバックが行われます。

 会員テーブルの更新部分は前回とまったく同じです。処理イメージを再掲しておきます。

図12.会員テーブルの更新
図12.会員テーブルの更新

 ただし、「(3)旧レコードの更新(楽観的同時実行制御)」部分のUPDATE文では、WHERE句の条件にMemberIDだけでなく、すべてのフィールドが、元のデータと同じであることを追加しています。また、名寄せ処理が他のユーザにより行われた場合も考慮し、Status='A'の条件(これによりUnifiedIDのチェックは不要)も追加しています。

 UPDATE文は旧会員IDの1レコードごとに行い、実行により影響を受けた行数が1でなかったら、他のユーザによりデータが更新されたことになります。この場合業務エラーとしlblErrMsg.Textにメッセージを設定し、(7)でCompleteメソッドを実行しないことにより、ロールバックが行われるようにしています。

リスト6 btnUnify_Click(会員テーブルの楽観的同時実行制御の部分)
'会員テーブル名寄せ元の処理(楽観的同時実行制御)
'UPDATE文の会員共通部分の文字列を組み立てる
strSQL = "UPDATE Member SET Status='U', UnifiedID=" & newMemberID
For col As Integer = 0 To oldMemberID.Length - 1 '会員数だけ繰り返す
    'UPDATE文のWHERE句の条件を組み立てる
    Dim strWHERE As String = " WHERE MemberID=" & oldMemberID(col) '会員ID条件
    For row As Integer = 1 To aryField.Length - 1 '会員ID以外のデータの条件を追加
        If data(row, col) <> "'NULL'" Then
            strWHERE &= " AND (" & aryField(row) & "=" & data(row, col) & ")"
        Else
            strWHERE &= " AND (" & aryField(row) & " IS NULL)"
        End If
    Next
    strWHERE &= " AND Status='A'" '名寄せ前である条件を追加
    sqlcmd = New SqlCommand(strSQL & strWHERE, sqlcon)
    Dim affectedRows As Integer = sqlcmd.ExecuteNonQuery()
    If affectedRows <> 1 Then 'UPDATE文が失敗した場合
        lblErrMsg.Text &= "会員ID:" & oldMemberID(col) & "のデータが他ユーザにより更新されたので、処理を中断します。<br />"
    End If
Next

 関連テーブルの更新部分は、(4)利用していたサービスIDを配列に格納し、(5)新しい会員IDとサービスIDを持つ新規レコードを関連テーブルに追加し、(6)関連テーブルの元のレコードを名寄せ済みとして更新します。

図13.関連テーブルの更新
図13.関連テーブルの更新

 なお、関連テーブルについては、読み出しから書き込みの間にユーザとの対話時間がなく、瞬時に終了するものと考え、楽観的同時実行制御は省略しています。

分散トランザクションが必要になる場合

 System.Transactions名前空間では、通常はローカルトランザクションとして動作しますが、必要に応じて分散トランザクションに昇格(守備範囲の拡大)します。

 「ローカル」と「分散」トランザクションの違いは、「RDBが1台のサーバーのみにあり、複数の同時接続がない」か「RDBが複数サーバーにまたがるか、複数の同時接続がある」かの違いです。

 今回のサンプルでは1つのRDB、1つの接続なのでローカルトランザクションとして動作しますので心配はありませんが、お使いのシステムに組み込んだ際「サーバー '~' のMSDTCは使用できません」といったエラーが起こる場合は、分散トランザクションが必要な状況になっています。例えば、関連テーブルの更新処理が別のRDBに分散している、関数化(モジュール化)されている中でRDB接続を開いている、といった場合が考えられます。

 このような場合、分散トランザクションを制御するMS-DTC(Microsoft Distributed Transaction Coordinator)サービスが稼働している必要がありますので、サーバーの[管理ツール]から[サービス]を起動し、サービス一覧の中から、「Distributed Transaction Coordinator」を[開始]させてください。

図14.MS-DTCの開始
図14.MS-DTCの開始

まとめ

 3回にわたり、名寄せに必要な処理を一通り紹介しました。

  • (1)SQLによる重複値の抽出
  • (2)泣き別れデータの統合
  • (3)関連テーブルの更新

 (1)では同姓同名データのEXISTS句による鮮やかな抽出、(2)ではASP.NETにおける動的なTableコントロールの扱い、(3)では関連するテーブルの処理とトランザクション、排他制御について取り上げました。皆様がお使いの会員管理や顧客管理のRDBシステムに、名寄せ機能を組み込む指針となれば幸いです。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
既存のRDBシステムに会員情報の名寄せ機能を追加する連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 遠藤 存(エンドウ アリ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/6855 2012/11/13 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング