Показать сообщение отдельно
Старый 01.02.2010, 16:27   #6  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Неоднократно занимался подобной штукой в одиночестве на тестовой базе (Zabr абсолютно прав! ), причем, последний раз не так давно - 16.12.2009, как следует из условия в while select (см. в джобе). После постинга пробегался по всем таблицам Аксапты, имеющим записи и запоминал из них несколько (например, 200) самых последних по RecId. Дальше выгружал в Excel, сортировал по RecId и смотрел записи в районе тех RecId, в которых я был уверен, что они относятся к исследуемой операции (обычно известны RecId ранесенных записей из LedgerTrans). Пару раз обнаруживал таблицы, о которых откровенно не знал, что они задействованы при данной операции!

Вот мой джоб:
X++:
#CCADO
#define.rowLimit( 200 )

static void Job235_whereRecId(Args _args)
{
    Dictionary  dictionary = new Dictionary();
    TableId     tableId;
    DictTable   dictTable;
    Common      common;
    COM         rst, flds, fld;
    int         row, i, timeStart;
    ;

    timeStart = timenow();

    rst = AdoRst::openRecordsetInMemory([
            ['TableId'   , #adInteger  ],
            ['TableName' , #adVarChar  ],
            ['RecId'     , #adInteger  ]]);

    flds = rst.Fields();

    // цикл по таблицам
    for (i=1; i<= dictionary.tableCnt(); i++)
    {
        tableId   = dictionary.tableCnt2Id(i);
        dictTable = new DictTable(tableId);

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

        // если в очередной таблице нет записей
        // то переходим к следующей
        if (! new SysDictTable(tableId).recordCount())
            continue;

        common = dictTable.makeRecord();

        row = 0;
        // цикл по последним записям таблицы
        while select common order by RecId desc
            where common.createdDate == 16\12\2009
               && common.createdBy   == 'MNY'
        {
            row++;
            if (row > #rowLimit) break;
            rst.AddNew();
                fld = flds.Item(0); fld.Value(tableId);
                fld = flds.Item(1); fld.Value(dictTable.name());
                fld = flds.Item(2); fld.Value(common.RecId);
            rst.Update();
        }
    }

    AdoRst::sendRecordsetToExcel(rst);

    info(strfmt('Время выполнения: %1 сек', timenow()-timeStart));
}
А это к нему используемая статика (класс у меня, как видно выше, называется AdoRst):

* Метод openRecordsetInMemory можно взять в моем блоге: http://axforum.info/forums/blog.php?b=60

* Метод sendRecordsetToExcel имеет следующий вид (и к нему еще внутри - deleteFilteredRecords):
X++:
// Created on 06 Ноя 2008 at 16:26:03 by KKU

// выводит в Excel переданный ADODB.Recordset (обычно это disconnected recordset в памяти)

// если записи выводятся на несколько листов, то рекордсет в процессе вывода "ломается"
// за счёт удаления уже выведенных в Excel записей

// возвращает false, если удалений записей не было (т.е. всё вывелось на один лист и рекордсет остался нетронутым)
// или true - если были удаления (rstWasChanged)
static boolean sendRecordsetToExcel( COM    _rst,
                                     int    _maxRows = 65000,
                                     int    _maxColumns = 0)
{
    COM         xlApp   = new COM('Excel.Application');
    COM         wbks    = xlApp.Workbooks();
    COM         wbk     = wbks.Add();
    COM         wkss    = wbk.Worksheets();
    COM         wks;
    COM         rng;
    COM         cell;

    COM         column;
    COM         rngCols;
    COMVariant  columnWidth;

    COM         flds    = _rst.Fields();

    int         i;
    // разобраться здесь _maxColumns- то считается с единицы
    int         iMax    = (_maxColumns) ? (min(_maxColumns,flds.Count())) : flds.Count();
    int         sheet   = 1;

    #CCADO
    ;

    // раз уж CopyFromRecordset, как выясняется, выводит без фильтра,
    // то на всякий случай еще и принудительно их удалим (а то вдруг починятся когда-нибудь у Microsoft)
    _rst.Filter('');
    _rst.Sort('');

    while (true)
    {
        // добавляем лист, если нужно
        if (sheet > wkss.Count())
            wkss.Add(COMArgument::NoValue, wkss.Item(wkss.Count()));

        wks = wkss.Item(sheet);
        wks.Activate(); // для отображения ДАТЫ как ДАТЫ на 2-м и 3-м существующих листах

        // выводим строку имен полей (1-я строка листа Excel)
        rng = wks.Range('A1');
        for (i = 1; i <= iMax; i++)
        {
            cell = rng.Offset(0, i-1);
            cell.Value2( COM::createFromObject( flds.Item(i-1) ).Name() );
        }
        rng = rng.CurrentRegion();
        COM::createFromObject( rng.Font() ).Bold(true); // делаем выведенные заголовки жирным шрифтом

        // выводим данные, начиная со 2-й строки листа Excel
        rng = wks.Range('A2');
        if ( !(_rst.BOF() && _rst.EOF()) ) // если есть записи
        {
            _rst.MoveFirst();
            if (_maxColumns)
                rng.CopyFromRecordset( _rst, _maxRows, _maxColumns );
            else
                rng.CopyFromRecordset( _rst, _maxRows);
        }

        // подгонка ширины колонок (с ограничением не более 35)
        rng = rng.CurrentRegion();
        rngCols = rng.Columns();
        COM::createFromObject( rngCols.EntireColumn() ).AutoFit();

        for (i = 1; i <= iMax; i++)
        {
            column = COM::createFromVariant( rngCols.Item(i) );
            columnWidth = column.ColumnWidth();

            if (columnWidth.double() > 35) column.ColumnWidth(35);
        }

        // здесь накручено, чтобы не пользоваться RecordCount
        // --------------------------------------------------
        // попытка достичь конца файла, продвинувшись от начала на количество выведенных записей
        // у нас это кол-во всегда одно - _maxRows, так как мы удаляем записи на каждом шаге
        // точнее ПОСЛЕ каждого (поэтому, если лист один, то рекордсет сохраняется в первозданном виде)
        if (!_rst.EOF())
            _rst.Move(_maxRows, #adBookmarkFirst);
        // если вдруг конец файла уже был достигнут ранее, то его обработает следующий оператор

        if (!_rst.EOF())
        {
            AdoRst::deleteFilteredRecords(_rst, _maxRows);
            sheet++;
        }
        else
        {
            break; // иначе завершаем цикл вывода на листы
        }
    }

    xlApp.Visible(true);

    if (sheet > 1)
        return true; // rstWasChanged - если листов более одного, то исходный рекордсет не сохранен
    else
        return false;
}

/*
    Удаляет "видимые" записи из текущего рекордсета.

    Если рекордсет был отфильтрован, то будут удалены только записи, соответствующие фильтру.

    Можно задать удаление не всех отфильтрованных записей,
    а только несколько первых (_numRecords > 0).
    В этом случае имеет смысл перед удалением применить сортировку,
    чтобы гарантировать определенный порядок удаляемых записей.
*/
static int deleteFilteredRecords(COM _rst, int _numRecords = 0)
{
    int affectedRecords = 0;
    int i;

    void deleteOneRecord()
    {
        affectedRecords++;
        _rst.Delete();
        _rst.MoveNext();
    }
    ;

    if (_rst.BOF() && _rst.EOF()) return 0;  // если набор пуст - выходим

    _rst.MoveFirst();
    if (_numRecords)
    {
        for (i=1;i<=_numRecords;i++)
        {
            if (!_rst.EOF())
                deleteOneRecord();
            else
                break;  // если _numRecords больше реального числа записей
        }
    }
    else
    {
        while (!_rst.EOF())
            deleteOneRecord();
    }
    _rst.MovePrevious(); /* так надо, так как после последнего удаления
                            указатель находится за последней записью (EOF=true),
                            но при этом BOF=false - так вот для этой коррекции и надо,
                            чтобы стало и BOF=true, и EOF=true

                            если же в наборе остались записи,
                            то это действие поставит указатель на последнюю запись
                         */
    return affectedRecords; // кол-во удаленных записей
}

Последний раз редактировалось Gustav; 01.02.2010 в 16:30.
За это сообщение автора поблагодарили: mazzy (2), alex55 (1), Proba (1), andriy_s (1).