Změna ID v datovém slovníku Dynamics AX

Pokud se v Dynamics AX změní ID tabulky nebo pole, dojde při synchronizaci ke ztrátě dat, protože je nejprve zahozena tabulka se starým ID (která obsahuje data) a následně je vytvořena nová tabulka se stejnou strukturou, ale jiným ID. To samé platí analogicky pro pole v tabulce.

Zabránit této ztrátě dat lze několikerým způsobem (třeba exportem a opětovným importem dat), ale jedno řešení je velmi prosté a bezbolestné. Uvědomte si, jak vypadá situace v AX po změně ID (třeba po instalaci vrstvy s jinými ID) ale ještě před synchronizací databáze:

  1. Data jsou stále v databázi
  2. Metadata tabulky v AOT obsahují nové ID
  3. Data v tabulce SqlDictionary obsahují staré ID

Není tedy třeba exportovat data nebo zaznamenávat stará ID, veškeré informace jsou stále v systému. Jen musíte zajistit, aby nedošlo k synchronizaci předčasně.

Porovnáním hodnot v AOT a v SqlDictionary lze identifikovat všechna změněná ID. A aktualizací SqlDictionary na novou hodnotu ID lze zabránit přegenerování databázových objektů během synchronizace.

Já k tomu používám několik scriptů, zde je nejjednodušší z nich:

Dictionary dictionary = new Dictionary();
SysDictTable dictTable;
DictField dictField;
TableId tableId;
FieldId fieldId;
SqlDictionary sqlDictionaryTable;
SqlDictionary sqlDictionaryField;
 
setPrefix("Aktualizace ID v datového slovníku");
tableId = dictionary.tableNext(0);
ttsbegin;
 
while (tableId)
{
    dictTable = new SysDictTable(tableId);
    setPrefix(dictTable.name());
 
    if (!dictTable.isSystemTable())
    {
        //Najde tabulku v SqlDictionary dle jména v AOT, pokud se změnilo ID.
        //Prázné číslo pole říká, že jde o tabulku.
        select sqlDictionaryTable
            where sqlDictionaryTable.name == dictTable.name()
            && sqlDictionaryTable.fieldId == 0
            && sqlDictionaryTable.tabId != dictTable.id();
 
        if (sqlDictionaryTable)
        {
            //Aktualizuje ID tabulky SqlDictionary
            if (ReleaseUpdateDB::changeTableId(
                sqlDictionaryTable.tabId,
                dictTable.id(),
                dictTable.name()))
            {
                info(strFmt("ID tabulky změněno (%1 -> %2)", sqlDictionaryTable.tabId, dictTable.id()));
            }
        }
 
        fieldId = dictTable.fieldNext(0);
 
        //Pro všechna pole v tabulce
        while (fieldId)
        {
            dictField = dictTable.fieldObject(fieldId);
 
            if (dictField.isSql() && !dictField.isSystem())
            {
                //Najde pole v SqlDictionary podle jména a porovná ID
                select sqlDictionaryField
                    where sqlDictionaryField.tabId == dictTable.id()
                    && sqlDictionaryField.name == dictField.name()
                    && sqlDictionaryField.fieldId != 0
                    && sqlDictionaryField.fieldId != dictField.id();
 
                if (sqlDictionaryField)
                {
                    //Aktualizuje ID pole v SqlDictionary
                    if (ReleaseUpdateDB::changeFieldId(
                        dictTable.id(),
                        sqlDictionaryField.fieldId,
                        dictField.id(),
                        dictTable.name(),
                        dictField.name()))
                    {
                        info(strFmt("Pole %1 - ID změněno (%2 -> %3)",
                            dictField.name(),
                            sqlDictionaryField.fieldId,
                            dictField.id()));
                    }
                }
            }
            fieldId = dictTable.fieldNext(fieldId);
        }
    }
    tableId = dictionary.tableNext(tableId);
}
ttscommit;

Pokud mě paměť neklame, tento script funguje v AX4 – AX2012, ale Axapta 3.0 nemá v ReleaseUpdateDB metody changeTableId() a changeFieldId() a musíte si je tam naimplementovat sami.

Často se také zapomíná na to, že ID objektů se vyskytují také v uživatelských datech v databázi, příkladem může být ID tabulky, ke které je přiřazen nějaký dokument. Ignorování tohoto problému může narušit integritu databáze, kterou uživatel opět vnímá jako ztrátu dat. Jedním z možných řešení je použít můj DataReferenceSearcher – ten sice nalezené reference přímo neopravuje, ale můžete díky němu napsat potřebné scripty pro upgrade dat.