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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 09.06.2007, 11:55   #1  
Dron AKA andy is offline
Dron AKA andy
Moderator
 
944 / 253 (10) ++++++
Регистрация: 27.03.2002
Адрес: Москва
Кнопка "Add-ins\Дубликаты" на табличных индексах в АОТ
Вот, случайно обнаружил и спешу поделиться.

Если встать в Репозитарии на любой созданный на таблице индекс и вызвать контекстное меню, то в пункте "Add-ins" появляется интересный пункт "Дубликаты". Он позволяет, в частности, проверить индекс на потенциальную возможность включения уникальности: выводит список повторяющихся значений набора полей из состава индекса, а если не находит дублей, пишет "Без дубликатов".

Правда, у меня он нормально не заработал (АХ 3.0 SP3CU1), вот исправленный код метода SysDictIndex\showDuplicates:
X++:
void showDuplicates()
{
    tableId         tableId         = this.tableid();
    DictTable       dictTable       = new DictTable(tableId);
    boolean         dataPrCompany   = dictTable.dataPrCompany();
    container       fields;
    Counter         numberOfFields  = this.numberOfFields();
    Counter         i;
    str             stmtStr;
    str             resultLineStr;
    str             resultField;
    str             resultField1;
    UserConnection  con             = new UserConnection();
    Statement       stmt            = con.createStatement();
    ResultSet       resultSet;
    boolean         anyDuplicates   = false;
    ;
    if (dataPrCompany)
        fields += dictTable.fieldname(fieldnum(common,DataAreaId),DbBackend::SQL);

    for (i = 1; i <= numberOfFields; i++)
    {
        fields += dictTable.fieldName(this.field(i), DbBackend::SQL);
    }
    if (dataPrCompany)
        numberOfFields++;

    stmtStr = 'select count(*)';
    // andy, 08.06.2007 -->
    if (dataPrCompany)
        stmtStr += ', ' + conPeek(fields, 1);
    //for (i = 1; i <= numberOfFields; i++)
    for (i = 1; i < numberOfFields; i++)
    // andy, 08.06.2007 <--
    {
        stmtStr += ', ' + dictTable.fieldName(this.field(i),dbBackend::Sql,0,fieldNameGenerationMode::FieldListGroupBy);
    }
    stmtStr += ' from ' + dictTable.name(DbBackend::SQL);
    stmtStr += ' group by ';
    // andy, 08.06.2007 -->
    if (dataPrCompany)
        stmtStr += conPeek(fields, 1);
    //for (i = 1; i <= numberOfFields; i++)
    for (i = 1; i < numberOfFields; i++)
    // andy, 08.06.2007 <--
    {
        // andy, 08.06.2007 -->
        //if (i > 1)
        if (dataPrCompany || i > 1)
        // andy, 08.06.2007 <--
            stmtStr += ', ';
        stmtStr += dicttable.fieldName(this.field(i),dbBackend::Sql,0,fieldNameGenerationMode::GroupByFieldList);
    }
    stmtStr += ' having count(*) > 1';
    if (numberOfFields > 0)
    {
        stmtStr += ' order by ';
        for (i = 1; i <= numberOfFields; i++)
        {
            if (i > 1)
                stmtStr += ', ';
            stmtStr += int2str(i+1);
        }
        stmtStr += ' desc';
    }
    resultSet = stmt.executeQuery(stmtStr);

    while (resultSet.next())
    {
        resultLineStr = "@SYS283" + strFmt(': %1', resultSet.getString(1));
        for (i = 1; i <= numberOfFields; i++)
        {
            resultField = strLtrim(resultSet.getString(i+1));
            if (i == 1 && dataPrCompany)
                resultField1 = resultField;
            else
                resultLineStr += strFmt(', %1: \'%2\'', conPeek(fields, i), resultField);
        }
        if (dataPrCompany)
            setPrefix(strFmt('%1: %2', conPeek(fields, 1), resultField1));
        info(resultLineStr);
        anyDuplicates = true;
    }
    if (!anyDuplicates)
        info("@SYS68671");
}
__________________
Андрей.
За это сообщение автора поблагодарили: mazzy (5), Logger (4), Мариночка (2).
Старый 18.07.2007, 13:19   #2  
Dron AKA andy is offline
Dron AKA andy
Moderator
 
944 / 253 (10) ++++++
Регистрация: 27.03.2002
Адрес: Москва
Продолжение темы.
Выяснилось, что с моими исправлениями даная фича не работает для общих таблиц (SaveDataPerCompany = No), хотя без моих изменений все работало в этом случае. После непродолжительных размышлений над оригинальным кодом я заметил некую странность: мы сначала наполняем контейнер названиями полей в SQL-интерпретации через
X++:
dictTable.fieldName(this.field(i), DbBackend::SQL)
, а затем повторно получаем названия тех же полей, но уже чуть видоизмененным способом:
X++:
dictTable.fieldName(this.field(i),dbBackend::Sql,0,fieldNameGenerationMode::FieldListGroupBy)
Буду рад, если кто-нибудь мне объяснит, почему сделано именно так, а пока предлагаю использовать полученные ранее названия полей. Вот последняя версия метода, работает для общих и раздельных таблиц. Изменения от оригинала минимальны.
X++:
void showDuplicates()
{
    tableId         tableId         = this.tableid();
    DictTable       dictTable       = new DictTable(tableId);
    boolean         dataPrCompany   = dictTable.dataPrCompany();
    container       fields;
    Counter         numberOfFields  = this.numberOfFields();
    Counter         i;
    str             stmtStr;
    str             resultLineStr;
    str             resultField;
    str             resultField1;
    UserConnection  con             = new UserConnection();
    Statement       stmt            = con.createStatement();
    ResultSet       resultSet;
    boolean         anyDuplicates   = false;
    ;
    if (dataPrCompany)
        fields += dictTable.fieldname(fieldnum(common,DataAreaId),DbBackend::SQL);

    for (i = 1; i <= numberOfFields; i++)
    {
        fields += dictTable.fieldName(this.field(i), DbBackend::SQL);
    }
    if (dataPrCompany)
        numberOfFields++;

    stmtStr = 'select count(*)';
    for (i = 1; i <= numberOfFields; i++)
    {
        // andy, 18.07.2007 -->
        //stmtStr += ', ' + dicttable.fieldName(this.field(i),dbBackend::Sql,0,fieldNameGenerationMode::FieldListGroupBy);
        stmtStr += ', ' + conPeek(fields, i);
        // andy, 18.07.2007 <--
    }
    stmtStr += ' from ' + dictTable.name(DbBackend::SQL);
    stmtStr += ' group by ';
    for (i = 1; i <= numberOfFields; i++)
    {
        if (i > 1)
            stmtStr += ', ';
        // andy, 18.07.2007 -->
        //stmtStr += dicttable.fieldName(this.field(i),dbBackend::Sql,0,fieldNameGenerationMode::GroupByFieldList);
        stmtStr += conPeek(fields, i);
        // andy, 18.07.2007 <--
    }
    stmtStr += ' having count(*) > 1';
    if (numberOfFields > 0)
    {
        stmtStr += ' order by ';
        for (i = 1; i <= numberOfFields; i++)
        {
            if (i > 1)
                stmtStr += ', ';
            stmtStr += int2str(i+1);
        }
        stmtStr += ' desc';
    }
    resultSet = stmt.executeQuery(stmtStr);

    while (resultSet.next())
    {
        resultLineStr = "@SYS283" + strFmt(': %1', resultSet.getString(1));
        for (i = 1; i <= numberOfFields; i++)
        {
            resultField = strLtrim(resultSet.getString(i+1));
            if (i == 1 && dataPrCompany)
                resultField1 = resultField;
            else
                resultLineStr += strFmt(', %1: \'%2\'', conPeek(fields, i), resultField);
        }
        if (dataPrCompany)
            setPrefix(strFmt('%1: %2', conPeek(fields, 1), resultField1));
        info(resultLineStr);
        anyDuplicates = true;
    }
    if (!anyDuplicates)
        info("@SYS68671");
}
__________________
Андрей.
Старый 20.07.2007, 12:11   #3  
Мариночка is offline
Мариночка
Microsoft Dynamics
Аватар для Мариночка
Сотрудники Microsoft Dynamics
 
75 / 56 (2) ++++
Регистрация: 31.01.2005
Адрес: Киев
Эта фича не работает в версиях старше 4ки. Туда добавили код, который ассертит сиквел пермишны:

X++:
ssep =  new SqlStatementExecutePermission(stmtStr);
    ssep.assert();
который ругается на:

X++:
Request for the permission of type 'SqlStatementExecutePermission' failed.
(S)\Classes\SqlStatementExecutePermission\demand
(S)\Classes\Statement\executeQuery
(C)\Jobs\directSQL - line 15
Происходит потому, что код исполняется на клиенте (SysDictIndex - Called From) и будет так везде, где пермишны требуются на клиенте , можно попробовать в простой джобе.

Лечится изменением модификатора класса на Сервер (правда, не знаю, насколько чревато).
Бага с SaveDataPerCompany тож осталась, поэтому требуются изменения, как у Dron AKA andy.
Старый 03.07.2009, 06:19   #4  
SRF is offline
SRF
Участник
MCBMSS
Axapta Retail User
 
362 / 542 (19) +++++++
Регистрация: 08.08.2007
Записей в блоге: 1
->
Цитата:
Сообщение от Dron AKA andy Посмотреть сообщение
... После непродолжительных размышлений над оригинальным кодом я заметил некую странность: мы сначала наполняем контейнер названиями полей в SQL-интерпретации через
X++:
dictTable.fieldName(this.field(i), DbBackend::SQL)
, а затем повторно получаем названия тех же полей, но уже чуть видоизмененным способом:
X++:
dictTable.fieldName(this.field(i),dbBackend::Sql,0,fieldNameGenerationMode::FieldListGroupBy)
Буду рад, если кто-нибудь мне объяснит, почему сделано именно так ...
Для MS SQL Server разницы в вызове dictTable.fieldName(...) c параметром fieldNameGenerationMode по-умолчанию или со значением fieldNameGenerationMode::FieldListGroupBy нет никакой(по крайней мере я ее не замечал), а вот для Oracle разница существенная.

В случае, наличия в индексе строкового поля, при вызове dictTable.fieldName(this.field(i),dbBackend::Sql,0,fieldNameGenerationMode::FieldListGroupBy) ядро AX преобразует поля к одному регистру, добавляя вызов substr и nls_lower.

Т.е. в AX 3.0 для СУБД Oracle вызов dictTable.fieldName(this.field(i), DbBackend::SQL) для строкового поля вернет field, а вызов dictTable.fieldName(this.field(i),dbBackend::Sql,0,fieldNameGenerationMode::FieldListGroupBy) - вернет
substr(nls_lower(field), 1, strlen(field)).

В конфигурации АОСа AX4.0 есть параметр -
Database Tuning\AutoGeneration Options\Include SUBSTR and LOWER in all SELECT statements to support Oracle mixed-case systems, который видимо и отвечает за генерацию данного substr и nls_lower.

В итоге, если в таблице есть значения строкового поля 'SSS' и 'sss', данное поле включено в индекс и используемая СУБД Oracle, то замена

X++:
dictTable.fieldName(this.field(i),dbBackend::Sql,0,fieldNameGenerationMode::FieldListGroupBy)
на

X++:
dictTable.fieldName(this.field(i), DbBackend::SQL)
приведет к тому, что при использовании пункта 'Дубликаты' - появится инфолог - 'Дубликатов нет'. А при попытке сделать индекс уникальным, вывалится ошибка, о не возможности его создания, поскольку ядро AX при создании индекса на Oracle использует все те же substr и nls_lower

В связи с этим, привожу немного измененный код(для AX 3.0, аналогичные изменения можно внести и для AX 4.0), который одинаково работает как на SQL, так и на Oracle.

X++:
void showDuplicates()
{
    tableId         tableId         = this.tableid();
    DictTable       dictTable       = new DictTable(tableId);
    boolean         dataPrCompany   = dictTable.dataPrCompany();
    container       fields;
    Counter         numberOfFields  = this.numberOfFields();
    Counter         i;
    str             stmtStr;
    str             resultLineStr;
    str             resultField;
    str             resultField1;
    UserConnection  con             = new UserConnection();
    Statement       stmt            = con.createStatement();
    ResultSet       resultSet;
    boolean         anyDuplicates   = false;
    ;
    if (dataPrCompany)
        fields += dictTable.fieldname(fieldnum(common,DataAreaId),DbBackend::SQL);
    for (i = 1; i <= numberOfFields; i++)
    {
        fields += dictTable.fieldName(this.field(i), DbBackend::SQL);
    }
    //srf -->
    //if (dataPrCompany)
    //    numberOfFields++;
    //srf <--
    stmtStr = 'select count(*)';
    //srf -->
    if (dataPrCompany)
    {
        stmtStr += ', ' + dictTable.fieldName(fieldnum(Common,DataAreaId),DbBackend::Sql,0,FieldNameGenerationMode::FieldListGroupBy);
    }
    //srf <--
    for (i = 1; i <= numberOfFields; i++)
    {
        stmtStr += ', ' + dictTable.fieldName(this.field(i),dbBackend::Sql,0,fieldNameGenerationMode::FieldListGroupBy);
    }
    stmtStr += ' from ' + dictTable.name(DbBackend::SQL);
    stmtStr += ' group by ';
    //srf -->
    if (dataPrCompany)
    {
        stmtStr += dictTable.fieldName(fieldnum(Common,DataAreaId),DbBackend::Sql,0,FieldNameGenerationMode::FieldListGroupBy);
        if (numberOfFields >= 1)
        {
            stmtStr+= ', ';
        }
    }
    //srf <--
    for (i = 1; i <= numberOfFields; i++)
    {
        if (i > 1)
            stmtStr += ', ';
        stmtStr += dicttable.fieldName(this.field(i),dbBackend::Sql,0,fieldNameGenerationMode::GroupByFieldList);
    }
    //srf -->
    if (dataPrCompany)
        numberOfFields++;
    //srf <--
    stmtStr += ' having count(*) > 1';
    if (numberOfFields > 0)
    {
        stmtStr += ' order by ';
        for (i = 1; i <= numberOfFields; i++)
        {
            if (i > 1)
                stmtStr += ', ';
            stmtStr += int2str(i+1);
        }
        stmtStr += ' desc';
    }
    resultSet = stmt.executeQuery(stmtStr);
    while (resultSet.next())
    {
        resultLineStr = "@SYS283" + strFmt(': %1', resultSet.getString(1));
        for (i = 1; i <= numberOfFields; i++)
        {
            resultField = strLtrim(resultSet.getString(i+1));
            if (i == 1 && dataPrCompany)
                resultField1 = resultField;
            else
                resultLineStr += strFmt(', %1: \'%2\'', conPeek(fields, i), resultField);
        }
        if (dataPrCompany)
            setPrefix(strFmt('%1: %2', conPeek(fields, 1), resultField1));
        info(resultLineStr);
        anyDuplicates = true;
    }
    if (!anyDuplicates)
        info("@SYS68671");
}
P.S. Кстати данную функцию починили в AX2009, а параметр Database Tuning\AutoGeneration Options\Include SUBSTR and LOWER in all SELECT statements to support Oracle mixed-case systems в конфигурации АОСа отсутсвует.
За это сообщение автора поблагодарили: tricky (1).
Теги
aot, ax2009, ax3.0, ax4.0, code access security, security, баг, безопасность, индекс, инструменты, ошибка, полезное

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
После перестроения перекрестных ссылок начинает жутко тормозить Add-Ins MironovI DAX: Программирование 4 27.09.2007 13:46
AX4: Кнопка "Сценарий" в паспорте записи Den Ram DAX: Функционал 2 19.04.2007 13:53
Axapta Lessons: Add menu options to the Add-Ins submenu Blog bot DAX Blogs 0 28.10.2006 18:22
Что делает кнопка "Упорядочить" в форме разноски накладной? Bega DAX: Функционал 1 10.01.2006 12:25
Недоступна кнопка "Разноска" YaHooka DAX: Прочие вопросы 1 24.03.2005 16:57
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

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

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

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 12:05.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.