09.06.2007, 11:55 | #1 |
Moderator
|
Кнопка "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 |
Moderator
|
Продолжение темы.
Выяснилось, что с моими исправлениями даная фича не работает для общих таблиц (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 |
Microsoft Dynamics
|
Эта фича не работает в версиях старше 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 Лечится изменением модификатора класса на Сервер (правда, не знаю, насколько чревато). Бага с SaveDataPerCompany тож осталась, поэтому требуются изменения, как у Dron AKA andy. |
|
03.07.2009, 06:19 | #4 |
Участник
|
Цитата:
Сообщение от Dron AKA andy
... После непродолжительных размышлений над оригинальным кодом я заметил некую странность: мы сначала наполняем контейнер названиями полей в SQL-интерпретации через
X++: dictTable.fieldName(this.field(i), DbBackend::SQL) X++: dictTable.fieldName(this.field(i),dbBackend::Sql,0,fieldNameGenerationMode::FieldListGroupBy) В случае, наличия в индексе строкового поля, при вызове 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 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"); } |
|
|
За это сообщение автора поблагодарили: tricky (1). |
Теги |
aot, ax2009, ax3.0, ax4.0, code access security, security, баг, безопасность, индекс, инструменты, ошибка, полезное |
|
|