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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 08.12.2010, 10:24   #21  
KiselevSA is offline
KiselevSA
Злыдни
Аватар для KiselevSA
Злыдни
Лучший по профессии 2015
 
958 / 333 (13) ++++++
Регистрация: 25.01.2002
Адрес: Москва
Цитата:
Сообщение от Alenka Посмотреть сообщение
2 KiselevSA: Откуда такая уверенность, что линейное увеличение размеров базы приведет к линейному увеличению времени дефрагментации? Если 25Гб пересчитывалась 1 час, то это не значит, что база в 20 раз больше будет обрабатываться в 20 раз дольше.
При работе механизма деврагментации, насколько я помню, идет заполнение временной таблицы с последующим update всех записей с RecId и встеченными RefRecId. На таких операциях вряд ли возможен рост по экспоненте (если только у Вас системным администратором не назначен безголовый сотрудник).
Настоятельно рекомендую перед дефрагментацией почистить некоторые таблицы: выполненные пакеты, записи об официальных лицах и т.п.
__________________
люди...считают, что если техника не ломается, то ее не нужно ремонтировать. Инженеры считают, что если она не ломается, то нуждается в совершенствовании.
Старый 08.12.2010, 10:30   #22  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от Vadik Посмотреть сообщение
А код, который отыщет свободные диапазоны RecId в компании на объеме данных, сопоставимом с постановкой задачи (500Гб) - будет?
Поделюсь всем, что имею на эту тему, без утайки. Я готовил диапазоны дыр в своем любимом MS Access и далее заполнял ими таблицу RECIDHOLES в схеме Аксапты в Оракле. В Аксесе обработка очень простая: в одну табличку собираем все существующие RecId и затем в другую складываем диапазоны НЕсуществующих RecId, т.е. дыры.

Для этих манипуляций у меня имеется джоб оббегания по таблицам Аксапты и сохранения данных в Аксесе:
X++:
static void Job350_AllRecIds(Args _args)
{
    str             strFileName = @'C:\AxForumTests\Gustav\RecId.mdb';
    str             strFolderName;

    Filename        filename;
    FilenameType    filenameType;
    FilePath        filePath;
    container       conPath;

    COM             dbe, db;
    COM             cnn, rst;
    COM             flds, fld;

    int             i, nLines;
    int             timeFullStart, timeFullFinish;

    Dictionary      dictionary = new Dictionary();
    TableId         tableId;
    DictTable       dictTable;
    Common          common;
    int             row, timeStart;
    int             recordCount;

    #CCADO
    #define.dbLangGeneral(';LANGID=0x0409;CP=1252;COUNTRY=0')

    void recreateAccessTable(str _table, str _fields)
    {
        try
        {
            nLines = infolog.line();
            db.Execute('DROP TABLE [' + _table + ']');
        }
        catch   (Exception::Error)
        {
            infolog.clear(nLines);
        }

        db.Execute( 'CREATE TABLE [' + _table + '] (' + _fields + ')' );
    }
    ;

    timeFullStart = timenow();

    // parse file name -----------------------------------------------------------------------------
    [filePath, filename, filenameType] = fileNameSplit(strFileName);

    conPath = str2con_RU(filePath, '\\'); // you can use str2con if you do not have Russian DIS-layer

    if (subStr(filePath,2,1) != ':')
    {
        info(@'For drive use only one character syntax: C:\...! Do not use: \\server\folder\...!');
        return;
    }

    // create folders if they not exist ------------------------------------------------------------
    if (conlen(conPath)>1)
    {
        strFolderName = conpeek(conPath,1); // C:

        for (i=2; i<=conlen(conPath); i++)
        {
            strFolderName += strFmt(@'\%1', conpeek(conPath,i)); // C:\AxForumTests...

            if (!WinAPI::folderExists(strFolderName))
                 WinApi::createDirectory(strFolderName);
        }
    }

    // create MDB-file if not exists ---------------------------------------------------------------
    dbe = new COM('DAO.DBEngine.36');

    if (!WinAPI::fileExists( strFileName ))
    {
        try
        {
            nLines = infolog.line();
            db = dbe.CreateDatabase(strFileName, #dbLangGeneral);
        }
        catch  (Exception::Error)
        {
            infolog.clear(nLines);
            info( 'Probably mdb-file already exists and is open at the moment!' );
        }
    }
    else
    {
        db = dbe.OpenDatabase( strFileName );
    }

    // (re)create tables in MDB-file ----------------------------------------------------------------
    recreateAccessTable(
        'UsedRecId',
            'TblId          LONG,   ' +
            'DataAreaId     TEXT(3),' +
            'RecId          LONG    ' );

    recreateAccessTable(
        'RecIdHoles',
            'FromRecId      LONG,   ' +
            'ToRecId        LONG    ' );

    db.Close();
    db = null;
    dbe = null;

    // export EmplTable (a few fields) from Axapta to similar table in Access ----------------------
    cnn = new COM('ADODB.Connection');
    cnn.connectionString('Provider=Microsoft.Jet.OLEDB.4.0;' +
                         'Data Source=' + strFileName);
    cnn.Open();

    rst = new COM('ADODB.Recordset');
    rst.LockType(#adLockOptimistic);
    rst.Open('UsedRecId', cnn);

    flds = rst.Fields();

    row = 0;
    for (i=1; i<= dictionary.tableCnt(); i++)
    {
        tableId   = dictionary.tableCnt2Id(i);

        dictTable = new DictTable(tableId);

        print strFmt('%1 -- %2 -- %3', tableId, dictTable.name(), row);

        // если в очередной таблице нет записей
        // то переходим к следующей
        try
        {
            nLines = infolog.line();
            recordCount = new SysDictTable(tableId).recordCount();
        }
        catch //может случиться, если таблица есть в репозитарии, но нет в базе
        {
            infolog.clear(nLines);
            recordCount = 0;
        }
        if (! recordCount)
            continue;

        common = dictTable.makeRecord();

        // цикл по записям таблицы (МОЖНО ОГРАНИЧИТЬ ДИАПАЗОН ПОИСКА RecId ПО ВСЕМ ТАБЛИЦАМ)
        while select common
            where common.RecId >= 800000001 && common.RecId <= 900000000
        {
            row++;
            rst.AddNew();
                fld = flds.Item('TblId'     ); fld.Value(tableId);
                fld = flds.Item('DataAreaId'); fld.Value(common.dataAreaId);
                fld = flds.Item('RecId'     ); fld.Value(common.RecId);
            rst.Update();
        }
    }

    rst.Close();
    rst = null;

    cnn.Close();
    cnn = null;

    timeFullFinish = timenow();
    box::info(strfmt('Total running time: %1 sec -- Records: %2', timeFullFinish - timeFullStart, row));
}
Обратите внимание, что можно задать диапазон поиска RecId и таким образом разобраться с базами Аксапты любых объемов, просто деля задачу на части (т.е. на диапазоны поиска RecId по всем таблицам) и даже создавая для каждой части отдельный mdb-файл. Тут необходимо помнить, что имеется ограничение на максимальный размер mdb-файла = 2 Гб (по крайней мере, раньше было такое ограничение, не знаю как сейчас).

Далее уже в Аксесе создаем VBA-модуль со следующей начинкой:
Код:
Option Compare Database
Option Explicit

Sub packHolesInIntSequenceToRanges()
'===================================================================
' Сворачивание "дырок" в последовательности целых чисел в диапазоны
'===================================================================

    Dim cnn As ADODB.Connection
    Dim rstSource As ADODB.Recordset
    Dim rstRanges As ADODB.Recordset
   
    Dim rangeNum   As Integer 'счетчик непрерывных диапазонов
    
    Dim curr      As Long  'текущее (очередное) значение из последовательности
    Dim prev      As Long  'предыдущее значение (конец непрерывного диапазона)
    
    Const minDelta As Long = 25    'минимальный размер непрерывного диапазона
                                   '(диапазоны меньшей длины игнорируются)
                                   
    Const startFrom As Long = 15082620 '15000001 'стартовое значение процессса - либо "дырка", либо нет
                                       'первый prev все равно на 1 меньше и считается не дыркой
    Const stopOn As Long = 19997895 'следующий NextVal - 25*кол-во пользователей - последний curr - считается дыркой
    
    
    Set cnn = Application.CurrentProject.AccessConnection
    
    Set rstSource = New ADODB.Recordset
    With rstSource
         Set .ActiveConnection = cnn
         .source = "SELECT * FROM UsedRecId WHERE DataAreaId=""ppp""" & _
                   " AND RecId >= " & CStr(startFrom) & _
                   " AND RecId <= " & CStr(stopOn) & " ORDER BY RecId"
                   
         .Open
    End With
    
    Set rstRanges = New ADODB.Recordset
    With rstRanges
         Set .ActiveConnection = cnn
         .source = "RecIdHoles"
         .LockType = adLockOptimistic
         .Open
    End With
       
    rangeNum = 0
    prev = startFrom - 1
    
    Do While Not rstSource.EOF
    
        curr = rstSource("RecId").Value
        
        If curr - prev > 1 Then
            'записываем диапазон, закончившийся на предыдущем
            If curr - prev - 1 >= minDelta Then 'здесь именно минус 1
                rangeNum = rangeNum + 1
                Debug.Print rangeNum
                rstRanges.AddNew
                    rstRanges("FromRecId").Value = prev + 1 'From
                    rstRanges("ToRecId").Value = curr - 1 'To
                rstRanges.Update
            End If
        End If
        
        prev = curr
        rstSource.MoveNext
    Loop
    
    'для последнего диапазона
    curr = stopOn + 1 'не дырка
    If curr - prev > 1 Then
        'записываем диапазон, закончившийся на предыдущем
        If curr - prev - 1 >= minDelta Then 'здесь именно минус 1
            rangeNum = rangeNum + 1
            Debug.Print rangeNum
            rstRanges.AddNew
                rstRanges("FromRecId").Value = prev + 1 'From
                rstRanges("ToRecId").Value = curr - 1 'To
            rstRanges.Update
        End If
    End If
    
    Set rstRanges = Nothing
    Set rstSource = Nothing
    Set cnn = Nothing
    
End Sub
Собственно, эта единственная процедура packHolesInIntSequenceToRanges и запускается. Она ищет непрерывные последовательности дырок RecId длиной не менее 25 в аксесной табличке UsedRecId и складывает их в аксесную же табличку RecIdHoles (таблички были созданы в mdb-файле упомянутым выше аксаптовским джобом, а UsedRecId еще и заполнена им же).

Последующий перенос данных из аксессной RecIdHoles в RECIDHOLES в схеме Аксапты - любым желаемым способом, вплоть до приаттачивания RECIDHOLES к файлу MDB как таблицы ODBC и элементарного ручного копипаста из одной таблицы в другую. Или через Excel - вставляем данные RecIdHoles в колонки A и B, в ячейке C1 пишем формулу ="INSERT INTO RECIDHOLES VALUES ("&A1&","&B1&");" и копируем ее на следующие строки; далее копируем содержимое колонки С в QA (для MS SQL Server) или TOAD (для Oracle) и исполняем этот набор операторов INSERT.

Последний раз редактировалось Gustav; 08.12.2010 в 11:28.
За это сообщение автора поблагодарили: mazzy (2), Logger (10), lev (5), vml (1), S.Kuskov (8).
Старый 08.12.2010, 11:37   #23  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
Цитата:
Сообщение от Gustav Посмотреть сообщение
Код:
CREATE TABLE RECIDHOLES
(
  FROMRECID NUMBER(10),
  TORECID   NUMBER(10)
)
..
Код:
CREATE OR REPLACE TRIGGER SystemSequences_TBU
BEFORE UPDATE
ON SYSTEMSEQUENCES REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
WHEN (
SUBSTR(NLS_LOWER(Old.DataAreaId),1,3) = 'ppp' 
      AND Old.Id = -1 
      AND Old.TabId = 0
      )
А если компаний более одной?
Код:
while select common
where common.RecId >= 800000001 && common.RecId <= 900000000
{
    row++;
    rst.AddNew();
    fld = flds.Item('TblId'     ); fld.Value(tableId);
    fld = flds.Item('DataAreaId'); fld.Value(common.dataAreaId);
    fld = flds.Item('RecId'     ); fld.Value(common.RecId);
    rst.Update();
}
А сколько времени нужно чтобы отработал этот код в компании с числом записей в несколько сотен миллионов? А триггер проверялся для случая с несколькими активными "писателями" (толстыми клиентами и AOS-ами) в SystemSequences ? Я конечно предвижу ответ что и диапазоны можно в разрезе компаний хранить и триггер переписать, но что-то вся обвязка становится слишком похожей на самолет-этажерку. Не проще вправить мозг стандартному "дефрагментатору" и успокоиться еще на пару лет?
P.S. Особенно если в простых случаях (нет виртуальных компаний и правильное наследование типов для ссылок по RecId) все и так само собой работает
__________________
-ТСЯ или -ТЬСЯ ?
За это сообщение автора поблагодарили: Gustav (3).
Старый 08.12.2010, 11:44   #24  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
Цитата:
Сообщение от Ivanhoe Посмотреть сообщение
Про неуплаченную дань не в курсе, я что-то пропустил? =)
Я взял на себя смелость предположить что будь в свое время проплачена поддержка, топикстартер в 2010 году на версии 3.0 не сидел бы и сейчас просто взять и получить лицензии на 2009 задешево не получится
__________________
-ТСЯ или -ТЬСЯ ?
Старый 08.12.2010, 12:45   #25  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от Vadik Посмотреть сообщение
А если компаний более одной?
Ну, несколько компаний можно прописать либо через условие триггера:
Код:
WHEN (
(SUBSTR(NLS_LOWER(Old.DataAreaId),1,3) = 'ppp' AND Old.Id = -1 AND Old.TabId = 0)
OR
(SUBSTR(NLS_LOWER(Old.DataAreaId),1,3) = 'rrr' AND ...)
OR
(SUBSTR(NLS_LOWER(Old.DataAreaId),1,3) = 'sss' AND ...)
     )
либо создав для каждой компании свой отдельный триггер (их вроде может быть несколько на одно событие). Соответственно, внутри придется развить логику на эти несколько компаний. Но обычно критической по заканчивающимся RecId является какая-то одна. У нас, например, есть вторая (помимо dat, конечно), в которую транслируются данные из основной для ГААПа, так там любо-дорого посмотреть - дыр практически нет, всё последовательно и текущее максимальное значение раз в 30 меньше текущего максимального из основной (русской) компании. А в основной построишь какую-нибудь подробную оборотно-сальдовую ведомость по складу или ОС - 100 тыщ RecId улетело в трубу (Привет, GMCS!)
Цитата:
Сообщение от Vadik Посмотреть сообщение
А сколько времени нужно чтобы отработал этот код в компании с числом записей в несколько сотен миллионов?
Навскидку не помню... Не одна минута конечно, но и не какое-то невероятное кол-во времени (типа несколько суток, недель), способное удручить и заставить отказаться от эксперимента. Я обычно оставлял на ночь, утром приходил - заказанная порция была готова. Порция внушительная, в джобе же виден диапазон реального перебора в 100 миллионов (от 800 до 900 млн). Для 500 млн. пусть будет нуу... часов 10-12...Не имею возможности сейчас проверить, но тот, кто заинтересуется, может погонять на своей системе и сообщить получающееся время.

И совсем же необязательно сразу искать дыры во всем диапазоне от 0 до текущего значения NextVal. Допустим, текущий NextVal в районе 1.5 миллиардов, тогда можно, не торопясь, составить таблицу дыр для RecId = 0..500 млн, настроить триггер, запустить его в эксплуатацию. А через годик обработать уже диапазон 500 млн..1 млрд и т.д. Это ж всё разовые задачки - обработал, запустил, забыл ("украл, выпил, в тюрьму" (с))
Цитата:
Сообщение от Vadik Посмотреть сообщение
А триггер проверялся для случая с несколькими активными "писателями" (толстыми клиентами и AOS-ами) в SystemSequences ?
Мы без АОСа, на двухзвенке, клиенты у нас все толстые. Несколько одновременно активных "писателей" захватывали свои порции по 25 штук вполне благополучно.
Цитата:
Сообщение от Vadik Посмотреть сообщение
..но что-то вся обвязка становится слишком похожей на самолет-этажерку. Не проще вправить мозг стандартному "дефрагментатору" и успокоиться еще на пару лет?
Я ж только предлагаю, не навязываю же...
Старый 08.12.2010, 12:59   #26  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,651 / 1158 (42) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
Цитата:
Сообщение от Vadik Посмотреть сообщение
А если компаний более одной?
А сколько времени (...)? А триггер проверялся для случая (...)? (...) Не проще вправить мозг стандартному "дефрагментатору" и успокоиться еще на пару лет?
Я тут за автора попробую ответить.

Для начала, код очевидно "сырой". В том смысле, что писался под конкретную задачу и как решение "в общем случае" не годится. В этом решении важен не конкретный код, а сама идея хранения "дыр" и своевременное переключение нумератора на очередную "дыру". Ну, а "допилить" код под свои нужды - уже дело техники. Важна сама идея.

Теперь по вопросам:

1. Насчет компаний, сам же и ответил. Не вижу особых проблем добавить фильтр по компаниям

2. Время выполнения определения "дыр"

Очевидно, по крайней мере, будет не больше, чем время дефрагментации. Кроме того, очевидно, есть предмет для оптимизации алгоритма. Чего не скажешь про алгоритм дефрагментации.

Ведь поиск дыр - это чистые запросы, а дефрагментация - это запросы+модификация. Если запросы еще можно оптимизировать (да и сам алгоритм изменить), то с модификацией особых вариантов нет. Надо модифицировать ВСЕ записи. Быстро это выполнено быть не может

Кроме того, как справедливо заметил Gustav, можно разбить процесс поиска дыр на этапы, чего в принципе невозможно сделать для дефрагментации.

3. Преимущества, по сравнению с дефрагментацией (явно не прозвучало, но вопрос очевиден)

Дефрагментация, как бы ей мозги ни вправляли, всегда оставляет вероятность того, что где-то чего-то не учли и ссылки будут нарушены. При использовании "дыр" такого быть не может, поскольку ссылки не меняются.

Цитата:
Сообщение от Vadik Посмотреть сообщение
P.S. Особенно если в простых случаях (нет виртуальных компаний и правильное наследование типов для ссылок по RecId) все и так само собой работает
Как мне кажется, это из области фантастики. Большинство российских внедрений кастомизировано по самое "не балуйся". Причем не один раз и самыми разным людьми с очень разным опытом. Поэтому простых случаев не может быть "по определению"
За это сообщение автора поблагодарили: Gustav (12).
Старый 08.12.2010, 13:14   #27  
glibs is offline
glibs
Member
Сотрудники компании It Box
Most Valuable Professional
Лучший по профессии 2011
Лучший по профессии 2009
 
4,942 / 911 (40) +++++++
Регистрация: 10.06.2002
Адрес: I am from Kyiv, Ukraine. Now I am in Moscow. For private contacts: glibs@hotmail.com
Цитата:
Сообщение от Vadik
...
Не проще вправить мозг стандартному "дефрагментатору" и успокоиться еще на пару лет?
...
Судя по не утихающим до сих пор дискуссиям, в решении данной проблемы простота и надежность не являются приоритетными критериями.
__________________
С уважением,
glibs®
Старый 08.12.2010, 13:23   #28  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
И правда, если
X++:
    EDT  ,  RecId
в собственных же модификациях, я пожалуй агитацию за советскую власть в этой ветке закончу
Цитата:
Судя по не утихающим до сих пор дискуссиям, в решении данной проблемы простота и надежность не являются приоритетными критериями
да уж, богата земля рассейская Кулибиными
__________________
-ТСЯ или -ТЬСЯ ?
Старый 08.12.2010, 13:56   #29  
fed is offline
fed
Moderator
Аватар для fed
Ex AND Project
Соотечественники
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,890 / 5647 (194) ++++++++++
Регистрация: 13.03.2002
Адрес: Hüfingen,DE
Лично однажды забыл поставить RefRecId для одной ссылки. (Хотя я не самый безграмотный и ленивый специалист и про проблему эту знал когда писал. Просто делал в жуткой запаре). Потом мы этим "Надежным и простым" дефрагментатором продефрагментировали. Потом дня через три заметили утрату связи (функциональность не слишком регулярно использовалась). Потом около 2 недель - восстанавливали связь по всяким эвристикам.

Так что вероятность ошибок со ссылками всегда присутствует. А поскольку на работающей системе остановить бизнес на денек-другой и попросить сотрудников потестить - не потрелялось ли что - не реально, предложеный Gustav'ом вариант имеет право на жизнь. Хотя мне тоже кажется что его надо еще доводить и оптимизировать изрядно. Но сама по себе идея интересна и неплоха...

Кстати - тут помниться пару недель назад была дискуссия насчет навешивания ярлыков... Это я к употреблению термина "Кулибин"...
Старый 08.12.2010, 16:04   #30  
Alenka is offline
Alenka
Участник
 
58 / 25 (1) +++
Регистрация: 19.04.2006
Очень понравилась идея от Gustav, т.к. дырок действительно очень много. При "использованных" свыше 3,5 млрд номеров RecId у нас в базе всего около 400 млн записей. И ее еще можно почистить...

Переход на новую версию - это что-то из области фантастики для нашей компании.

Так что вполне вероятно никакой дефрагментации не потребуется.
Старый 08.12.2010, 21:34   #31  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от Alenka Посмотреть сообщение
дырок действительно очень много. При "использованных" свыше 3,5 млрд номеров RecId у нас в базе всего около 400 млн записей. И ее еще можно почистить...
У нас похожий процент заполненности, даже еще меньше: на 1.3 млрд RecId - около 100 млн записей.

Я набросал еще один джоб, помогающий понять степень заполненности базы по диапазонам-этапам (stages) размером в 100 млн. номеров RecId.
X++:
static void Job351_CountRecIdsPerStage(Args _args)
{
    // расчет количеств RecId по этапам
    int             i, nLines;
    int             timeFullStart, timeFullFinish;

    Dictionary      dictionary = new Dictionary();
    TableId         tableId;
    DictTable       dictTable;
    Common          common;
    int             row, timeStart;
    int             recordCount;

    int             stage;
    int             stageCnt        = 22;
    int             recIdPerStage   = 100000000;
    int             recIdStart      = 1;
    int             recIdEnd        = recIdPerStage;
    int             recIdCount;

    ;

    timeFullStart = timenow();

    for (stage=1; stage<= stageCnt; stage++)
    {
        recIdCount = 0;
        for (i=1; i<= dictionary.tableCnt(); i++)
        {
            tableId   = dictionary.tableCnt2Id(i);
            dictTable = new DictTable(tableId);

            print strFmt('%1 -- %2 -- %3', stage, tableId, dictTable.name());

            // если в очередной таблице нет записей
            // то переходим к следующей
            try
            {
                nLines = infolog.line();
                recordCount = new SysDictTable(tableId).recordCount();
            }
            catch //может случиться, если таблица есть в репозитарии, но нет в базе
            {
                infolog.clear(nLines);
                recordCount = 0;
            }
            if (! recordCount)
                continue;

            common = dictTable.makeRecord();

            select count(RecId) from common
                where common.RecId >= recIdStart && common.RecId <= recIdEnd;

            recIdCount += common.RecId;
        }

        info(strFmt('%1 -- %2 -- %3 -- %4', stage, recIdStart, recIdEnd, recIdCount));

        if (! recIdCount) 
            break;

        recIdStart      = recIdEnd + 1;
        recIdEnd        = recIdEnd + recIdPerStage;
    }

    timeFullFinish = timenow();
    box::info(strfmt('Total running time: %1 sec', timeFullFinish - timeFullStart));
}
У меня джоб трудился около часа и по окончании выдал следующую информацию (привожу в слегка облагороженном виде):
Код:
stage recIdStart      recIdEnd        recIdCount   % к 100 млн.
---------------------------------------------------------------
 1                1     100 000 000    8 050 727    8%
 2      100 000 001     200 000 000      878 353    1%
 3      200 000 001     300 000 000    1 478 347    1%
 4      300 000 001     400 000 000    1 131 490    1%
 5      400 000 001     500 000 000    2 195 859    2%
 6      500 000 001     600 000 000    1 427 424    1%
 7      600 000 001     700 000 000    1 259 705    1%
 8      700 000 001     800 000 000    2 905 657    3%
 9      800 000 001     900 000 000   16 803 406   17%
10      900 000 001   1 000 000 000   12 565 324   13%
11    1 000 000 001   1 100 000 000   16 741 287   17%
12    1 100 000 001   1 200 000 000   21 317 892   21%
13    1 200 000 001   1 300 000 000   11 187 373   11%
14    1 300 000 001   1 400 000 000            0    0%
---------------------------------------------------------------
                                      97 942 844
Такая вот занятная картина получается. Явная пустота первых 700 млн - полагаю, интенсивная черновая работа на внедрении. Хотя все равно с трудом представляю, как за внедрение можно столько id-шек растратить... Ну и как же тут не захотеть по дыркам повторно пройтись?

Кстати, нашёл у себя аксесный mdb-файл на 28 млн. записей таблицы UsedRecId (см. мой пред. пост). Так вот этот файл имеет размер 1 Gb. Можно использовать это соотношение как оценочное при планировании "завоевания" этапов.

Alenka, а у вас поставщик-внедренец Аксапты - не GMCS ли тоже? У них в приложении масса полезных запросов-отчетов, которые, тем не менее, RecId расходуют нещадно - за счет заполнения вспомогательных таблиц временными данными (не путать с временными таблицами). Т.е. на "совесть" этих запросов-отчетов в нашем приложении половину дыр точно можно списывать...
За это сообщение автора поблагодарили: aidsua (1), G.Menshikh (1).
Старый 08.12.2010, 22:13   #32  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от Gustav Посмотреть сообщение
Явная пустота первых 700 млн - полагаю, интенсивная черновая работа на внедрении. Хотя все равно с трудом представляю, как за внедрение можно столько id-шек растратить...
легко. экспорт/импорт с удалением.
__________________
полезное на axForum, github, vk, coub.
Старый 09.12.2010, 10:00   #33  
Alenka is offline
Alenka
Участник
 
58 / 25 (1) +++
Регистрация: 19.04.2006
2 Gustav: внедряли Аксапту нам КОРУС Консалтинг.
Такое кол-во пустот в нумерации действительно возникает из-за таблиц с временными данными, жизнь которых составляет от1 дня до 2 недель. Пустота вначале тоже есть, но она составляет всего около 2 млн.
За это сообщение автора поблагодарили: Gustav (2).
Старый 14.01.2011, 10:03   #34  
Alenka is offline
Alenka
Участник
 
58 / 25 (1) +++
Регистрация: 19.04.2006
Наконец-то нашлось время на испытание, проверку и тестирование идеи с триггером. Выяснилось, что, к сожалению, сама идея заполнения дыр, оставляя в запасе 25 номеров, "не дружит" с insert_recordset. При использовании insert_redordset значение nextVal в SystemSequence передвигается сразу на количество вставляемых записей. Поэтому необходимо держать в запасе в текущей дыре с неиспользованными RecId не 25 номеров, а неограниченное количество, что естественно невозможно.
2 Gustav: Этот случай был просто не учтен Вами или есть какое-то решение?

Последний раз редактировалось Alenka; 14.01.2011 в 10:58.
За это сообщение автора поблагодарили: Vadik (1), Gustav (3), S.Kuskov (1).
Старый 14.01.2011, 12:33   #35  
Alexius is offline
Alexius
Участник
Аватар для Alexius
 
461 / 248 (9) ++++++
Регистрация: 13.12.2001
Цитата:
Сообщение от Alenka Посмотреть сообщение
Такое кол-во пустот в нумерации действительно возникает из-за таблиц с временными данными, жизнь которых составляет от1 дня до 2 недель.
Такие таблицы неплохо бы вытащить в отдельные виртуальные компании.
За это сообщение автора поблагодарили: Vadik (1), Alenka (1).
Старый 14.01.2011, 13:50   #36  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от Alenka Посмотреть сообщение
Наконец-то нашлось время на испытание, проверку и тестирование идеи с триггером. Выяснилось, что, к сожалению, сама идея заполнения дыр, оставляя в запасе 25 номеров, "не дружит" с insert_recordset. При использовании insert_redordset значение nextVal в SystemSequence передвигается сразу на количество вставляемых записей. Поэтому необходимо держать в запасе в текущей дыре с неиспользованными RecId не 25 номеров, а неограниченное количество, что естественно невозможно.
2 Gustav: Этот случай был просто не учтен Вами или есть какое-то решение?
Тэк-с... Давайте по порядку. Отдельно специально такой случай не изучался и к сожалению не могу его быстро проверить. Но есть мысли. Полагаю, что этот случай практически относится к моим опасениям с размером кэша, отличным от 25:
Цитата:
Сообщение от Gustav Посмотреть сообщение
2. У таблицы SystemSequence в Ax 3.0 имеется метод setCacheSize, позволяющий установить размер кэша иным, нежели 25. Перед использованием триггера рекомендуется проверить код приложения Аксапты на присутствие вызовов этого метода (у меня не было ни одного). При необходимости можно увеличить minDelta в триггере до значения максимального параметра этих вызовов, либо (более муторно) в триггере предусмотреть генерирование ошибки (исключения) при попытке Аксапты сделать шаг больше, чем 25.
Ключевая подстрока - "при попытке Аксапты сделать шаг больше, чем 25". Перед изменением nextVal в триггере вы контролируете и старое (:Old.NextVal), и новое (:New.NextVal) значения. И соответственно можете вычислить дельту и направить алгоритм в дырку соответствующего размера. Для быстрой реакции разумно будет добавить соответствующее поле HoleSize в таблицу RECIDHOLES. Естественно, придется должным образом модифицировать алгоритм триггера.

Логично? Или я выдаю желаемое за действительное? Еще раз отмечу, что в данный момент могу рассуждать только теоретически.
Старый 14.01.2011, 14:10   #37  
Alenka is offline
Alenka
Участник
 
58 / 25 (1) +++
Регистрация: 19.04.2006
Нет, такой анализ не поможет, т.к. проанализировать-то можно, а исправить ничего уже будет нельзя, т.к. вставляемым записям будут присваиваться RecId, начиная со старого значения. Т.е. надо было при предыдущем смещении nextVal "предусмотреть" возможность вставки с помощью insert_recordset.
Старый 14.01.2011, 14:14   #38  
Alexius is offline
Alexius
Участник
Аватар для Alexius
 
461 / 248 (9) ++++++
Регистрация: 13.12.2001
Цитата:
Сообщение от Gustav Посмотреть сообщение
Перед изменением nextVal в триггере вы контролируете и старое (:Old.NextVal), и новое (:New.NextVal) значения.
Беда в том, что (:Old.NextVal) и является первым выделенным RecId для текущей вставки, а (:New.NextVal) очередной RecId уже для следующей вставки и изменить можно только его.
Старый 14.01.2011, 14:23   #39  
Alexius is offline
Alexius
Участник
Аватар для Alexius
 
461 / 248 (9) ++++++
Регистрация: 13.12.2001
Цитата:
Сообщение от Alenka Посмотреть сообщение
... "предусмотреть" возможность вставки с помощью insert_recordset.
Боюсь это придется делать в АХ, т.е. "прошерстить" код и делать уже там перенаправление на самую большую дырку

Можно попробовать забить на эту проблему, ну не пройдет с первого раза транзакция, так пройдет со второго
Потом в АХ найдется немного мест, которые требуют уникальности RecId в разрезе нескольких таблиц, т.ч. шансы на удачный исход дополнительно повышаются
За это сообщение автора поблагодарили: Gustav (2).
Старый 14.01.2011, 14:25   #40  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от Alenka Посмотреть сообщение
Нет, такой анализ не поможет, т.к. проанализировать-то можно, а исправить ничего уже будет нельзя, т.к. вставляемым записям будут присваиваться RecId, начиная со старого значения. Т.е. надо было при предыдущем смещении nextVal "предусмотреть" возможность вставки с помощью insert_recordset.
Цитата:
Сообщение от Alexius Посмотреть сообщение
Беда в том, что (:Old.NextVal) и является первым выделенным RecId для текущей вставки, а (:New.NextVal) очередной RecId уже для следующей вставки и изменить можно только его.
Да, да! Не прав я, коллеги, признаю!

insert_recordset, желающий вставить 100 записей (вместо 25), вероятно, возьмет старый nextVal и прибавит к нему 100 и попытается сохранить в SystemSequence для последующих запросов.

Что можем мы в этой ситуации? Можем увидеть, что собирается быть выполненным шаг больше 25, а также проверить существует ли свободное пространство в текущей дыре для выполнения этого шага. Если оно есть, то всё в порядке. Если его нет, то выбирать следующую подходящую дыру бесполезно, так как наш insert_recordset уже запомнил для себя 100 последовательных номеров - надо генерить ошибку (исключение). При этом можно, конечно, уже иметь на примете следующую подходящую дыру.

В общем, дальнейший успех зависит от того, сможем ли мы хорошо обработать это исключение. Тогда текущая попытка увеличения nextVal будет как бы холостым шагом, а следующая после обработки исключения попытка выделения 100 номеров будет успешной. Блин, должна быть!
Теги
ax3.0, recid, дефрагментирование recid, законченный пример, полезное

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
if (record) vs if (record.RecId) kashperuk DAX: Программирование 18 27.11.2008 18:53
поля, содержащие RecId somebody DAX: Программирование 15 16.05.2008 17:50
Что лучше select RecId или select TableId Logger DAX: Программирование 9 02.06.2007 15:13
aEremenko: Дефрагментация RecID Blog bot DAX Blogs 2 06.03.2007 22:25
Два RecId у одной записи таблицы sparur DAX: Программирование 33 18.12.2006 15:56
Опции темы Поиск в этой теме
Поиск в этой теме:

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

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

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

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