09.06.2008, 18:36 | #21 |
MCTS
|
Цитата:
Сообщение от Gustav
В Excel нет отдельного типа "Время" (не путать с форматом, отображающим только время, без даты). Есть единый тип "Дата", в котором собственно дата является целым числом (до запятой), а время дробным (после запятой). Особенно хорошо это видно при программировании на VBA: есть тип Date, но нет Time.
Чтобы загрузить время в Аксапту следует до импорта из Excel в самом Excel: - либо превратить время в вещественный тип путем назначения столбцу формата General (Общий); - либо превратить его в вещественный тип, умножить на 86400 и тащить в Аксапту в виде общего количества секунд; - либо распарсить исходное время на часы, минуты и секунды, импортировать эти 3 значения по отдельности и уже в Аксапте собирать, - либо превратить в текст вида "09:30:00" при помощи функции вроде =ТЕКСТ(A1,"[ЧЧ]:ММ:СС"). |
|
02.10.2008, 17:52 | #22 |
Участник
|
Как перейти к конкретной записи recordset?
Почитал help на эту тему, там есть метод move, но заставить его работать у меня не получилось. Ответ нашелся очень просто: X++: recordset.move( NumRecords, Start ); Последний раз редактировалось Stainless; 02.10.2008 в 18:05. |
|
02.10.2008, 19:02 | #23 |
Moderator
|
хм, в хелпе тоже самое, вроде:
Цитата:
Сообщение от файл ADO210.CHM
Move Method
Moves the position of the current record in a Recordset object. Syntax recordset.Move NumRecords, Start |
|
07.11.2008, 13:21 | #24 |
Moderator
|
э-хе-хе...
Испытываю возобновление интереса к теме и вот в связи с чем. ADODB.Recordset имеет полезнейшие свойства Filter и Sort, которыми можно эффективно пользоваться, фильтруя исходный набор или изменяя порядок записей исходного Recordset'а и далее перебирая в цикле нужные записи в нужном порядке.
Резонно было бы предположить, хотя про это нигде не сказано в справке, что методом CopyFromRecordset должен выгружаться набор записей с учетом фильтрации и сортировки. Увы, при управлении Excel из Аксапты этого не происходит: X++: static void Job103(Args _args) { #CCADO ComExcelDocument_RU doc = new ComExcelDocument_RU(); COM rst = new COM('ADODB.Recordset'); COM xlApp; COM wbook; COM activeSheet; COM range; COM flds,fld; int i; ; doc.NewFile('',false); wbook = doc.getComDocument(); xlApp = wbook.Parent(); activeSheet = xlApp.ActiveSheet(); flds = rst.Fields(); flds.Append('myField1', #adVarChar, 50); flds.Append('myField2', #adInteger); rst.Open(); for (i=1;i<=1000;i++) { rst.AddNew(); fld = flds.Item(0); fld.Value(int2str(i)); fld = flds.Item(1); fld.Value(i*2); rst.Update(); } rst.Filter("myField1 Like '*24*'"); rst.Sort('myField1 DESC'); rst.MoveFirst(); range = activeSheet.Range('A1'); range.CopyFromRecordset(rst, 300); range = activeSheet.Range('A1000'); range.CopyFromRecordset(rst, 300); doc.visible(true); // чтобы показать, что набор действительно фильтруется и сортируется rst.MoveFirst(); while (! rst.EOF()) { info(strFmt('%1 --- %2', new CCADOField( flds.Item(0) ).value(), new CCADOField( flds.Item(1) ).value())); rst.MoveNext(); } } Но этого не происходит. Вместо этого - и начиная с ячейки A1, и начиная с ячейки A1000 - одинаково выводятся первые 300 записей исходного набора. А хотелось бы, чтобы вывелись только 20 записей с ячейки A1, т.е. те записи, которые пример выводит в окно Infolog. Возможно мои претензии необоснованы и CopyFromRecordset должен себя вести именно так, как ведет себя в этом примере? Я уже было засомневался, вернее, почти отказался от своих "претензий", но решился на еще одну проверку - в самом Excel на VBA: Код: Sub Job103_VBA() Dim i As Integer Dim rst As Object Dim flds As Object Dim xlApp As Excel.Application Set rst = CreateObject("ADODB.Recordset") 'я сознательно создал rst через CreateObject, а не как New ADODB.Recordset, 'чтобы не искать ссылку на ADO через Tools\References Set flds = rst.Fields flds.Append "myField1", 200, 50 '200 = adVarChar flds.Append "myField2", 3 '3 = adInteger rst.Open For i = 1 To 1000 rst.AddNew rst.Fields(0).Value = i rst.Fields(1).Value = i * 2 rst.Update Next i rst.Filter = "myField1 Like '*24*'" rst.Sort = "myField1 DESC" rst.MoveFirst 'Set xlApp = New Excel.Application Set xlApp = Application xlApp.Workbooks.Add xlApp.Range("A1").CopyFromRecordset rst, 300 xlApp.Range("A1000").CopyFromRecordset rst, 300 xlApp.Visible = True End Sub Код: Set xlApp = New Excel.Application 'Set xlApp = Application Есть ли у кого какие-нибудь соображения? Может, кто-нибудь интересующийся продвинуто заглянет какими-нибудь крутыми вьюерами в тайны происходящих процессов COM-автоматизации? Может быть удастся победить ситуацию использованием каких-нибудь COMDispFunction? P.S. Проделал даже такой финт. Создал шаблон xlt, в который зашил VBA-процедуру: Код: Sub myCopyFromRecordset(ByVal rng As Range, ByVal rst As ADODB.Recordset, ByVal maxRows As Integer) rng.CopyFromRecordset rst, maxRows End Sub X++: xlApp.Run('myCopyFromRecordset', range, rst, 300); |
|
07.11.2008, 20:12 | #25 |
Administrator
|
Цитата:
Я удивлен другим. Я удивлен - что CopyFromRecordset все-таки может копировать с учетом фильтрации - но об этом видимо "помнит" только текущее приложение - т.е. поддержка реализована не на уровне "ядра" ADO
__________________
Возможно сделать все. Вопрос времени |
|
23.04.2009, 11:32 | #26 |
Участник
|
Возникла проблема с экспортом через ADO в Excel полей с типом дата. Если переносить как adbSTR, то это не устраивает конечных пользователей (не работает нормльно фильтрация). Если переносить как adDBDate, то datenull() отображается в Excel как 02.01.1900. Применение формата "ДД.ММ.ГГГГ" к ячейке не помогает. Можно ли как-нибудь передать дату так, чтобы пустая дата не отображалась?
|
|
23.04.2009, 11:40 | #27 |
Участник
|
Сам нашел выход. Если значение поля в Axapte datenull(), то нужно писать в ADO field.value() не значение поля, а просто 0. А в Excel у активного листа выставить в false параметр DisplayZeros(0).
|
|
23.04.2009, 11:44 | #28 |
Участник
|
|
|
|
За это сообщение автора поблагодарили: Gustav (2). |
23.04.2009, 12:06 | #29 |
Участник
|
Тот же результат, что и с 0.
|
|
23.04.2009, 13:58 | #30 |
Moderator
|
Да, именно так и надо делать. Лично я - сторонник этого способа
У меня так происходит, если тип поля рекордсета задавать константой adDate(7). А если взять adDBDate, которая 133, то на месте нулевых дат в Excel будут желаемые пустые ячейки. Во всяком случае для сочетания Ax 3.0 SP4 + Oracle 10 + Excel 2003 это так. |
|
23.04.2009, 17:29 | #31 |
Moderator
|
Пользуясь сегодняшнем случаем наконец-то подправил в своем рабочем классе (практически в персональном "пролетариате") метод, устанавливающий значение в поле рекордсета:
X++: #CCADO void setValue(anytype _fldName, anytype _fldValue) { // _fldName - можно текстовое имя, а можно числовое, начиная с 1 (!), а не 0 как в самом ADO anytype fldName; ; if (typeof(_fldName)==Types::Integer) fldName = _fldName - 1; else fldName = _fldName; fld = flds.Item(fldName); // COM flds и fld - полЯ и полЕ рекордсета, определены в Class Declaration if (! ((fld.Type()==#adDate || fld.Type()==#adDBDate) && !_fldValue) ) { fld.Value(_fldValue); // если непустая дата или любое др.значение } // иначе если значение - пустая дата, то вообще не прописываем в поле } X++: if (ltDb.TransDate)
out2xls.setValue(nextNum(),ltDb.TransDate); |
|
29.04.2009, 13:16 | #32 |
Участник
|
А я во вспомогательном канале вывода в Excel зашел с другого конца: при создании структуры ADO.Recordset я указываю опциональный признак того, надо ли в соотв. колонке избавиться от "незначащих" значений, и после вывода просто делают замену средствами самого Excel таких значений на то, что возвращает COMVariant::createNoValue()
X++: // очистка ячеек с "пустыми" значениями (ноль для чисел либо 01.01.1900 для дат), чтоб не надо было докручивать шаблон для их сокрытия // _cell - ячейка, на которой в outputReportBody() вызывается метод CopyFromRecordset() protected void clearEmptyCells(COM _cell) { COMVariant cvSrcValue; COMVariant cvDstValue; str strAddr; container conFieldInfo; boolean bClearEmpty; Counter cnRows; Counter n; COM oRng; // область, в которой будет производиться замена ; cnRows = rstAxa.RecordCount(); // количество выведенных строк данных cvDstValue = COMVariant::createNoValue(); // создаем пустое значение for (n = 1; n <= arrFields.lastIndex(); n++) { conFieldInfo = arrFields.value( n ); if (conlen(conFieldInfo) >= #ConPosFieldInfoClearEmpty) // по умолчанию этого поля в контейнеере быть не должно { bClearEmpty = conpeek( conFieldInfo, #ConPosFieldInfoClearEmpty ); if (!bClearEmpty) continue; cvSrcValue = this.getEmptyVariantValue( conpeek( conFieldInfo, #ConPosFieldInfoType ) ); if (!cvSrcValue) // если вернулся null, значит, тип не поддерживается continue; // формируем адрес диапазона ячеек *относительно* _cell strAddr = ComExceldocument_RU::numToNameCell( n, 1 ); if (cnRows > 1) strAddr += @':' + ComExceldocument_RU::numToNameCell( n, cnRows ); oRng = _cell.range( strAddr ); // получаем столбец внутри диапазона _cell oRng.Replace( cvSrcValue, cvDstValue, #xlWhole, #xlByColumns ); } } } // возвращает COMVariant, представляющий "пустое" значение для указанного типа ADO, либо null, если указанный тип не поддерживается protected COMVariant getEmptyVariantValue( Integer _adoType ) { COMVariant ret; ; switch( _adoType ) { case #adDate : case #adDBDate : case #adDBTimeStamp : ret = COMVariant::createFromDateAndTime( datenull(), 0 ); break; case #adSingle : case #adDouble : case #adCurrency : case #adDecimal : case #adNumeric : ret = COMVariant::createFromReal( 0.0 ); break; case #adTinyInt : case #adSmallInt : case #adInteger : case #adBigInt : case #adUnsignedTinyInt : case #adUnsignedSmallInt : case #adUnsignedInt : case #adUnsignedBigInt : ret = COMVariant::createFromInt( 0 ); break; default : ret = null; break; } return ret; } PS. По ходу реализации наткнулся на одни "грабли": оказалось, что COMVariant::createFromDate( datenull() ) возвращает COMVariant не со значением 01.01.1900 00:00, как можно было бы ожидать, а со значением 01.01.1900 <текущее_время>! |
|
29.04.2009, 15:17 | #33 |
Moderator
|
Цитата:
Вот почему мы любим тип #adDBDate (133), который, как положено, сохраняет только дату без времени |
|
|
За это сообщение автора поблагодарили: alex55 (1). |
06.05.2010, 10:00 | #34 |
Участник
|
Как передать в Excel (через ADO) число типа real так, чтобы оно не преобразовывалось в дату? Я указывают тип поля adDouble, а Excel всё равно преобразует в дату.
|
|
06.05.2010, 10:29 | #35 |
Участник
|
Цитата:
X++: static void Job20100506(Args _args) { #define.adDouble(5) COM Recordset; COM Fields; COM Field; COM Application; COM Workbooks; COM Workbook; COM Worksheets; COM Worksheet; COM Range; ; Recordset = new COM('ADODB.Recordset'); Fields = Recordset.Fields(); Fields.Append('Field1', #adDouble); Recordset.Open(); Recordset.AddNew(); Field = Fields.Item('Field1'); Field.Value(123.45); Recordset.Update(); Application = new COM('Excel.Application'); Workbooks = Application.Workbooks(); Workbook = Workbooks.add(); Worksheets = Workbook.Worksheets(); Worksheet = Worksheets.Item(1); Range = Worksheet.Range('A1'); Range.CopyFromRecordset(Recordset); Recordset.Close(); Application.Visible(true); } |
|
06.05.2010, 10:35 | #36 |
Moderator
|
Тоже не воспроизвелось
X++: static void Job298(Args _args) { #CCADO COM rng = SysExcelApplication::construct().workbooks().add().worksheets().itemFromNum(1).range('A1').comObject(); COM rst = new COM('ADODB.Recordset'); COM flds = rst.Fields(); COM fld; int i; flds.Append('MyReal', #adDouble); rst.Open(); fld = flds.Item('MyReal'); for (i=1; i<=10; i++) { rst.AddNew(); fld.Value(100 + i/10); rst.Update(); } rng.CopyFromRecordset(rst); COM::createFromObject(rng.Application()).Visible(true); // отобразим Excel } |
|
06.05.2010, 10:51 | #37 |
Участник
|
Цитата:
А меня в шаблоне в ячейке указан формат "Число". И если передавать как строку - всё работает. Но уже второй день бьюсь над этим. Пришлось передавать поля типа "Строка" |
|
06.05.2010, 11:11 | #38 |
Moderator
|
Я обычно adDouble не использую, а использую для adCurrency.
Конечно, на файл шаблона бы взглянуть... Хотя бы на тот его фрагмент, куда пишется adDouble. Можно выложить? |
|
06.05.2010, 13:01 | #39 |
Участник
|
Выложил результат работы и шаблон
Последний раз редактировалось Roman N. Krivov; 06.05.2010 в 13:05. |
|
06.05.2010, 14:07 | #40 |
Moderator
|
Роман, у меня два вопроса:
|
|
Теги |
ado, comvariant, excel, faq, odbc, sql, интеграция, прямой доступ, формат дат, экспорт, экспорт в excel |
|
|