DBMotoを使用してOracleのデータを差分レプリケーションする場合、Redoログを参照して直接トランザクションを確認します。
しOracleの順序オブジェクト(シーケンス)はトランザクション処理ではなくRedoログに情報が記録されないため、DBMotoで普通にレプリケーションを行うことができません。
DBMotoをディザスタリカバリ等のBCP目的で使用される場合には、テーブルデータは同期が取れても順序オブジェクトの同期が取れないということになり、この場合は順序オブジェクトを手動で移動したりシーケンスの最後の値を手動で設定する等が必要になります。
しかしDBMotoの標準機能であるスクリプトを記述することで、疑似的ではありますが順序オブジェクトのレプリケーションが可能となります。
●順序オブジェクトのレプリケーションを実現させるための準備と流れ
1. ソースとターゲットのシーケンスを予め[NOCACHE]にします。
⇒ALTER SEQUENCE シーケンス名 NOCACHE
2. ソースとターゲットDBにダミーテーブルを準備します。(レコード数は0で問題ありません)
3. ダミーテーブルを使用したリフレッシュのレプリケーション定義を作成します。
4. レプリケーション定義に対してリフレッシュを定期間隔(1分おき等)で実行するようスケジュール設定します。
5. レプリケーション定義に対して、下記からダウンロード可能なスクリプトを適用します。
ダミーテーブルを使用してスケジュールリフレッシュ毎にスクリプトを実行し、スクリプトから順序オブジェクトをレプリケーションする、という流れになります。
スクリプトは下記よりダウンロードいただけます。
Imports System Imports System.Data Imports System.Collections Imports Microsoft.VisualBasic Imports DBMotoPublic Imports DBMotoScript Imports DBRS.GlobalScript Namespace DBRS Public Class SequenceName Public owner As String Public name As String Public incrementBy As Integer Public lastNumber As Integer End Class Public Class ReplicationScript : Inherits IReplicationScript Dim sequenceArrayList As New ArrayList() Public Overrides Sub Record_onBeforeMapping(recSource As IRecord, ByRef AbortRecord As Boolean) ' Do this so that no record is written to the target dummy table AbortRecord = True End Sub Public Overrides Sub Refresh_onPrepareRefresh(ByRef CancelRefresh As Boolean, ByRef Filter As String) ' Here, we are identifying and initializing the sequences we want to replicate AddLog("debug> Refresh_onPrepareRefresh()", 0) sequenceArrayList.Clear() ' ここにスキーマ名とシーケンス名を設定します。 Dim selectCondition = "SEQUENCE_OWNER = 'schemaname' AND SEQUENCE_NAME LIKE '%SEQ%'" Dim conn as IDbConnection = Nothing Dim cmd as IDbCommand = Nothing Dim reader as IDataReader = Nothing Dim opened As Boolean = False Try conn = InternalSourceConnection If conn.State <> ConnectionState.Open Then AddLog("debug> open source connection because it hasn't been", 0) conn.Open opened = True End If cmd = conn.CreateCommand cmd.CommandText = "SELECT SEQUENCE_OWNER, SEQUENCE_NAME, INCREMENT_BY, LAST_NUMBER FROM ALL_SEQUENCES WHERE " & selectCondition & " ORDER BY 1,2" AddLog("debug> cmd.CommandText = " & cmd.CommandText, 0) reader = cmd.ExecuteReader While reader.Read Dim seq = new SequenceName seq.owner = reader.GetString(0) seq.name = reader.GetString(1) seq.incrementBy = reader.GetDecimal(2) seq.lastNumber = reader.GetDecimal(3) sequenceArrayList.add(seq) AddLog("debug> " & seq.owner & "." & seq.name & " incr:" & seq.incrementBy.ToString & " lastnum: " & seq.lastNumber , 0) End While If sequenceArrayList.Count = 0 Then AddLog("Warning, no sequences will be replicated.",0) AddLog("This statement returns no rows: [" + cmd.CommandText + "]",0) End If Catch ex As Exception AddLog("Exception in Internal Target Connection: " & ex.ToString,0) Finally If (not reader is Nothing) Then reader.Close End If If (not cmd is Nothing) Then cmd.Dispose End If If opened = True Then conn.Close End If End Try AddLog("debug> sequenceArrayList.Count = " & sequenceArrayList.Count.ToString, 0) End Sub Public Overrides Sub Refresh_onAfterRefresh() AddLog("debug> Refresh_onAfterRefresh()...", 0) Dim conn as IDbConnection = InternalTargetConnection Dim cmd as IDbCommand = conn.CreateCommand Dim cmd2 as IDbCommand = conn.CreateCommand Dim reader as IDataReader = Nothing Dim opened As Boolean = False Try If conn.State <> ConnectionState.Open Then AddLog("debug> open target connection because it hasn't been", 0) conn.Open opened = True End If Dim seq As SequenceName For Each seq In sequenceArrayList cmd.CommandText = "select LAST_NUMBER from ALL_SEQUENCES where SEQUENCE_OWNER='" & seq.owner & "' AND SEQUENCE_NAME='" & seq.name & "'" AddLog("debug> lastNumber = " & seq.lastNumber.ToString & ", target sql = " & cmd.CommandText, 0) reader = cmd.ExecuteReader Dim nextval As Integer = -1 If reader.Read Then nextval = reader.GetDecimal(0) AddLog("debug> target nextval = " & nextval.ToString, 0) reader.Close While (seq.incrementBy > 0 AND seq.lastNumber > nextval) OR (seq.incrementBy < 0 AND seq.lastNumber < nextval) AddLog("debug> 1) lastNumber " & seq.lastNumber.ToString & " > nextval " & nextval.ToString, 0) cmd2.CommandText = "select " & seq.owner & "." & seq.name & ".NEXTVAL from ALL_OBJECTS where ROWNUM <= ABS(" & ((seq.lastNumber-nextval)/seq.incrementBy).ToString & ")" AddLog("debug> cmd2.CommandText = " & cmd2.CommandText, 0) reader = cmd2.ExecuteReader While reader.Read 'AddLog("debug> 2) lastNumber " & seq.lastNumber.ToString & " > nextval " & nextval.ToString, 0) nextval = reader.GetDecimal(0)+seq.incrementBy End While reader.Close End While End If Next Catch ex As Exception AddLog("Exception in Internal Target Connection: " & ex.ToString,0) Finally if (not reader is Nothing) Then reader.Close End If if (not cmd is Nothing) Then cmd.Dispose End If If opened = True Then conn.Close End If End Try End Sub End Class End Namespace
●スクリプトの内容
1. ソースとターゲット側のシーケンスの最後の値を抽出します。
2. 両者の値を比較し、【ソース>ターゲット】の場合には【ソース=ターゲット】になるまで、ターゲット側のシーケンスに対してNEXTVALを実行します。
●スクリプト適用手順
メタデータは下記の構成となっています。
・テーブル「EMPLOYEE」が順序オブジェクトの疑似レプリケーション用ダミーテーブル(レコードはなし)
・テーブル「TABLE1」が順序オブジェクトを使用している実テーブル
・レプリケーション「TABLE1」が、実テーブルTABLE1のミラーリング構成
・レプリケーション「順序レプリケーション」は、ダミーテーブルEMPLOYEEのリフレッシュ構成
⇒これに対してスクリプトを適用します。
リフレッシュスケジュールを1分間隔で実行するよう設定します。
「スクリプトを使用」にチェックを入れ、スクリプト画面を開きます。
スクリプトをコピーして適用します。
関連したトピックス
- ミラーリングおいてのレコード数を確認の方法【リアルタイムレプリケーションツールDBMoto】
- 2つのソーステーブルからターゲットの一つのレコードにレプリケーション
- [Syniti(旧DBMoto)]レコード競合(コンフリクト)発生時のレコード内容を出力する方法
- [DBMoto][スクリプト]条件付きレプリケーションを行うサンプルVBスクリプト
- [DBMoto]関数を使用してレプリケーション時のデータを変換する方法(VB/C#言語選択、関数適用、ユーザ関数作成、置換関数、一括設定手順)
- [HiT JDBC/400]AS/400接続用サンプルJavaプログラム(JDBCドライバ)
- レプリケーションの際にnullを特定の値に変換する方法 その2【リアルタイムレプリケーションツールDBMoto】
- [DBMoto][スクリプト]カラムを結合・分割したレプリケーション
- [HiT JDBC/400] AS/400データ登録・更新・削除用サンプルJavaプログラム(JDBCドライバ)
- [DBMoto]「未マッピング使用」機能によるマッピング外のデータを活用したレプリケーション