AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 17.07.2011, 22:59   #1  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Post Программное воссоздание записей SqlDictionary для определенной таблицы
В пору обновления приложения Axapta 3.0 до AX 2009 я столкнулся с серьезной проблемой при подъеме модификаций для таблиц, поля которых в стандарте используют идентификаторы из диапазона usr-слоя (см. тему DAX2009: поля таблиц стандартного приложения с идентификаторами из диапазона usr-слоя). Попытки синхронизации таких таблиц неизменно заканчивались ошибками, а поскольку происходило все на пустой разрабоческой базе, было решено обойти придурь ядра с помощью "программной эмуляции": удалить вообще записи в SqlDictionary для проблемных таблиц и воссоздать их, "как положено". Ниже приводится использовавшийся для этого код, немного сдобренный комментариями, - авось еще кому пригодится. В коде поля типа UtcDateTime обрабатываются специальным образом, обсуждение см. в теме Зачем нужно поле для хранения временной зоны для значений полей типа UtcDateTime?
X++:
public static server void sqlDictionaryFill4Table(tableId _tableId)
{
    #macrolib.DictField
    #define.RecIdBaseType   (49)                // для полей с типом RecId/RefRecId/createdTransactionId/modifiedTransactionId используется не Types::Int64, а этот тип
    #define.TZIDsuffix      ('_TZID')           // для несистемных полей типа UtcDateTime в БД создается дополнительное поле с кодом временной зоны, в которой было записано значение

    SqlDictionary   sqlDict;
    SysdictType     dictType;
    DictTable       dictTable = new DictTable( _tableId );
    DictField       dictField;
    ArrayIdx        arrIdx;
    Counter         numOfSqlFields;             // сколько записей для полей таблицы должно быть в SqlDictionary
    fieldName       fieldName;
    fieldId         fieldId;

    boolean processTableField(DictField _dictField, ArrayIdx _arrIdx, boolean _isTzIdField = false)
    {
        ArrayIdx    dictArrIdx;
        str         infoName;                   // это имя поля сугубо для сообщений
        FieldName   sqlName;
        boolean     ret;
        ;
        if (_isTzIdField)
        {
            if (    _dictField.baseType()   != Types::UtcDateTime
                ||  _dictField.id()         == fieldnum(Common, createdDateTime)
                ||  _dictField.id()         == fieldnum(Common, modifiedDateTime)
               )
            {
                throw error( Error::wrongUseOfFunction( funcname() ) );
            }
            dictArrIdx  = _dictField.arraySize() + _arrIdx;
            sqlName     = _dictField.dateTimeTimeZoneRuleFieldName( _arrIdx - 1 );
            infoName    = _dictField.name() + #TZIDsuffix;
        }
        else
        {
            dictArrIdx  = _arrIdx;
            sqlName     = _dictField.name( DbBackend::Sql, _arrIdx );
            infoName    = _dictField.name();
        }
        select firstonly sqlDict
            where   sqlDict.tabId   == _dictField.tableid()
                &&  sqlDict.fieldId == _dictField.id()
                &&  sqlDict.array   == dictArrIdx
                    ;
        if (!sqlDict)
        {
            sqlDict.clear();
            sqlDict.initValue();
            sqlDict.tabId           = _dictField.tableid();
            sqlDict.fieldId         = _dictField.id();
            sqlDict.array           = dictArrIdx;
            sqlDict.name            = strupr( _dictField.name( DbBackend::Native, _arrIdx ) );
            sqlDict.sqlName         = sqlName;
            dictType                = new SysDictType( _dictField.typeId() );
            if (_isTzIdField)
            {
                sqlDict.fieldType   = Types::Integer;
            }
            else
            if (        _dictField.id()     == fieldnum(Common, RecId)
                ||      _dictField.id()     == fieldnum(Common, createdTransactionId)
                ||      _dictField.id()     == fieldnum(Common, modifiedTransactionId)
                ||      _dictField.typeId() == typeid(RecId)
                ||      _dictField.typeId() == typeid(RefRecId)
                ||  (   dictType
                    &&  dictType.isExtending( extendedtypenum(RecId) )
                    )
               )
            {
                // для RecId в поле fieldType прописывается не Types::Int64, а число 49
                sqlDict.fieldType   = #RecIdBaseType;
            }
            else
            {
                sqlDict.fieldType   = _dictField.baseType();
            }
            sqlDict.strSize         = _dictField.stringLen();
            sqlDict.shadow          = bitTest( _dictField.flags(), #DBF_SHADOW );
            sqlDict.rightJustify    = bitTest( _dictField.flags(), #DBF_RIGHT );
            sqlDict.nullable        = _dictField.baseType() == Types::Container || _dictField.baseType() == Types::VarString;
            sqlDict.flags           = sqlDict.shadow;   // а вот ни фига не _dictField.flags();
            if (sqlDict.validateWrite())
            {
                sqlDict.insert();
                ret = true;
                info( strfmt( @"Создана запись для поля %1.%2%3",
                                dictTable.name(), infoName, _dictField.arraySize() > 1 ? strfmt( @"[%1]", _arrIdx ) : ''
                    ));
                // для всех несистемных полей UtcDateTime создаем также связанное поле TZID
                if (   !_isTzIdField
                    &&  _dictField.baseType()   == Types::UtcDateTime
                    &&  _dictField.id()         != fieldnum(Common, createdDateTime)
                    &&  _dictField.id()         != fieldnum(Common, modifiedDateTime)
                   )
                {
                    processTableField( _dictField, _arrIdx, true );
                }
            }
            else
            {
                ret = checkFailed( strfmt( @"Запись в %1 для поля %2.%3 не была создана", tablestr(SqlDictionary), dictTable.name(), infoName ) );
            }
        }
        return ret;
    }
    ;
    if (!dictTable)
    {
        throw error( strfmt( @"Не удалось создать объект %1 для таблицы '%2' (%3)", classstr(DictTable), tableid2name( _tableId ), _tableId ) );
    }
    if (dictTable.isSystemTable())
    {
        throw error( strfmt( @"Таблица '%1' - системная, на это я пойтить не могу", dictTable.name() ) );
    }
    if (!dictTable.isSql())
    {
        throw error( strfmt( @"Таблицы '%1' вообще не должно быть в базе", dictTable.name() ) );
    }
    for (fieldId = dictTable.fieldNext( 0 ); fieldId; fieldId = dictTable.fieldNext( fieldId ))
    {
        dictField = dictTable.fieldObject( fieldId );
        if (dictField && (dictField.flags() & #DBF_STORE))
        {
            fieldName = dictField.name();
            for (arrIdx = 1; arrIdx <= dictField.arraySize(); arrIdx++)
            {
                numOfSqlFields++;
                processTableField( dictField, arrIdx );
            }
        }
    }
    select firstonly sqlDict
        where   sqlDict.tabId   == _tableId
            &&  sqlDict.fieldId == 0
                ;
    if (!sqlDict)
    {
        sqlDict.clear();
        sqlDict.initValue();
        sqlDict.tabId       = _tableId;
        sqlDict.name        = strupr( dictTable.name() );
        sqlDict.sqlName     = dictTable.name( DbBackend::Sql );
        sqlDict.strSize     = numOfSqlFields;       // для "заголовка" таблицы тут указывается, сколько у нее полей в БД
        sqlDict.flags       = dictTable.isView();   // "мой дедуктивный метод..."
        sqlDict.insert();
        info( strfmt( @"Создана запись для таблицы %1", dictTable.name() ) );
    }
}
За это сообщение автора поблагодарили: raz (15), sukhanchik (10), propeller (1).
Старый 15.02.2012, 19:47   #2  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
! Исправленная версия, учитывающая EDT createdTransactionId и modifiedTransactionId
Как показали полевые испытания, скрипт учитывал не все. Ниже - исправленная версия
X++:
public static server void sqlDictionaryFill4Table(tableId _tableId)
{
    #macrolib.DictField
    #define.RecIdBaseType   (49)                // для полей с типом RecId/RefRecId/createdTransactionId/modifiedTransactionId используется не Types::Int64, а этот тип
    #define.TZIDsuffix      ('_TZID')           // для несистемных полей типа UtcDateTime в БД создается дополнительное поле с кодом временной зоны, в которой было записано значение

    SqlDictionary   sqlDict;
    SysdictType     dictType;
    DictTable       dictTable;
    DictField       dictField;
    ArrayIdx        arrIdx;
    Counter         numOfSqlFields;             // сколько записей для полей таблицы должно быть в SqlDictionary
    fieldName       fieldName;
    fieldId         fieldId;

    boolean processTableField(DictField _dictField, ArrayIdx _arrIdx, boolean _isTzIdField = false)
    {
        ArrayIdx    dictArrIdx;
        str         infoName;                   // это имя поля сугубо для сообщений
        FieldName   sqlName;
        boolean     ret;
        ;
        if (_isTzIdField)
        {
            if (    _dictField.baseType()   != Types::UtcDateTime
                ||  _dictField.id()         == fieldnum(Common, createdDateTime)
                ||  _dictField.id()         == fieldnum(Common, modifiedDateTime)
               )
            {
                throw error(Error::wrongUseOfFunction(funcname()));
            }
            dictArrIdx  = _dictField.arraySize() + _arrIdx;
            sqlName     = _dictField.dateTimeTimeZoneRuleFieldName(_arrIdx - 1);
            infoName    = _dictField.name() + #TZIDsuffix;
        }
        else
        {
            dictArrIdx  = _arrIdx;
            sqlName     = _dictField.name(DbBackend::Sql, _arrIdx);
            infoName    = _dictField.name();
        }
        select firstonly sqlDict
            where   sqlDict.tabId   == _dictField.tableid()
                &&  sqlDict.fieldId == _dictField.id()
                &&  sqlDict.array   == dictArrIdx
                    ;
        if (!sqlDict)
        {
            sqlDict.clear();
            sqlDict.initValue();
            sqlDict.tabId           = _dictField.tableid();
            sqlDict.fieldId         = _dictField.id();
            sqlDict.array           = dictArrIdx;
            sqlDict.name            = strupr(_dictField.name(DbBackend::Native, _arrIdx));
            sqlDict.sqlName         = sqlName;
            dictType                = new SysDictType( _dictField.typeId() );
            if (_isTzIdField)
            {
                sqlDict.fieldType   = Types::Integer;
            }
            else
            if (            _dictField.id()     == fieldnum(Common, RecId)
                ||          _dictField.id()     == fieldnum(Common, createdTransactionId)
                ||          _dictField.id()     == fieldnum(Common, modifiedTransactionId)
                ||          _dictField.typeId() == extendedtypenum(RecId)
                ||          _dictField.typeId() == extendedtypenum(RefRecId)
                ||          _dictField.typeId() == extendedtypenum(createdTransactionId)
                ||          _dictField.typeId() == extendedtypenum(modifiedTransactionId)
                ||  (       dictType
                    &&  (   dictType.isExtending(extendedtypenum(RecId))
                        ||  dictType.isExtending(extendedtypenum(createdTransactionId))
                        ||  dictType.isExtending(extendedtypenum(modifiedTransactionId))
                        )
                    )
               )
            {
                // для RecId в поле fieldType прописывается не Types::Int64, а число 49
                sqlDict.fieldType   = #RecIdBaseType;
            }
            else
            {
                sqlDict.fieldType   = _dictField.baseType();
            }
            sqlDict.strSize         = _dictField.stringLen();
            sqlDict.shadow          = bitTest( _dictField.flags(), #DBF_SHADOW );
            sqlDict.rightJustify    = bitTest( _dictField.flags(), #DBF_RIGHT );
            sqlDict.flags           = sqlDict.shadow;   // а вот ни фига не _dictField.flags();
            sqlDict.nullable        =   _dictField.baseType() == Types::Container
                                    ||  _dictField.baseType() == Types::VarString
                                        ;
            if (sqlDict.validateWrite())
            {
                sqlDict.insert();
                ret = true;
                info(strfmt(@"Создана запись для поля %1.%2%3",
                            dictTable.name(), infoName, _dictField.arraySize() > 1 ? strfmt(@"[%1]", _arrIdx) : ''
                    ));
                // для всех несистемных полей UtcDateTime создаем также связанное поле TZID
                if (   !_isTzIdField
                    &&  _dictField.baseType()   == Types::UtcDateTime
                    &&  _dictField.id()         != fieldnum(Common, createdDateTime)
                    &&  _dictField.id()         != fieldnum(Common, modifiedDateTime)
                   )
                {
                    processTableField(_dictField, _arrIdx, true);
                }
            }
            else
            {
                ret = checkFailed(strfmt(@"Запись в %1 для поля %2.%3 не была создана", tablestr(SqlDictionary), dictTable.name(), infoName));
            }
        }
        return ret;
    }
    ;
    dictTable = new DictTable( _tableId );
    if (!dictTable)
    {
        throw error(strfmt(@"Не удалось создать объект %1 для таблицы '%2' (%3)", classstr(DictTable), tableid2name( _tableId ), _tableId));
    }
    if (dictTable.isSystemTable())
    {
        throw error(strfmt(@"Таблица '%1' - системная, на это я пойтить не могу", dictTable.name()));
    }
    if (!dictTable.isSql())
    {
        throw error(strfmt(@"Таблицы '%1' вообще не должно быть в базе", dictTable.name()));
    }
    for (fieldId = dictTable.fieldNext( 0 ); fieldId; fieldId = dictTable.fieldNext( fieldId ))
    {
        dictField = dictTable.fieldObject( fieldId );
        if (dictField && dictField.isSql())
        {
            fieldName = dictField.name();
            for (arrIdx = 1; arrIdx <= dictField.arraySize(); arrIdx++)
            {
                numOfSqlFields++;
                processTableField( dictField, arrIdx );
            }
        }
    }
    select firstonly sqlDict
        where   sqlDict.tabId   == _tableId
            &&  sqlDict.fieldId == 0
                ;
    if (!sqlDict)
    {
        sqlDict.clear();
        sqlDict.initValue();
        sqlDict.tabId       = _tableId;
        sqlDict.name        = strupr(dictTable.name());
        sqlDict.sqlName     = dictTable.name(DbBackend::Sql);
        sqlDict.strSize     = numOfSqlFields;       // для "заголовка" таблицы тут указывается, сколько у нее полей в БД
        sqlDict.flags       = dictTable.isView();   // "мой дедуктивный метод..."
        sqlDict.insert();
        info(strfmt(@"Создана запись для таблицы %1", dictTable.name()));
    }
}
За это сообщение автора поблагодарили: KiselevSA (5), Владимир Максимов (10), Logger (10), alex55 (5), GBH (1).
Старый 15.02.2012, 22:04   #3  
Pustik is offline
Pustik
Участник
 
807 / 372 (14) ++++++
Регистрация: 04.06.2004
Цитата:
Сообщение от gl00mie Посмотреть сообщение
В пору обновления приложения Axapta 3.0 до AX 2009 я столкнулся с серьезной проблемой при подъеме модификаций для таблиц, поля которых в стандарте используют идентификаторы из диапазона usr-слоя (см. тему DAX2009: поля таблиц стандартного приложения с идентификаторами из диапазона usr-слоя). Попытки синхронизации таких таблиц неизменно заканчивались ошибками, а поскольку происходило все на пустой разрабоческой базе, было решено обойти придурь ядра с помощью "программной эмуляции": удалить вообще записи в SqlDictionary для проблемных таблиц и воссоздать их, "как положено".
[/XPP]
Какая замечательная вещь. Где Вы были раньше?))) Жаль нельзя оставить респект сейчас.
__________________
-Ты в гномиков веришь?
-Нет.
-А они в тебя верят, смотри, не подведи их.
Старый 26.11.2014, 12:37   #4  
KiselevSA is offline
KiselevSA
Злыдни
Аватар для KiselevSA
Злыдни
Лучший по профессии 2015
 
958 / 333 (13) ++++++
Регистрация: 25.01.2002
Адрес: Москва
Джобик отработал на AX2012R3 с одной ошибкой: для поля Partition присвоился тип Int64 вместо типа для RecId (49). Пришлось менять через SQL. Может найдется время для модификации job-а под AX2012?
__________________
люди...считают, что если техника не ломается, то ее не нужно ремонтировать. Инженеры считают, что если она не ломается, то нуждается в совершенствовании.
Старый 26.11.2014, 14:58   #5  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Спасибо за обкатку на AX 2012 R3 Там кроме Partition надо дорабатывать код с учетом наследования таблиц; как будет свободное время, обязательно займусь.
Старый 17.03.2015, 18:28   #6  
propeller is offline
propeller
Участник
Аватар для propeller
 
359 / 29 (1) +++
Регистрация: 25.07.2007
Скрипт хороший, а нет что-нибудь подобного для индексов? или в какой табличке хранятся индексы таблиц?
Синхронизирую одну таблицу, ошибка drop index на индекс, которого в AX 2012 нет. Не пойму, где его почистить ..
Старый 17.03.2015, 21:59   #7  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от propeller Посмотреть сообщение
Скрипт хороший, а нет что-нибудь подобного для индексов? или в какой табличке хранятся индексы таблиц?
Синхронизирую одну таблицу, ошибка drop index на индекс, которого в AX 2012 нет. Не пойму, где его почистить ..
Так нет таблички аналога SQLDictionary для индексов. Так что причина ошибки скорее всего в чем-то другом. Может прав нет на удаление индекса.
Старый 18.03.2015, 02:51   #8  
Alex_KD is offline
Alex_KD
Участник
AxAssist
MCBMSS
Соотечественники
 
522 / 362 (14) ++++++
Регистрация: 06.07.2006
Адрес: Melbourne, Down Under
Цитата:
Сообщение от propeller Посмотреть сообщение
Скрипт хороший, а нет что-нибудь подобного для индексов? или в какой табличке хранятся индексы таблиц?
Синхронизирую одну таблицу, ошибка drop index на индекс, которого в AX 2012 нет. Не пойму, где его почистить ..
Смотрите что у вас на SQL ...

Например, AX R3 не умеет дропать индекс если он PK и таблица синхронизируется с Retail Store. В данном случаем на тиблице включен Change Tracking и SQL не даст вам изменять PK (надо ручками отключать Change Tracking).
__________________
AxAssist 2012 - Productivity Tool for Dynamics AX 2012/2009/4.0/3.0
За это сообщение автора поблагодарили: gl00mie (3), Logger (1).
Старый 18.03.2015, 13:15   #9  
Maxim Gorbunov is offline
Maxim Gorbunov
Administrator
Соотечественники
Лучший по профессии 2009
 
2,483 / 645 (26) +++++++
Регистрация: 27.11.2001
Адрес: Dubai, UAE
А почему бы просто не сделать Check/Synchronize и экспортировать DDL-скрипт? Там все команды для заполнения SqlDictionary будут.

С индексом проблему, кстати, так же можно решить: сделайте Check/Synchronize и запустите скрипт в SQL Management Studio.
__________________
Not registered yet? Register here!
Have comments, questions, suggestions or anything else regarding our web site? Don't hesitate, send them to me
Старый 18.06.2015, 09:46   #10  
UNRW is offline
UNRW
Участник
 
383 / 67 (3) ++++
Регистрация: 16.09.2004
Адрес: Москва
Под 2012 удалось кому-нибудь переделать?
Старый 31.01.2022, 11:49   #11  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,317 / 3547 (125) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
На будущее оставлю здесь ссылку на SQL-скрипт для AX2012
https://github.com/dodiggitydag/AX-2...model%20db.sql
__________________
Возможно сделать все. Вопрос времени
За это сообщение автора поблагодарили: mazzy (2), Logger (5).
Старый 13.04.2022, 11:03   #12  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от KiselevSA Посмотреть сообщение
Джобик отработал на AX2012R3 с одной ошибкой: для поля Partition присвоился тип Int64 вместо типа для RecId (49). Пришлось менять через SQL. Может найдется время для модификации job-а под AX2012?
Исправил.
Еще для гуидов хранится 45 вместо types::Guid

X++:
// [url=https://axforum.info/forums/showthread.php?t=39005&highlight=sqlDictionary]Программное воссоздание записей SqlDictionary для определенной таблицы[/url]
public static server void sqlDictionaryFill4Table(tableId _tableId)
{
    #macrolib.DictField
    #define.RecIdBaseType   (49)                // для полей с типом RecId/RefRecId/createdTransactionId/modifiedTransactionId используется не Types::Int64, а этот тип
    #define.TZIDsuffix      ('_TZID')           // для несистемных полей типа UtcDateTime в БД создается дополнительное поле с кодом временной зоны, в которой было записано значение

    SqlDictionary   sqlDict;
    SysdictType     dictType;
    DictTable       dictTable;
    DictField       dictField;
    ArrayIdx        arrIdx;
    Counter         numOfSqlFields;             // сколько записей для полей таблицы должно быть в SqlDictionary
    fieldName       fieldName;
    fieldId         fieldId;

    boolean processTableField(DictField _dictField, ArrayIdx _arrIdx, boolean _isTzIdField = false)
    {
        ArrayIdx    dictArrIdx;
        str         infoName;                   // это имя поля сугубо для сообщений
        FieldName   sqlName;
        boolean     ret;
        ;
        if (_isTzIdField)
        {
            if (    _dictField.baseType()   != Types::UtcDateTime
                ||  _dictField.id()         == fieldnum(Common, createdDateTime)
                ||  _dictField.id()         == fieldnum(Common, modifiedDateTime)
               )
            {
                throw error(Error::wrongUseOfFunction(funcname()));
            }
            dictArrIdx  = _dictField.arraySize() + _arrIdx;
            sqlName     = _dictField.dateTimeTimeZoneRuleFieldName(_arrIdx - 1);
            infoName    = _dictField.name() + #TZIDsuffix;
        }
        else
        {
            dictArrIdx  = _arrIdx;
            sqlName     = _dictField.name(DbBackend::Sql, _arrIdx);
            infoName    = _dictField.name();
        }
        select firstonly sqlDict
            where   sqlDict.tabId   == _dictField.tableid()
                &&  sqlDict.fieldId == _dictField.id()
                &&  sqlDict.array   == dictArrIdx
                    ;
        if (!sqlDict)
        {
            sqlDict.clear();
            sqlDict.initValue();
            sqlDict.tabId           = _dictField.tableid();
            sqlDict.fieldId         = _dictField.id();
            sqlDict.array           = dictArrIdx;
            sqlDict.name            = strupr(_dictField.name(DbBackend::Native, _arrIdx));
            sqlDict.sqlName         = sqlName;
            dictType                = new SysDictType( _dictField.typeId() );
            if (_isTzIdField)
            {
                sqlDict.fieldType   = Types::Integer;
            }
            else
            if (            _dictField.id()     == fieldnum(Common, RecId)
                ||          _dictField.id()     == fieldnum(Common, createdTransactionId)
                ||          _dictField.id()     == fieldnum(Common, modifiedTransactionId)
                ||          _dictField.id()     == fieldnum(Common, Partition)

                ||          _dictField.typeId() == extendedtypenum(RecId)
                ||          _dictField.typeId() == extendedtypenum(RefRecId)
                ||          _dictField.typeId() == extendedtypenum(createdTransactionId)
                ||          _dictField.typeId() == extendedtypenum(modifiedTransactionId)
                ||          _dictField.typeId() == extendedtypenum(Partition)

                ||  (       dictType
                    &&  (   dictType.isExtending(extendedtypenum(RecId))
                        ||  dictType.isExtending(extendedtypenum(createdTransactionId))
                        ||  dictType.isExtending(extendedtypenum(modifiedTransactionId))
                        ||  dictType.isExtending(extendedtypenum(Partition))
                        )
                    )
               )
            {
                // для RecId в поле fieldType прописывается не Types::Int64, а число 49
                sqlDict.fieldType   = #RecIdBaseType;
            }
            else
            if (            _dictField.BaseType() == Types::Guid
                ||          _dictField.BaseType() == 45 // Types_MRC::SysGuid
               )
            {
                sqlDict.fieldType   = 45;
            }
            else
            {
                sqlDict.fieldType   = _dictField.baseType();
            }
            sqlDict.strSize         = _dictField.stringLen();
            sqlDict.shadow          = bitTest( _dictField.flags(), #DBF_SHADOW );
            sqlDict.rightJustify    = bitTest( _dictField.flags(), #DBF_RIGHT );
            sqlDict.flags           = sqlDict.shadow;   // а вот ни фига не _dictField.flags();
            sqlDict.nullable        =   _dictField.baseType() == Types::Container
                                    ||  _dictField.baseType() == Types::VarString
                                        ;
            if (sqlDict.validateWrite())
            {
                sqlDict.insert();
                ret = true;
                info(strfmt(@"Создана запись для поля %1.%2%3",
                            dictTable.name(), infoName, _dictField.arraySize() > 1 ? strfmt(@"[%1]", _arrIdx) : ''
                    ));
                // для всех несистемных полей UtcDateTime создаем также связанное поле TZID
                if (   !_isTzIdField
                    &&  _dictField.baseType()   == Types::UtcDateTime
                    &&  _dictField.id()         != fieldnum(Common, createdDateTime)
                    &&  _dictField.id()         != fieldnum(Common, modifiedDateTime)
                   )
                {
                    processTableField(_dictField, _arrIdx, true);
                }
            }
            else
            {
                ret = checkFailed(strfmt(@"Запись в %1 для поля %2.%3 не была создана", tablestr(SqlDictionary), dictTable.name(), infoName));
            }
        }
        return ret;
    }
    ;
    dictTable = new DictTable( _tableId );
    if (!dictTable)
    {
        throw error(strfmt(@"Не удалось создать объект %1 для таблицы '%2' (%3)", classstr(DictTable), tableid2name( _tableId ), _tableId));
    }
    if (dictTable.isSystemTable())
    {
        throw error(strfmt(@"Таблица '%1' - системная, на это я пойтить не могу", dictTable.name()));
    }
    if (!dictTable.isSql())
    {
        throw error(strfmt(@"Таблицы '%1' вообще не должно быть в базе", dictTable.name()));
    }
    for (fieldId = dictTable.fieldNext( 0 ); fieldId; fieldId = dictTable.fieldNext( fieldId ))
    {
        dictField = dictTable.fieldObject( fieldId );
        if (dictField && dictField.isSql())
        {
            fieldName = dictField.name();
            for (arrIdx = 1; arrIdx <= dictField.arraySize(); arrIdx++)
            {
                numOfSqlFields++;
                processTableField( dictField, arrIdx );
            }
        }
    }
    select firstonly sqlDict
        where   sqlDict.tabId   == _tableId
            &&  sqlDict.fieldId == 0
                ;
    if (!sqlDict)
    {
        sqlDict.clear();
        sqlDict.initValue();
        sqlDict.tabId       = _tableId;
        sqlDict.name        = strupr(dictTable.name());
        sqlDict.sqlName     = dictTable.name(DbBackend::Sql);
        sqlDict.strSize     = numOfSqlFields;       // для "заголовка" таблицы тут указывается, сколько у нее полей в БД
        sqlDict.flags       = dictTable.isView();   // "мой дедуктивный метод..."
        sqlDict.insert();
        info(strfmt(@"Создана запись для таблицы %1", dictTable.name()));
    }
}
Старый 14.04.2022, 16:09   #13  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Оказывается джоб неверно заполнял поля flags и shadow для вьюх.
Подправил.

X++:
// [url=https://axforum.info/forums/showthread.php?t=39005&highlight=sqlDictionary]Программное воссоздание записей SqlDictionary для определенной таблицы[/url]
public static server void sqlDictionaryFill4Table(tableId _tableId)
{
    #macrolib.DictField
    #define.RecIdBaseType   (49)                // для полей с типом RecId/RefRecId/createdTransactionId/modifiedTransactionId используется не Types::Int64, а этот тип
    #define.TZIDsuffix      ('_TZID')           // для несистемных полей типа UtcDateTime в БД создается дополнительное поле с кодом временной зоны, в которой было записано значение

    SqlDictionary   sqlDict;
    SysdictType     dictType;
    DictTable       dictTable;
    DictField       dictField;
    ArrayIdx        arrIdx;
    Counter         numOfSqlFields;             // сколько записей для полей таблицы должно быть в SqlDictionary
    fieldName       fieldName;
    fieldId         fieldId;

    boolean processTableField(DictField _dictField, ArrayIdx _arrIdx, boolean _isTzIdField = false)
    {
        ArrayIdx    dictArrIdx;
        str         infoName;                   // это имя поля сугубо для сообщений
        FieldName   sqlName;
        boolean     ret;
        ;
        if (_isTzIdField)
        {
            if (    _dictField.baseType()   != Types::UtcDateTime
                ||  _dictField.id()         == fieldnum(Common, createdDateTime)
                ||  _dictField.id()         == fieldnum(Common, modifiedDateTime)
               )
            {
                throw error(Error::wrongUseOfFunction(funcname()));
            }
            dictArrIdx  = _dictField.arraySize() + _arrIdx;
            sqlName     = _dictField.dateTimeTimeZoneRuleFieldName(_arrIdx - 1);
            infoName    = _dictField.name() + #TZIDsuffix;
        }
        else
        {
            dictArrIdx  = _arrIdx;
            sqlName     = _dictField.name(DbBackend::Sql, _arrIdx);
            infoName    = _dictField.name();
        }
        select firstonly sqlDict
            where   sqlDict.tabId   == _dictField.tableid()
                &&  sqlDict.fieldId == _dictField.id()
                &&  sqlDict.array   == dictArrIdx
                    ;
        if (!sqlDict)
        {
            sqlDict.clear();
            sqlDict.initValue();
            sqlDict.tabId           = _dictField.tableid();
            sqlDict.fieldId         = _dictField.id();
            sqlDict.array           = dictArrIdx;
            sqlDict.name            = strupr(_dictField.name(DbBackend::Native, _arrIdx));
            sqlDict.sqlName         = sqlName;
            dictType                = new SysDictType( _dictField.typeId() );
            if (_isTzIdField)
            {
                sqlDict.fieldType   = Types::Integer;
            }
            else
            if (            _dictField.id()     == fieldnum(Common, RecId)
                ||          _dictField.id()     == fieldnum(Common, createdTransactionId)
                ||          _dictField.id()     == fieldnum(Common, modifiedTransactionId)
                ||          _dictField.id()     == fieldnum(Common, Partition)

                ||          _dictField.typeId() == extendedtypenum(RecId)
                ||          _dictField.typeId() == extendedtypenum(RefRecId)
                ||          _dictField.typeId() == extendedtypenum(createdTransactionId)
                ||          _dictField.typeId() == extendedtypenum(modifiedTransactionId)
                ||          _dictField.typeId() == extendedtypenum(Partition)

                ||  (       dictType
                    &&  (   dictType.isExtending(extendedtypenum(RecId))
                        ||  dictType.isExtending(extendedtypenum(createdTransactionId))
                        ||  dictType.isExtending(extendedtypenum(modifiedTransactionId))
                        ||  dictType.isExtending(extendedtypenum(Partition))
                        )
                    )
               )
            {
                // для RecId в поле fieldType прописывается не Types::Int64, а число 49
                sqlDict.fieldType   = #RecIdBaseType;
            }
            else
            if (            _dictField.BaseType() == Types::Guid
                ||          _dictField.BaseType() == 45 // Types_MRC::SysGuid
               )
            {
                sqlDict.fieldType   = 45;
            }
            else
            {
                sqlDict.fieldType   = _dictField.baseType();
            }
            sqlDict.strSize         = _dictField.stringLen();
            // sqlDict.shadow          = bitTest( _dictField.flags(), #DBF_SHADOW );
            sqlDict.shadow          = 0; 
            // shadow может быть отлично от 0 только для записей VIEW
            // для VIEW помимо полей также в SQLDictionary хранится перечень входящих во VIEW табличек. Вот для таких записей shadow = 1 
            // но мы такие записи в этом джобе не воссоздаем, проще грохнуть вьюху и синхронизировать
            
            sqlDict.rightJustify    = bitTest( _dictField.flags(), #DBF_RIGHT );
            // sqlDict.flags           = sqlDict.shadow;   // а вот ни фига не _dictField.flags();
            sqlDict.flags           = 0;
            // flags может быть отлично от 0 только для записей VIEW
            // для VIEW помимо полей также в SQLDictionary хранится перечень входящих во VIEW табличек. Вот для таких записей flags = 1 
            // а также для самой записи соответвующей шапке VIEW flags = 1  
            // но мы такие записи в этом джобе не воссоздаем, проще грохнуть вьюху и синхронизировать
            
            sqlDict.nullable        =   _dictField.baseType() == Types::Container
                                    ||  _dictField.baseType() == Types::VarString
                                        ;
            if (sqlDict.validateWrite())
            {
                sqlDict.insert();
                ret = true;
                info(strfmt(@"Создана запись для поля %1.%2%3",
                            dictTable.name(), infoName, _dictField.arraySize() > 1 ? strfmt(@"[%1]", _arrIdx) : ''
                    ));
                // для всех несистемных полей UtcDateTime создаем также связанное поле TZID
                if (   !_isTzIdField
                    &&  _dictField.baseType()   == Types::UtcDateTime
                    &&  _dictField.id()         != fieldnum(Common, createdDateTime)
                    &&  _dictField.id()         != fieldnum(Common, modifiedDateTime)
                   )
                {
                    processTableField(_dictField, _arrIdx, true);
                }
            }
            else
            {
                ret = checkFailed(strfmt(@"Запись в %1 для поля %2.%3 не была создана", tablestr(SqlDictionary), dictTable.name(), infoName));
            }
        }
        return ret;
    }
    ;
    dictTable = new DictTable( _tableId );
    if (!dictTable)
    {
        throw error(strfmt(@"Не удалось создать объект %1 для таблицы '%2' (%3)", classstr(DictTable), tableid2name( _tableId ), _tableId));
    }
    if (dictTable.isSystemTable())
    {
        throw error(strfmt(@"Таблица '%1' - системная, на это я пойтить не могу", dictTable.name()));
    }
    if (!dictTable.isSql())
    {
        throw error(strfmt(@"Таблицы '%1' вообще не должно быть в базе", dictTable.name()));
    }
    if (dictTable.isView())
    {
        throw error(strfmt(@"Таблица '%1' это не табличка а VIEW. Для нее проще просто удалить объект в БД. Либо дописывать корректное заполнение полей shadow и flags", dictTable.name()));
    }
    
    for (fieldId = dictTable.fieldNext( 0 ); fieldId; fieldId = dictTable.fieldNext( fieldId ))
    {
        dictField = dictTable.fieldObject( fieldId );
        if (dictField && dictField.isSql())
        {
            fieldName = dictField.name();
            for (arrIdx = 1; arrIdx <= dictField.arraySize(); arrIdx++)
            {
                numOfSqlFields++;
                processTableField( dictField, arrIdx );
            }
        }
    }
    
    select firstonly sqlDict
        where   sqlDict.tabId   == _tableId
            &&  sqlDict.fieldId == 0
                ;
    if (!sqlDict)
    {
        sqlDict.clear();
        sqlDict.initValue();
        sqlDict.tabId       = _tableId;
        sqlDict.name        = strupr(dictTable.name());
        sqlDict.sqlName     = dictTable.name(DbBackend::Sql);
        sqlDict.strSize     = numOfSqlFields;       // для "заголовка" таблицы тут указывается, сколько у нее полей в БД
        sqlDict.flags       = dictTable.isView();   // "мой дедуктивный метод..."
        sqlDict.insert();
        info(strfmt(@"Создана запись для таблицы %1", dictTable.name()));
    }
}
За это сообщение автора поблагодарили: propeller (1).
Старый 15.04.2022, 17:54   #14  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от sukhanchik Посмотреть сообщение
На будущее оставлю здесь ссылку на SQL-скрипт для AX2012
https://github.com/dodiggitydag/AX-2...model%20db.sql
Спасибо.
В скрипте не хватает обработки NumberSequenceDatatype.
За это сообщение автора поблагодарили: sukhanchik (10).
Старый 15.04.2022, 18:45   #15  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,317 / 3547 (125) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
Цитата:
Сообщение от Logger Посмотреть сообщение
Спасибо.
В скрипте не хватает обработки NumberSequenceDatatype.
Ну так-то все таблицы, где присутствует в данных TableId или ExtendedDataTypeId надо обрабатывать. Соглашусь, что NumberSequenceDatatype из этого списка достаточно важная таблица.
__________________
Возможно сделать все. Вопрос времени
Старый 15.04.2022, 19:44   #16  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Да уж натворили они делов.
Надо было тогда везде, где возможно отказываться от идентификаторов таблиц, EDT
Или использовать Origin который guid
Старый 04.05.2023, 17:37   #17  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от sukhanchik Посмотреть сообщение
На будущее оставлю здесь ссылку на SQL-скрипт для AX2012
https://github.com/dodiggitydag/AX-2...model%20db.sql
Там похоже ошибка в этом месте
X++:
----------------------------------------------------------------------------------------------
-- Step 4 Fix the field ids in SQLDictionary which do not match
----------------------------------------------------------------------------------------------
PRINT 'Step 4';

WITH t AS (
    SELECT (
            SELECT m1.NAME
            FROM ModelElement m1
            WHERE m1.ElementHandle = m.ParentHandle
        ) AS [Table Name],
        m.NAME AS [mName],
        m.AXid,
        s.RECID,
        M.ParentId,
        s.TableId,
        s.FieldID,
        S.NAME,
        s.SQLNAME
    FROM ModelElement m
    LEFT OUTER JOIN AX2012DB.dbo.SQLDICTIONARY s
        ON m.ParentId = s.TABLEID
        AND s.NAME =  upper(m.NAME) collate Latin1_General_CI_AS
    WHERE m.ElementType = 42 -- UtilElementType::TableField
        AND (s.ARRAY = 1 OR s.ARRAY IS NULL)
        AND (s.FieldID > 0 OR s.FieldID IS NULL)
        AND s.FieldID != m.AxId
)
UPDATE AX2012DB.dbo.SQLDICTIONARY
SET FIELDID = (t.axid * -1) -- Set to a negative number but correct ID
FROM t join AX2012DB.dbo.SQLDICTIONARY s
ON upper(t.mName) collate Latin1_General_CI_AS = s.NAME
    AND s.FieldID <> 0
    AND s.TableId = t.ParentId
проблема в том что для табличек с наследованием (DirPartyTable -->..--> CompanyInfo) таблички наследники в ModelElement содержат ParentId равный идентификатору таблички (для CompanyInfo - будет 41 т.е. tableNum(CompanyInfo), а в SqlDictionary содержат в TableId значение корневой таблички иерархии, т.е. 2303 = tableNum(DirPartyTable) )

Последний раз редактировалось Logger; 04.05.2023 в 17:42.
За это сообщение автора поблагодарили: sukhanchik (10).
Старый 04.05.2023, 20:13   #18  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,317 / 3547 (125) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
Спасибо! По опыту использования этого скрипта - могу подтвердить - что там точно есть где-то ошибка, но так детально я не исследовал
__________________
Возможно сделать все. Вопрос времени
Теги
ax2009, sqldictionary, законченный пример

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Set записей таблицы OOuph DAX: Программирование 6 11.02.2008 10:27
Как добавить в фильтрацию записей доп. таблицы n:1 или 1:n? Hidden DAX: Программирование 6 11.08.2006 14:04
вывод количества записей в таблице на web форме и указание текущей страницы таблицы bambuk1960 DAX: Программирование 1 06.07.2006 13:27
Программное создание новых записей Владимир Максимов DAX: Программирование 35 06.03.2006 14:08
Lookupы при большом количестве записей выводимой таблицы Pavlo AKA Panok DAX: Программирование 9 07.05.2002 22:02

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 21:58.