Change tracking is a lightweight solution that provides an efficient change tracking mechanism for applications. Typically, to enable applications to query for changes to data in a database and access information that is related to the changes, application developers had to implement custom change tracking mechanisms. Creating these mechanisms usually involved a lot of work and frequently involved using a combination of triggers, timestamp columns, new tables to store tracking information, and custom cleanup processes.
1. About Change Tracking (SQL Server)
• What rows have changed for a user table?
• Only the fact that a row has changed is required, not how many times the
row has changed or the values of any intermediate changes.
• The latest data can be obtained directly from the table that is being tracked.
• Has a row changed?
• The fact that a row has changed and information about the change must be
available and recorded at the time that the change was made in the same
transaction.
Change tracking is a lightweight solution that provides an efficient change tracking mechanism
for applications. Typically, to enable applications to query for changes to data in a database and
access information that is related to the changes, application developers had to implement
custom change tracking mechanisms. Creating these mechanisms usually involved a lot of work
and frequently involved using a combination of triggers, timestamp columns, new tables to store
tracking information, and custom cleanup processes.
4. ALTER PROCEDURE [dbo].[GETUPLOADSESSIONLOG]
@lastSyncVer BIGINT
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL READ
UNCOMMITTED
DECLAR@minCTVer =
CHANGE_TRACKING_MIN_VALID_VERSION(OBJECT_ID('UPLOADSES
SIONLOG'));
E @minCTVer BIGINT;
SELECT
IF (@lastSyncVer >= 0) AND (@lastSyncVer >= @minCTVer)
BEGIN
SELECT usl.[DATASTOREID], usl.[UPLOADSESSIONID],
usl.[STATUS], usl.[MESSAGE], usl.[DATECREATED]
FROM [dbo].[UPLOADSESSIONLOG] AS usl with(nolock)
INNER JOIN CHANGETABLE(CHANGES
[dbo].[UPLOADSESSIONLOG], @lastSyncVer) AS ct ON usl.[ID] =
ct.[ID]
END
ELSE
BEGIN
SELECT [DATASTOREID], [UPLOADSESSIONID], [STATUS],
[MESSAGE], [DATECREATED]
FROM [dbo].[UPLOADSESSIONLOG] with(nolock)
5. ALTER PROCEDURE [dbo].[GETUPLOADSESSION]
@jobID NVARCHAR(20),
@dataGroup BIGINT,
@lastSyncVer BIGINT
AS
BEGIN
---SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
DECLARE @minCTVer BIGINT;
SELECT @minCTVer = CHANGE_TRACKING_MIN_VALID_VERSION(OBJECT_ID('UPLOADSESSION'));
IF (@lastSyncVer >= 0) AND (@lastSyncVer >= @minCTVer)
BEGIN
SELECT us.[DATASTOREID], us.[UPLOADSESSIONID], us.[UPLOADPATH], us.[STATUS], us.[DATEUPLOADED],
us.[DATECREATED], us.[CHECKSUM], us.[FILESIZE], us.[ID], us.[RERUNFOR]
FROM [dbo].[UPLOADSESSION] us with(nolock)
INNER JOIN CHANGETABLE(CHANGES [dbo].[UPLOADSESSION], @lastSyncVer) AS CT ON us.[ID] = CT.[ID]
INNER JOIN [dbo].[DATASTORE] ds with(nolock) ON us.[DATASTOREID] = ds.[ID]
INNER JOIN [dbo].[DATAGROUP] dg with(nolock) ON ds.[DATAGROUPID] = dg.[ID]
WHERE dg.[ID] = @dataGroup
AND us.[JOBID] = @jobID
AND us.[RERUN] = 0
ORDER BY us.[ID]
END
ELSE
6. • Change Tracking Cleanup
• Track Data Changes (SQL Server)
• Benefits of Using Change Data Capture or Change Tracking
• Feature Differences Between Change Data Capture and Change
Tracking
7. Job 1:
Firstly, to enable tracking on a table, the tracking should be enabled at two levels:
1- The whole database
2- On each table that the user wishes to track.
8. …
server static void aaChangeTracking_1_EnableTracking(Args _args)
{
Query query;
AifChangeTracking changeTracking;
AifChangeTrackingTable ctTable;
;
//Need to be run on server side
new AifChangeTrackingPermission().assert();
info(strFmt("%1", AifChangeTrackingConfiguration::isChangeTrackingEnabled()));
// Run this first to globally enable changeTracking
//This should be run once
AifChangeTrackingConfiguration::enableChangeTracking(true);
// Here we are loading the query that the webservice uses (I took a wild guess to figure this out)
query = new Query("AxdItem");
AifChangeTrackingConfiguration::enableChangeTrackingForQuery("TestScope1", query);
changeTracking = AifChangeTracking::construct(query, "TestScope1", AifChangeTrackingType::SqlChangeTracking);
// Did we enable change tracking?
info(changeTracking.isChangeTrackingEnabled()? "true" : "false");
// did we enable change tracking for this specific query?
info(changeTracking.isChangeTrackingEnabledForQuery()? "q:true" : "q:false");
// redundant check... but anyways its checking if the specific table has change tracking enabled
info(changeTracking.isChangeTrackingEnabledForTable(tableNum(InventTable))? "t:true" : "t:false");
// Revert the permission assert
CodeAccessPermission::revertAssert();
}
9. • Job 2:
• After enabling the tracking in both the database and the tables that
we need to track, we need to link the child tables to the parent table.
This means that if a change occurred (insert or update) in the child
table then the parent table should be touched.
•
10. server static void aaChangeTracking_2_CreateTriggers(Args _args)
{
Query query;
AifChangeTrackingTable ctTable;
;
//Need to be run on server side
new AifChangeTrackingPermission().assert();
query = new Query("AxdItem");
AifChangeTrackingConfiguration::createTouchTriggersForQuery("TestScope1",
AifChangeTrackingTriggerType::AfterInsert, query);
AifChangeTrackingConfiguration::createTouchTriggersForQuery("TestScope1",
AifChangeTrackingTriggerType::AfterUpdate, query);
// Revert the permission assert
CodeAccessPermission::revertAssert();
}
11. • Job 3:
• Tracking the versions will be saved in a table in AX called
AifSQLCTVersion. The following code will update this version control
table:
•
12. static void aaChangeTracking_3_UpdateTrackingVersion(Args _args)
{
container conVersion;
;
//To record the newest version
//There is a standard AX batch job that does this
AifSqlCtChangeTracking::recordCurrentVersion();
}
• This can be run as a batch job by calling the following batch job:
AifChangeTrackingVersionUpdateJob
•
13. • Job 4:
• To find the changes, I created a batch job that will define a query for
the inventTable only. Remember in job 2 we created triggers for the
child tables to touch the inventTable. So, in this case we don’t need to
check the changes in the child tables (e.g. invnetTableModule). we only
need to call the inventTable.
•
14. server static void aaChangeTracking_4_GetChangedTable(Args _args)
{
Query _query;
QueryBuildDataSource _qbds;
AifChangeTracking _changeTracking;
utcDateTime _dateTimeYesterday;
AifChangeTrackingTable ctTable;
;
//Change the date based on what is needed
_dateTimeYesterday = DateTimeUtil::addDays(DateTimeUtil::getSystemDateTime(), -2);
new AifChangeTrackingPermission().assert();
_query = new Query();
_qbds = _query.addDataSource(tableNum(InventTable));
_changeTracking = AifChangeTracking::construct(_query);
_changeTracking.getChanges(_dateTimeYesterday, ctTable);
while select ctTable
{
info(strFmt("%1", ctTable.KeyField_RecId));
}
CodeAccessPermission::revertAssert();
}
15. Opening RPF Files
• In Dynamics AX 2012, we have this concept of pushing and pulling the
data for Retail between Head Office and Store.
• The data is written in XML form, compressed into an rpf file then
saved to a working folder. These rpf files or data packages can be
opened using DDPackView.exe which is available if you install Async
Server (Head Office) or Async Client (Store).
You may find it in:
• C:Program Files (x86)Microsoft Dynamics AX60CDXAsync
ServerPackage
16. If you execute it from here, you will need to specify the actual rpf file you want to check and
click Convert. You might encounter an error:
Could not load file or assembly 'Microsoft.Dynamics.Retail.StoreConnect.RequestHandlerManager,
Version=6.3.0.0, Cuture=neutral, PublicKeyToken=xxxx or one of its dependencies. The system
cannot find the file specified.
17. Usually, I just copy the following in the same folder of DDPackView:
DDPackView.exe.config
• Microsoft.Dynamics.Retail.EventTraceProvider.dll
• Microsoft.Dynamics.Retail.StoreConnect.Request.Compression.dll
• Microsoft.Dynamics.Retail.StoreConnect.Request.Interface.dll
• Microsoft.Dynamics.Retail.StoreConnect.Request.RequestHandlerManager.d
ll
And the RequestHandlers folder which contains:
• Microsoft.Dynamics.Retail.StoreConnect.Request.Base.dll
• Microsoft.Dynamics.Retail.StoreConnect.Request.SQLHandler.dll
You may find all of these from the CDXAsync ClientPackage folder.
I usually have a handy copy of these because it's just easier that way,
18. Anyway, if you're successful with the conversion
you should be able to see something like this:
This is an RPF file of a full sync 1000-Currency job. So if you are going to check the
Scheduler job for 1000, every subjobs here has its corresponding xml file: