Форум: "Прочее";
Текущий архив: 2011.03.20;
Скачать: [xml.tar.bz2];
ВнизКнига по ADO для не совсем чайника Найти похожие ветки
← →
ProgRAMmer Dimonych © (2010-11-22 22:13) [0]Хочу наконец-то взяться за освоение ADO в Delphi. С базами данных до этого работал связкой PHP+MySQL, успешно. Возникает куча проблем с тем, чтобы выбирать этих провайдеров, размещать БД и т.п. Гугление по теме даёт мало полезного.
Очень хотелось бы получить ссылки на книжки, где нет тупого перечисления свойств Delphi-йских компонентов и копипасты с учебника по реляционным БД, а описаны конкретно особенности использования ADO, то, как правильно настроить его для конкретной задачи.
Заранее благодарен.
← →
sniknik © (2010-11-22 22:37) [1]противоречивые требования... конкретно особенности, и вдруг для конкретной задачи...
нет такого. особенности, и настройки/описание обычно идут отдельно от конкретики. файл справки ADO210.CHM на твоем же компе, там и особенности и настройки, но естественно без конкретных задач.
> Гугление по теме даёт мало полезного.
гугление дает ВСЕ, главное правильно выбрать ключевые слова.
← →
ProgRAMmer Dimonych © (2010-11-22 22:53) [2]ОК. Гугл в частности выкинул на учебник на 256bit.ru. Где просто перечисляются объекты и их свойства. Этакая копипаста со справки. Остальное - обрывки. Ну, и ссылки на Фленовскую "Библию Delphi", но, помнится, не слишком лестные об этом авторе отзывы здесь были в своё время.
← →
MsGuns © (2010-11-23 00:21) [3]Насколько знаю, исчерпывающего пособия по АДО нет и не может быть в принципе, т.к. это не СУБД, а всего лишь технология, инструмент, набор компонент.
Но есть, однако несколько "путеводных" :)
1) Стандарт SQL, надо понимать, относящися к конкретной СУБД, см. соотв. мануалы к серверам
2) Строка подключения - ищи в инете ресурсы с ключевым словом ConnectionString. Ну и по форумам.
3) Особенности именно дельфишных компонент. Ничего лучше справки не видел, ну еще опыт, конечно. Очень может помочь поиск по этому сайту, в частности ремарки SnikNik, пожалуй, главного спеца в этой области на данном ресурсе.
А вообще лучше всего изучать АДО не вообще, а применительно к конкретному серверу, ИМХО, лучше всего к MS SQL Server
← →
MsGuns © (2010-11-23 00:24) [4]Я сейчас дома сижу в основном, можешь стучаться в асю по мере надобности, а сорсу слать на мыло. И то, и другое есть в анкете.
Опыт у меня в основном по мсскл и немного по акцесу
Могу выслать библиотеки свои, где много наваено именно для АДО
← →
MsGuns © (2010-11-23 00:28) [5]Фленова, Архангельского, Фаронова - сразу фтопку !
← →
MsGuns © (2010-11-23 00:36) [6]Вот по строке нашел (такой сложный урл :) )
http://www.connectionstrings.com/
← →
Дмитрий Тимохов (2010-11-23 16:02) [7]ADODB пользуюсь 10 лет, но еще тогда был вынужден отказаться от дельфишных компонент - они не умеют работать с типом TDecimal (тип MSSQL-Server"а Decimal(28,10). Такой тип у нас есть в БД.
Вернее, они умеют, но преобразовывают TDecimal в Double или Exctended. Если интересно, то могу посмотреть в деталях. На этот счет даже в Quality Central уже лет 8 лежит соотв. запрос на доработку.
Поэтому я просто импортировал соотв. библиотеку. С ней и работаю по раннему связыванию на своих компонентах.
Если особо не вдаваться в специфические функции ADODB (асинхронность, серверные курсоры и т.д.), то там все просто, я считаю.
У меня за все время только одна проблема была с ADODB. Но я думаю, что это скорее проблема MSSQL. Справился не очень красиво, но работает - иначе не смог. Вот мой коммент по этому поводу. Может полезно будет.
// В ходе эксплуатации было установлено, что в некоторых
// случаях в ходе выполнения сложной сохраненной
// процедуры происходит ее перекомпиляция (суд по профайлеру),
// что приводит к тому, что возвращается 2 рекордсета -
// первый пустой, а второй - с данными.
// Поиск причин подобного поведения не дал результатов.
// Поэтому было принято решение в тех случаях,
// где предполагается наличие рекордсета
// искать первый открытый рекордсет и его возвращать.
// Вот если такой рекордсет не найден возвращать ошибку.
← →
Sergey Masloff (2010-11-23 17:13) [8]Вот как раз готовил на выкид книжки - есть А.Федоров - Н.Елманова "ADO в Delphi" изд. 2000 год ISBN 5-94157-098-8 790 страниц, CD в комплекте. Состояние как новая.
Отдам за бутылку хорошего пива. Предложение действительно до пятницы - в субботу выкину.
← →
Дмитрий Тимохов (2010-11-23 17:55) [9]А за 2 бутылки, Сергей, можешь отдашь сети Оланда? :)))
← →
sniknik © (2010-11-23 18:12) [10]> что приводит к тому, что возвращается 2 рекордсета -
> первый пустой, а второй - с данными.UPDATE Action SET Name="" WHERE Name=""
SELECT * FROM Action
Выполнено применительно к 0 записям. (no recordset)
применительно к 16 записям
т.е. 2 рекордсета, один пустой, другой с данными. (рекордсет он не только для данных, там еще и ошибки возвращаются, информация ... кому не нужно должен пропускать)
или -
SET NOCOUNT ON
UPDATE Action SET Name="" WHERE Name=""
SELECT * FROM Action
SET NOCOUNT OFF
Выполнено применительно к 16 записям
т.е. 1 рекорсет, только данные.
← →
Дмитрий Тимохов (2010-11-23 18:54) [11]
> sniknik © (23.11.10 18:12) [10]
Ну ты эта, за новичка не держи.
Мы неделю сидели в глубокой отладке в профайлере.
Процедура по сама собой при своем выполнении перекомпилировалась.
Т.е. сама в ходе работы останавливалась, потом строила новый план, потом снова выполнялась. Вот с первого раза возвращался пустой роусет.
Я понимаю, что это специфично. Но мы на это неделю угорохали. Так и не нашли причину.
← →
MsGuns © (2010-11-23 19:02) [12]>Дмитрий Тимохов (23.11.10 18:54) [11]
>Процедура по сама собой при своем выполнении перекомпилировалась.
>Т.е. сама в ходе работы останавливалась, потом строила новый план, >потом снова выполнялась.
Работали по ночам ? Декалитры кофе, батареи сигарет ?
Это ничего, бывает. Рецепт простой - здоровый продолжительный сон
← →
sniknik © (2010-11-23 19:44) [13]> Процедура по сама собой при своем выполнении перекомпилировалась.
повторить сможешь? или хотя бы после бэкап, ресторе, на другом сервере повторяется?
← →
sniknik © (2010-11-23 19:46) [14]> Вот с первого раза возвращался пустой роусет.
вам бы с него ошибку считать, может быть и прояснилось чего.
← →
Sergey Masloff (2010-11-23 20:42) [15]Дмитрий Тимохов (23.11.10 17:55) [9]
Дим да я бесплатно тебе ее отдам если найду. Не пойму все перерыл уже :(
← →
Дмитрий Тимохов (2010-11-23 21:10) [16]
> sniknik © (23.11.10 19:46) [14]
>
> > Вот с первого раза возвращался пустой роусет.
> вам бы с него ошибку считать, может быть и прояснилось чего.
>
А как с роусета читать ошибку, если он пустой и закрытый?
Просвети.
← →
KSergey © (2010-11-23 21:22) [17]По-моему, про ADO самое полезное - это цикл из 3-х статей
http://delphikingdom.ru/asp/viewitem.asp?catalogid=408
и далее по ссылкам в конце каждой части (всего их три).
← →
sniknik © (2010-11-23 21:38) [18]> если он пустой и закрытый?
он не может быть закрытым, ведь он вернулся, и следующим за ним идет с данными... сам сказал.
вот, один из вариантов, ближе тебе если не используешь обертки.procedure TForm1.Button4Click(Sender: TObject);
var
cmd: _Command;
Conn: _Connection;
Pars: Parameters;
RA: OleVariant;
rs: _Recordset;
n: Integer;
begin
Conn:= CreateComObject(CLASS_Connection) as _Connection;
Conn.ConnectionString:= Edit1.Text;
Conn.Open(Conn.ConnectionString, "", "", Integer(adConnectUnspecified));
cmd:= CreateComObject(CLASS_Command) as _Command;
cmd.CommandType:= adCmdText;
Conn.CursorLocation:= adUseServer;
cmd.Set_ActiveConnection(Conn);
cmd.CommandText:= RichEdit1.Lines.GetText;
rs:= cmd.Execute(RA, 0, Integer(adCmdText));
while rs <> nil do begin
for n:= 0 to (Conn.Errors.Count-1) do
Memo1.Lines.Add(Conn.Errors.Item[n].Description);
rs:= rs.NextRecordset(RA);
end;
cmd.Set_ActiveConnection(nil);
Conn.Close;
Pars:= nil;
cmd:= nil;
Conn:= nil;
end;
проверить можно например PRINT-ом (в mssql это та же ошибка только низкого уровня (типа abort в делфи))
← →
Дмитрий Тимохов (2010-11-24 09:46) [19]
> sniknik © (23.11.10 21:38) [18]
>
> > если он пустой и закрытый?
> он не может быть закрытым, ведь он вернулся, и следующим
> за ним идет с данными... сам сказал.
>
Вот именно закрытый приходит.
Я его так достаю:
kAdoDb_Recordset := fAdoDb_Command.Execute(kRecordsAffected, EmptyParam, adCmdUnspecified);
while (kAdoDb_Recordset <> nil) and (kAdoDb_Recordset.State <> adStateOpen) do
kAdoDb_Recordset := kAdoDb_Recordset.NextRecordset(kRecordsAffected);
Assert(kAdoDb_Recordset <> nil, "Запрос не вернул таблицу данных!");
Ладно, это в общем специфика, видимо, моя. В настоящий момент не актуальная. Запишу как дело посмотреть детальней, когда время будет - может правда в ошибках приходит что-то?
А вопрос к тебе вот какой - как проставить Recordset.CursorType, если пользуешься для его получения Command.Execute?
Я тут озадачился, а какой у меня CursorType - оказалось, что статик (умолчательное значение). А мне и ForwardOnly подошел бы. Хочу его поставить, но не пойму как.
← →
MsGuns © (2010-11-24 15:22) [20]Курсор выставляется у соединения. Причем ДО его установления
← →
Дмитрий Тимохов (2010-11-24 17:24) [21]
> MsGuns © (24.11.10 15:22) [20]
>
> Курсор выставляется у соединения. Причем ДО его установления
Неа, CursorLocation устанавливается у соединения, CursorType у Recordset - либо в свойстве CursorType, либо в методе Open.
А вот если я пользуюсь не Recordset.Open, а Command.Execute. Тады как?
← →
Anatoly Podgoretsky © (2010-11-24 18:52) [22]Чего ой?
← →
Дмитрий Тимохов (2010-11-24 18:55) [23]
> Anatoly Podgoretsky © (24.11.10 18:52) [22]
>
> Чего ой?
Это кому? )
← →
Anatoly Podgoretsky © (2010-11-24 19:02) [24]> Дмитрий Тимохов (24.11.2010 18:55:23) [23]
А кто ойкает?
← →
Дмитрий Тимохов (2010-11-24 19:06) [25]
> Anatoly Podgoretsky © (24.11.10 19:02) [24]
>
> > Дмитрий Тимохов (24.11.2010 18:55:23) [23]
>
> А кто ойкает?
ты о чем конкретно?
я в чем-то не прав?Connection15 = interface(_ADO)
...
function Get_CursorLocation: CursorLocationEnum; safecall;
procedure Set_CursorLocation(plCursorLoc: CursorLocationEnum); safecall;
Recordset15 = interface(_ADO)
...
function Get_CursorType: CursorTypeEnum; safecall;
procedure Set_CursorType(plCursorType: CursorTypeEnum); safecall;
Собственно и вопрос, как поставить этот CursorType, если для получения Recordset я использую методCommand15 = interface(_ADO)
...
function Execute(out RecordsAffected: OleVariant; const Parameters: OleVariant; Options: Integer): _Recordset; safecall;
← →
MsGuns © (2010-11-24 19:28) [26]>Дмитрий Тимохов (24.11.10 17:24) [21]
>Неа, CursorLocation устанавливается у соединения, CursorType у Recordset ->либо в свойстве CursorType, либо в методе Open.
Да, конечно, невнимательно прочитал. Извини
>А вот если я пользуюсь не Recordset.Open, а Command.Execute. Тады как?
Дык это не одно и то же ?
Command.Execute возвращает 1-й рекордсет, если он есть или нил, разве не так ?
← →
MsGuns © (2010-11-24 19:31) [27]>Я тут озадачился, а какой у меня CursorType - оказалось, что статик
>(умолчательное значение). А мне и ForwardOnly подошел бы. Хочу его >поставить, но не пойму как.
А зачем собственно. Что, здоровый НД возвращается и тормоза при перемещении ? Не проще ли просто конкретизировать запрос, уменьшая к-во выбираемых записей ?
← →
MsGuns © (2010-11-24 19:33) [28]Вдогонку. Если курсор не серверный, не один ли фиг, двунаправленный он или однонаправленный ? Или курсор все же на сервере ?
← →
Дмитрий Тимохов (2010-11-24 19:43) [29]
>
> Дык это не одно и то же ?
> Command.Execute возвращает 1-й рекордсет, если он есть или
> нил, разве не так ?
ну в общем не одно и то же )
Command.Execute он же может выполнять запрос с параметрами (отсюда сохранение плана запроса для будущих нужд и прочие хорошие вещи), а Recordset.Open - нет.
> А зачем собственно. Что, здоровый НД возвращается и тормоза
> при перемещении ? Не проще ли просто конкретизировать запрос,
> уменьшая к-во выбираемых записей ?
Не, ну написано в справке, что Forward Only работает быстрее, значит им надо пользоваться. Все равно мне только в одну сторону надо бегать.
Насчет минимизации строк, ты прав, конечно. Но это только в планах - переписывать много надо. Но сделаем обязательно.
> Вдогонку. Если курсор не серверный, не один ли фиг, двунаправленный
Курсор клиентский. Серверными не пользуемся.
← →
Плохиш © (2010-11-24 20:06) [30]
> Дмитрий Тимохов (24.11.10 19:43) [29]
> Не, ну написано в справке, что Forward Only работает быстрее,
> значит им надо пользоваться.
> Курсор клиентский. Серверными не пользуемся.
В моей справке ещё написано
Hinweis: Wenn die Eigenschaft CursorLocation der ADO-Datenmenge den Wert clUseClientOnly hat, wird nur die Konstante ctStatic unterstützt.
← →
Дмитрий Тимохов (2010-11-24 20:15) [31]
> Hinweis: Wenn die Eigenschaft CursorLocation der ADO-Datenmenge
> den Wert clUseClientOnly hat, wird nur die Konstante ctStatic
> unterstützt.
да все понятно:
Примечание: Если имущество CursorLocation набор данных ADO имеет значение clUseClientOnly поддерживается только постоянным ctStatic.
Ну и не надо нам Forward Only.
Я все равно собираюсь переделывать так (так быстрее процентов на 30 загружается):var
kRows: OleVariant;
kRow: Integer;
kRecordCount: Integer;
begin
kRS := SomeCommand.Execute(...);
kRecordCount := kRS.RecordCount;
kRows := kRS.GetRows(kRecordCount, adBookmarkFirst, VarArrayOf(["Field1", "Field2"]));
for kRow := VarArrayLowBound(kRows, 2) to VarArrayHighBound(kRows, 2) do
begin
kField1 := kRows[0, kRow];
kField2 := kRows[1, kRow];
...
end;
← →
Игорь Шевченко © (2010-11-24 20:18) [32]Дмитрий Тимохов (24.11.10 20:15) [31]
dbExpress не проще ? Оно точно в одну сторону
← →
Дмитрий Тимохов (2010-11-24 20:21) [33]
> dbExpress не проще ? Оно точно в одну сторону
я еще не изучил.
ладно, вопрос по ADO снимается в силу:
> Hinweis: Wenn die Eigenschaft CursorLocation der ADO-Datenmenge
> den Wert clUseClientOnly hat, wird nur die Konstante ctStatic
> unterstützt.
Я, видимо, в 2001 году и не сделал Forward, т.к. прочел эту документацию.
← →
sniknik © (2010-11-25 08:55) [34]> Я все равно собираюсь переделывать так (так быстрее процентов на 30 загружается):
очень странно. присвоение готового рекордсета (ссылки) vs копирование его в массив, и копирование выигрывает? что то не то в датском королевстве (не ADO имею в виду).
← →
Дмитрий Тимохов (2010-11-25 09:41) [35]
> sniknik © (25.11.10 08:55) [34]
>
> > Я все равно собираюсь переделывать так (так быстрее процентов
> на 30 загружается):
> очень странно. присвоение готового рекордсета (ссылки) vs
> копирование его в массив, и копирование выигрывает? что
> то не то в датском королевстве (не ADO имею в виду).
А ты попробуй десятка два Мб скачать. Понимаю, что для реальной задачи - нонсенс, но для опыта пойдет.
мой код быстрее, нежели просто Recordset.MoveNext.
Сам был удивлен. Мне этот код подсказал наш базаданщик - он его на SQL.RU где-то подсмотрел.
Ну, может, не 30, а 20. Но быстрее.
← →
Игорь Шевченко © (2010-11-25 10:19) [36]
> А ты попробуй десятка два Мб скачать.
Дружный смех в зале
← →
Дмитрий Тимохов (2010-11-25 11:06) [37]
> Игорь Шевченко © (25.11.10 10:19) [36]
> > А ты попробуй десятка два Мб скачать.
> Дружный смех в зале
А что здесь такого? А если это отчет на 838389389389389 страниц?
Или ты отчеты не строишь?
← →
Игорь Шевченко © (2010-11-25 12:23) [38]
> А что здесь такого?
объемы небольшие.
← →
Дмитрий Тимохов (2010-11-25 12:27) [39]мы не обсуждаем объемы, мы обсуждаем подход к чтению рекордсета в ADODB.
у тебя вообще оракл )
← →
Игорь Шевченко © (2010-11-25 13:12) [40]
> у тебя вообще оракл )
и ADO я не пользуюсь
← →
MsGuns © (2010-11-25 15:16) [41]На вопросы не отвечает, упорно лепит колеса квадратные, меняя кол-во спиц. Когда все равно хреново едет, то вместо колес сандалим гусеницы
>Дмитрий Тимохов (25.11.10 12:27) [39]
>мы не обсуждаем объемы, мы обсуждаем подход к чтению рекордсета в >ADODB.
Да как бы не совсем.
1) У вас курсор клиентский ? Если "да", то все ваши "стотыщпяццот" однофихственно тащятся по сетке и запоминаются в кэше.
2) Вместо того, чтобы просто взять указатель на этот рекордсет и "наживить" его на крючок вроде TADODataSet и спокойно бегать по нему как хочется (ессно, максимально отключив все его события типа AfterScroll), начинаем пассы с курсором.
3) Игнорируем "бегание" по невидимому НД реализовать в "фоновом" потоке, убрав "тормозятый" эффект
Короче, всему виной АДО и фантомы некой ХП, самопроизвольно компиллящейся по ночам :)
← →
MsGuns © (2010-11-25 15:19) [42]А вот кистати, ежели рекордсет пробегается строго в одном направлении, т.е. используется разово, вероятно для каких-то расчетных или статистических операций, то льзя ли все это леаризовать непосредственно на сервере, а клиенту отсылать уже аггрегированные результаты ?
← →
Дмитрий Тимохов (2010-11-25 15:23) [43]
> Короче, всему виной АДО и фантомы некой ХП, самопроизвольно
> компиллящейся по ночам :)
ты вообще о чем?
давай без телепатии.
у меня был вопрос - товарищ на немецком мне ответил, я понял.
заодно я привел пример быстрой загрузки (ты же не будешь утверждать, что не бывает случаев, когда тебе надо что-то загрузить и унести с собой на флешке, например, домой?!)
возможно, автору топика пригодится.
не веришь, что он быстрее, проверь.
не нужен тебе быстрый пример загрузки, пройди мимо в другую ветку.
зачем писать "знаю-что-но-не-знаю-зачем" так еще и с пафосом про кв. колеса.
>:-[
← →
sniknik © (2010-11-25 16:54) [44]> не веришь, что он быстрее, проверь.
не верю, но проверил, не быстрее.
87477 записей (не смотрел сколько весит таблица, но весь файл (mdb) где она основная, после упаковки 79.3 мг) открытие (т.е. выполнение запроса + отображение в гриде (не хотелось отключать в готовой "тестилке")) - 5.469 сек.
запрос + перекладывание в масив "по рецепту" выше, около 7сек (готовой "тестилки" нет, т.что засекал по секундной стрелке из "свойства: дата и время").
могу и точнее после, но по моему очевидно, любое дополнительное действие добавляет дополнительное время, какое бы оно, это действие, малое время не занимало.
← →
Дмитрий Тимохов (2010-11-25 17:09) [45]> sniknik © (25.11.10 16:54) [44]
>
> > не веришь, что он быстрее, проверь.
> не верю, но проверил, не быстрее.
я на MSSQL делал.
← →
sniknik © (2010-11-25 17:23) [46]> я на MSSQL делал.
какая разница, ты оперируешь уже на клиенте, готовым рекордсетом. данные в момент перед операцией копирования в масив уже полностью на клиенте, какая база послужила для них источником тут абсолютно "пофиг".kRecordCount := kRS.RecordCount;
вот это вот показывает сколько готовых данных (если бы был серверный курсор, то могло быть либо -1, либо только скачанная часть, которую ты бы только и получил т.к.kRS.GetRows(kRecordCount
, вернее рекордсет бы, если в асинхронном режиме дополучил бы все в конце концов, но в масив попала бы только часть готовая к моменту проверки (вот так и происходят "чудеса"))
← →
Дмитрий Тимохов (2010-11-25 17:27) [47]ладна, напишу тебе пример потом, запустишь, и сравним.
может действительно у меня специфика в железе есть.
← →
sniknik © (2010-11-25 17:50) [48]не верю в специфику когда A + B меньше чем просто A, при том, что и A и B гарантированно больше 0.
← →
MsGuns © (2010-11-26 01:30) [49]>Дмитрий Тимохов (25.11.10 15:23) [43]
>> Короче, всему виной АДО и фантомы некой ХП, самопроизвольно
>> компиллящейся по ночам :)
>ты вообще о чем? давай без телепатии.
Какая телепатия, Господь с тобою. Читай свои же посты, в частности [7] и[11]
Тебе тут пытаются объяснить, что ошибка (непонятность) вовсе не в АДОДБ, а "в головах", иными словами что-то делается не так, криво.
Пафоса не было, была попытка слегка юморнуть, но, очевидно, не до всех этот юмор доходит :)
А по поводу прохождения мимо.. Надо ответить или сам догадаешься ?
← →
Дмитрий Тимохов (2010-11-26 11:14) [50]
> sniknik © (25.11.10 17:50) [48]
>
> не верю в специфику когда A + B меньше чем просто A, при
> том, что и A и B гарантированно больше 0.
А вот зря не веришь. Я написал пример не поленился. У меня разница 40%.
Пример, конечно, надуманный, но как пример, я думаю, вполне сойдет.
ОПИСАНИЕ ПРИМЕРА
1. Обращение к серверу происходит по сети (т.е. не локальный сервер).
Сервер, правда, медленный изрядно - специально использую для разработки не быстрый сервер, чтобы не избаловаться ;)
2. Есть таблица adodb_test_table (a int, b int). Без индекса (незачем, мы же тестируем скорость выборки, а не скорость выполнения). В таблице 1млн записей - случайные целые числа от 0 до 1000.
3. База данных и таблица создаются прилагаемым скриптом DBGenerator.sql. Скрипт работает долго - минут 5.
4. Суть теста: выгрузить на клиента всю таблицу и посчитать сумму колонок А и Б (про агрегатную функцию SUM я знаю - напоминаю,текущий пример призван только для того, чтобы сравнить скорости выборок двумя различными способами).
5. В архиве находится также оконное приложение - писано на Delphi2007, но есть exe, если не боитесь. В приложении есть 2 кнопки. Кнопки подписаны. одна проверят скорость выборки с помощьюwhile not kRS.EOF do
begin
читаем значения через kRS.Fields["A"].Value;
kRS.MoveNext();
end;
другаяkRS.RecordCount
kRS.GetRows(...)
for kRow := VarArrayLowBound(kRows, 2) to VarArrayHighBound(kRows, 2) do
...
ИСХОДНЫЙ КОД
www.vkkb.ru/temp/2010_11_26_242424/ADODB_GetRows.zip
ВОПРОС
Может я что-то не так делаю?
И у меня MoveNext работает медленнее, чем надо?
Николай, просвети.
Мне очень интересно услышать от тебя комментарии, т.к. результаты моих тестов сильно не согласуются с твоим высказыванием в [48].
← →
mem (2010-11-26 12:01) [51]
> sniknik © (23.11.10 21:38) [18]
а можно в двух словах, чем компоненты дельфовые не устраивают?
← →
Дмитрий Тимохов (2010-11-26 12:13) [52]
> mem (26.11.10 12:01) [51]
> а можно в двух словах, чем компоненты дельфовые не устраивают?
Отвечу от себя, хотя и не мне вопрос, но может полезно будет )))
Мне не нравится, что дельфовые компоненты не могут работать нормально с типом TDecimal. Они его преобразуют в Extended.
А тип TDecimal мне нужен, т.к. в MSSQL-Server ему соответствует decimal(28,10)
Справка. Тип TDecimal - это один из вариантных типов, как varInteger, varSingle и т.д. Но он не реализован в Delphi.
Вот комментарий в модуле system.pas (у меня Delphi 2007, в более поздних редакция не знаю)://varDecimal = $000E; { vt_decimal 14 } {UNSUPPORTED as of v6.x code base}
При этом сам дельфи не препятствует работе с varDecimal в части присвоения значения. Если пользоваться импортированной библиотекой ADODB, то varDecimal из БД приходит именно как varDecimal, ты его можешь присвоить переменной типа Variant. Выполнять арифметические операции не можешь, это да. Но я использую импортированные из oleauto32.exe функцииfunction _VarDecAdd(var aDec1: TDecimal; var aDec2: TDecimal;
out aDecResult: TDecimal): HResult;
stdcall; external "oleaut32.dll" name "VarDecAdd";
и прочие.
В quality central"е на сайте Delphi висит уже 10 лет запрос на реализацию TDecimal. Как раз приведен пример недостатков ADODB в работе с этим типом.
Но пока висит и висит этот запрос...
← →
Anatoly Podgoretsky © (2010-11-26 12:18) [53]
> а можно в двух словах, чем компоненты дельфовые не устраивают?
Не вижу ничего подобного в сообщение.
Умение пользоваться СОМ объектом напрямую огромный плюс, поскольку обертки от Борланда страдают ограниченностью, не грех вспомнить пример особой ограниченности FastNet
← →
Anatoly Podgoretsky © (2010-11-26 12:19) [54]> Дмитрий Тимохов (26.11.2010 12:13:52) [52]
Я уже писал, что он у них был в 1993 году, но они его условно похоронили.
← →
Дмитрий Тимохов (2010-11-26 12:22) [55]
> Anatoly Podgoretsky © (26.11.10 12:19) [54]
>
> > Дмитрий Тимохов (26.11.2010 12:13:52) [52]
>
> Я уже писал, что он у них был в 1993 году, но они его условно
> похоронили.
Анатолий, а где писал. Можно подробнее?
Почему похоронили?!?! Прекрасный тип. Очень мне его в дельфи не хватает.
← →
Anatoly Podgoretsky © (2010-11-26 12:22) [56]> Дмитрий Тимохов (26.11.2010 12:13:52) [52]
Кстати еще хуже с типом DECIMAL/NUMERIC(38,x)
← →
Дмитрий Тимохов (2010-11-26 12:24) [57]
> Anatoly Podgoretsky © (26.11.10 12:22) [56]
>
> > Дмитрий Тимохов (26.11.2010 12:13:52) [52]
>
> Кстати еще хуже с типом DECIMAL/NUMERIC(38,x)
именно поэтому пользуемся 28 ))
он 16 байтовый, а 38 вроде нет - не помню уже, 10лет назад это изучал.
← →
Anatoly Podgoretsky © (2010-11-26 12:25) [58]> Дмитрий Тимохов (26.11.2010 12:22:55) [55]
Речь естественно не о TDecimal а об поддержке BCD вообще, получили они его в
наследство с dBase V и успешно его убили уже в первой своей версии dBase 5.5
заменим его эмуляцией через Float
А реализация у Ashton Tate была великолепна, лучше чем у FoxPro Inc
← →
Дмитрий Тимохов (2010-11-26 12:37) [59]
> Anatoly Podgoretsky © (26.11.10 12:25) [58]
>
> > Дмитрий Тимохов (26.11.2010 12:22:55) [55]
>
> Речь естественно не о TDecimal а об поддержке BCD вообще,
Ага, спасыбо. Понял.
Ну я в общем тоже не о TDecimal - т.е. я не натаиваю, чтобы был именно он. Но аналоги бы хотелось иметь. Пока это Currency, но мне мало 4 знаков после запятой.
← →
mem (2010-11-26 12:48) [60]
> Дмитрий Тимохов (26.11.10 12:13) [52]
спасибо
> Anatoly Podgoretsky © (26.11.10 12:18) [53]
> поскольку обертки от Борланда страдают ограниченностью,
вот про это я и спрашивал. Может, еще что? Быстродействие, например)
← →
Anatoly Podgoretsky © (2010-11-26 13:20) [61]> Дмитрий Тимохов (26.11.2010 12:24:57) [57]
38 требует 128 бит
← →
Anatoly Podgoretsky © (2010-11-26 13:21) [62]> Дмитрий Тимохов (26.11.2010 12:37:59) [59]
Сurrency это не BCD - это эмудяция на основе Integer
← →
Суслик_ (2010-11-26 13:34) [63]
> Anatoly Podgoretsky © (26.11.10 13:21) [62]
> > Дмитрий Тимохов (26.11.2010 12:37:59) [59]
> Сurrency это не BCD - это эмудяция на основе Integer
я знаю. я тебе про восприятие программиста - тип с фиксированной запятой. по функционалу Currency именно такой тип, только с 4 знаками после запятой.
я смотрел реализацию Currency по CPU. мне было интересно - Delphi сама реализует арифметические операции из Currency или берет их аналоги их oleauto32.dll по аналогии с TDecimal (в dll и для Currency есть все необходимые функции).
оказалось, что Delphi сами реализуют арифметические операции...
непонятно, кстати, зачем.
← →
Дмитрий Тимохов (2010-11-26 13:35) [64]Блин, Хром меня знает как Суслика.
Виноват.
← →
Anatoly Podgoretsky © (2010-11-26 14:40) [65]> Суслик_ (26.11.2010 13:34:03) [63]
Сама, это написано в документации, а делает потому что не хочет связываться
с OLE это большой провал по производительности, вот решили свой велосипед
сварганить.
← →
app © (2010-11-26 14:41) [66]> Дмитрий Тимохов (26.11.2010 13:35:04) [64]
Прощаю
← →
mem (2010-11-26 14:48) [67]
> app ©
хочу себе фиолетовую с в кружочке
← →
Anatoly Podgoretsky © (2010-11-26 14:50) [68]А дождешься другого :-)
← →
Дмитрий Тимохов (2010-11-26 14:50) [69]
> Anatoly Podgoretsky © (26.11.10 14:40) [65]
> > Суслик_ (26.11.2010 13:34:03) [63]
> Сама, это написано в документации, а делает потому что не
> хочет связываться
> с OLE это большой провал по производительности, вот решили
> свой велосипед сварганить.
TDecimal мне нравится как сделан. Я сомневаюсь, что его можно сильно ускорить.
А в чем провал, если используешь напрямую функцию VarDecAdd из oleaut32.dll - это же просто функция, а не OLE.
Или я не прав? И там на каждый бит создается отдельный COM-объекты? :)
← →
sniknik © (2010-11-26 20:37) [70]> а можно в двух словах, чем компоненты дельфовые не устраивают?
в двух словах - читай тщательнее.
где я чтото подобное говорил?
Дмитрий Тимохов
Ю ИСХОДНЫЙ КОД
> www.vkkb.ru/temp/2010_11_26_242424/ADODB_GetRows.zip
ВОПРОС
> Может я что-то не так делаю?
> И у меня MoveNext работает медленнее, чем надо?
и что ты сравниваешь? ты же не передачу данных сравниваешь, ты работу(свою!!!) с ними сравниваешь. в одном случае выборку по позиционную из структуры (предназначенную не для линейной скорости доступа, а для обработки данных, фильтров например, индексирования на клиенте, ...), во втором пакетную (одной командой) операцию с индексным доступом (по смещению в массиве)...
для тех кто не в курсе аналог - массив обьектов из которых значения достаются по RTTI сравнивается с выборкой из "плоского" массива, в который закачали все данные в обход RTTI каким то методом объекта у которого есть прямой доступ.
в общем как и ожидал никаких чудес, просто неверное понимание.
по тесту -
такая же пакетная передача всего рекордсета в датасет про которую говорил, вида ADODataSet1.Recordset:= kRS; вместо своего цикла занимает 0 сек.
сумма из рекодсета, слегка поправлена (убрана явная подтасовка с поиском по текстовому имени внутри цикла. вынесено за него) - т.е. изменено на этоfA, fB: Field;
...
fA:= kRS.Fields["A"];
fB:= kRS.Fields["B"];
while not kRS.EOF do
begin
kCurrentA := fA.Value;
kCurrentB := fB.Value;
Inc(kSumOfA, kCurrentA);
Inc(kSumOfB, kCurrentB);
kRS.MoveNext();
end;
на 700 000 записей (10 как то вообще не показатель) занимает
AdodbRecordset.MoveNext test: Sum of A=350643419; Sum of B=349693129; Duration=1,9070
ну и с moveto массив + сумирование в нем
AdodbRecordset.GetRows: Sum of A=350643419; Sum of B=349693129; Duration=1,6212
т.е. не такая уж большая разница, на чудо не тянет.
ну а учитывая, что тест надуманный, такое вряд ли кто в здравом уме будет делать (запросом то эти же суммы получаются за 0.0360 сек) то пользы от этого "трюка" 0.
> т.к. результаты моих тестов сильно не согласуются с твоим высказыванием в [48].
по моим высказываниям ты тут сравниваешь 0 с 1,6212 (вернее с 1,4774 на GetRows(kRecordCount без перебора на суммирование). именно столько занимает B оттуда на передачу рекордсета, то что ты придумал еще дополнительно свое C, и сравниваешь с ним - твои проблемы.
← →
Дмитрий Тимохов (2010-11-26 21:07) [71]
> ну а учитывая, что тест надуманный,
да... это аргумент. последний )
я бы признал, что формально ты был не прав: getrows позволяет получить (действительно, это более корректный термин, нежели скачать) быстрее.
называй как хочешь - но если тебе нужны все данные, то выбирать (получать, доставать - как угодно) через getrows быстрее это факт.
мне не понятно - какие тут могут быть сомнения, сам же привел разницу в 15%.
опять же вполне бы мог признать, что для тебя это было удивительно, что getrows работает быстрее, хоть и не так сильно - действительно, не корректный тест я сделал утром с доступом по именам. виноват.
я пример свой переделал на одну колонку - стабильные разницы 10-15% имею.
← →
mem (2010-11-26 21:21) [72]
> sniknik © (26.11.10 20:37) [70]
>
> > а можно в двух словах, чем компоненты дельфовые не устраивают?
>
> в двух словах - читай тщательнее.
> где я чтото подобное говорил?
ну, если чел предпочитает сложный способ простому, а общение с ole db напрямую, по сравнению с компонентами ADO, таковым и является, то можно предположить, что чем-то(я не говорю всем) последние не устраивают. Хорошо, переформулирую вопрос: какие преимущества можно извлечь из непосредственного использования ole db, по сравнению с TADOxxx?
1. Работа с Decimal;
2.?
← →
sniknik © (2010-11-26 21:26) [73]> сам же привел разницу в 15%.
да нет, "моя" разница на 100% у больше. т.к. сравнивать нужно передачу, говорили о ней, а не обработку. а это 0 vs 1,4774.
если ты к примеру в цикл sleep поставишь, то что от этого время передачи увеличится? по твоему выходит так.
> опять же вполне бы мог признать, что для тебя это было удивительно, что getrows работает быстрее
во первых, на php только так и пишем, там все на массивах, рекордсетов нет, во вторых чего удивляться то "move" сработал быстрее, чем "выковыривание" из обьектов... ой ну надо же.
> я пример свой переделал на одну колонку - стабильные разницы 10-15% имею.
а давай переделаем на более реальное, ну например на сортировку, или на суммирование полей B только где A = 0, или поиск в данных... и т.д. т.е. сдвинем тест чуть ближе к тому для чего рекордсеты предназначены.
← →
sniknik © (2010-11-26 21:29) [74]> какие преимущества можно извлечь из непосредственного использования ole db, по сравнению с TADOxxx?
да никаких практически.
> 1. Работа с Decimal;
"варианты" есть и в "обертках", прямой доступ к обьектам, если, что не устраивает тоже.
> 2.?
понты.
← →
Дмитрий Тимохов (2010-11-26 21:32) [75]
> sniknik © (26.11.10 21:26) [73]
Я же не спорю сильно то ))
Просто формально быстрей работает в случаях тупой загрузки. Вообще мне все равно этот пример не подходит (((
У меня TDecimal, а эти типы в массива вариантов не живут. Вот в Variant просто могут жить, а в массиве нет.
Удалю ка я из тракера тикет с задачей по оптимизации посредством GetRows, чтобы не баламутил больше )
← →
sniknik © (2010-11-26 21:41) [76]первый вариант кстати можно ускорить. немного, структура все таки никуда не денется...
добавить вот это перед определением полейkRS:= kRS.Clone(adLockReadOnly);
время на построение обратных связей для полей уберет.
← →
mem (2010-11-26 21:43) [77]
> sniknik © (26.11.10 21:29) [74]
> > 2.?
> понты.
Серьезный довод. Серьезно.
← →
Дмитрий Тимохов (2010-11-26 21:50) [78]
> sniknik © (26.11.10 21:41) [76]
>
> первый вариант кстати можно ускорить. немного, структура
> все таки никуда не денется...
> добавить вот это перед определением полей
> kRS:= kRS.Clone(adLockReadOnly);
> время на построение обратных связей для полей уберет.
знаю, но имхо, мало очень. я не замечаю разницы.
← →
Дмитрий Тимохов (2010-11-26 22:02) [79]
> mem (26.11.10 21:43) [77]
>
> > > 2.?
> > понты.
если взять меня, то я просто не знаю дельфовые компонеты - ни доступа к БД, ни работы с БД в GUI.
так получилось, что когда серьезно базами занялся (до этого на дельфи много писал, но не БД) мне сразу не понарвилось, что нормально в TDecimal нельзя работать, так и не изучил, о чем, кстати, жалею.
← →
sniknik © (2010-11-26 22:12) [80]> я не замечаю разницы.
вот заметная разница, коментариш присвоение для одной из переменных, та и там, например
//kCurrentB := fB.Value;
и
kCurrentB := kRows[1, kRow];
и тест поменяется, примерно так
AdodbRecordset.MoveNext test: Sum of A=350643419; Sum of B=0; Duration=1,4684
AdodbRecordset.GetRows: Sum of A=350643419; Sum of B=0; Duration=1,6076
потому как время передачи никак не поменялось, время "выковыривания" меньше в 2 раза, а именно оно и существенно для рекордсета, и несущественно для взятия из массива.
добавь строк, и ситуация изменится еще более координально.
← →
Дмитрий Тимохов (2010-11-27 00:17) [81]
> sniknik © (26.11.10 22:12) [80]
Предлагаю забить на тему GetRows. Я вижу, что в некоторой придуманной нереальности сферического коня в вакуме GetRows дает прирост выковыривания всех данных. Предлагаю сей факт взять на заметку. И все ))
Я АДО не трогал уже лет 10 (сидит все под "фасадами" из интерфейсов, как-то там работает, ну и ладно). Тут что-то решил поделиться опытом, и заодно тракер посмотрел на предмет замечаний по АДО - вылезли только GetRows и проблема перекомпиляции сохраненной процедуры - почему и решил поделиться workaround"ами.
← →
Дмитрий Тимохов (2010-11-29 11:50) [82]Понимаю, что тема чужая, но т.к. в теме есть знатоки АДО, то задам вопрос тут.
Для обозначения параметра в запросе АДО использует символ вопроса.
Т.е. типа этогоexec MyProc ?, ?
Есть ли возможность использовать именованные параметры, а не символы вопросов?
Например, как-то такexec MyProc {prm1}, {prm2}
← →
sniknik © (2010-11-29 12:17) [83]именованные параметры относятся не к адо, а обертке (т.е vcl), и задают их как обычно не заморачиваясь на конкретику запроса (вот для селекта ты почему то знаешь как, а для exec нет. ну не странно ли?).
← →
Дмитрий Тимохов (2010-11-29 12:24) [84]
> (вот для селекта ты почему то знаешь как, а для exec нет.
> ну не странно ли?).
ничего не понял, если честно.
← →
Anatoly Podgoretsky © (2010-11-29 12:26) [85]> Дмитрий Тимохов (29.11.2010 12:24:24) [84]
А откуда у тебя тот текст взялся?
← →
Ega23 © (2010-11-29 12:34) [86]
> Есть ли возможность использовать именованные параметры,
> а не символы вопросов?
> Например, как-то так
>
> exec MyProc {prm1}, {prm2}
Параметры в VCL и параметры в парсере TSQL - разные вещи.declare
@NSQL nvarchar (1000),
@CLSTableNam varchar (32);
Select @NSQL=Cast(("Select @ObjID=ObjOwner from "+@CLSTableNam+" where ObjID=@ObjID") as nvarchar(1000));
execute sp_executesql @NSQL,
N"@ObjID int output",
@ObjID output;
← →
sniknik © (2010-11-29 12:43) [87]> ничего не понял, если честно.
вырванное из контекста оно не для понимания, оно для "запудривания мозгов отвечающему"...
вот ты благополучно пропустил часть про обертку... а если эксперимент, типа кинуть на форму ADODataSet и попробовать в нем написать запрос (селект) с параметром, в where например... получилось?
← →
Дмитрий Тимохов (2010-11-29 12:43) [88]
> Ega23 © (29.11.10 12:34) [86]
Олегыч, здесь вопрос чисто про синтаксис Adodb.Command - позволяет ли он работать не только с символами вопросов.
Как сейчас сделано я знаю по профайлеру.
Вот, например, вызов процедуры из профайлеру:declare @P1 int
set @P1=-2142644853
exec sp_executesql N"exec [mmk2010_tda].[dbo].[ObjectCreate] @P1, @P2, @P3, @P4 output", N"@P1 smallint,@P2 int,@P3 smallint,@P4 int OUTPUT", -32678, -2147464057, 3, @P1 output
select @P1
← →
Дмитрий Тимохов (2010-11-29 12:47) [89]
> вот ты благополучно пропустил часть про обертку... а если
> эксперимент, типа кинуть на форму ADODataSet и попробовать
> в нем написать запрос (селект) с параметром, в where например.
> .. получилось?
Это же разбираться )
Я штатных компонентов не знаю.
← →
sniknik © (2010-11-29 12:48) [90]ну на нет и суда нет, как в анекдоте, нет ножек - нет варенья.
← →
Дмитрий Тимохов (2010-11-29 12:52) [91]
> sniknik © (29.11.10 12:48) [90]
>
> ну на нет и суда нет, как в анекдоте, нет ножек - нет варенья.
>
ну я же тебя и не про VCL спрашивал вроде, да?
вопрос был про библиотеку АДО, которая умеет работать только с вопросами.
не вижу особой проблемы самому сделать обертку над sp_executesql - парсить текст запроса на предмет именованных параметров и самому их реализовать в тексте.
другой вопрос, что может "все уже украдено до нас" и в adodb.command есть функционал именованных параметров. я есно посмотрел, и не нашел, может я плохо искал.
а VCL если я за 15 лет не выучил в части работы с БД, то и сейчас не буду.
мне задач по изучению более полезных тем хватает.
← →
Ega23 © (2010-11-29 13:00) [92]
> другой вопрос, что может "все уже украдено до нас" и в adodb.
> command есть функционал именованных параметров. я есно посмотрел,
> и не нашел, может я плохо искал.
Насколько я понимаю, от тебя данный вопрос слышать несколько странно, ибо именно ты у нас главный специалист по native-ADO. :)
← →
Дмитрий Тимохов (2010-11-29 13:02) [93]
>
> Насколько я понимаю, от тебя данный вопрос слышать несколько
> странно, ибо именно ты у нас главный специалист по native-
> ADO. :)
я?! да ты что?
Олег, у меня этапами - разобрался в чем-то и забыл - это было в 2000 году последний раз когда я изучал АДО.
просто тут переписываю свой ORM - оптимизирую запросы и задался вопросом, что "вопросы" не есть удобно иногда. может именованные есть? посмотрел, не нашел, спросил... получил ))
← →
Ega23 © (2010-11-29 13:10) [94]
> я?! да ты что?
Дык я сколько тебя помню, ты всегда говорил, что VCL-обёрткой не пользуешься, ибо у тебя где-то там в numeric-полях несоответствие с BCD-шными типами VCL.
ИМХО: вопросы - геморно, много писанины, часто проблемы с рефакторингом. Но идеологически - более правильно.
← →
Anatoly Podgoretsky © (2010-11-29 13:56) [95]> Дмитрий Тимохов (29.11.2010 12:52:31) [91]
ADO может работать с параметрами и с placeholder, параметны начинаются с
символа @. а пласехолдер обозначается вопросом, в ado.vcl это символ
двоеточия.
← →
Дмитрий Тимохов (2010-12-03 09:22) [96]
> Anatoly Podgoretsky © (29.11.10 13:56) [95]
>
> > Дмитрий Тимохов (29.11.2010 12:52:31) [91]
>
> ADO может работать с параметрами и с placeholder, параметны
> начинаются с символа @. а пласехолдер обозначается вопросом, в ado.vcl
> это символ
> двоеточия.
Хочу к этой теме вернуться.
Как работать с плейсхолдером я знаю.
Хочу понять, что ты, Анатолий, сказал про @.
Вот пишу такой код:
procedure TForm1.Button3Click(Sender: TObject);
var
kCnn: _Connection;
kCmd: Command;
kPrm: Parameter;
kRecordsAffected: OleVariant;
kRS: Recordset;
begin
kCnn := CoConnection.Create;
kCnn.CursorLocation := adUseClient;
kCnn.IsolationLevel := adXactReadCommitted;
kCnn.Open(Format("Provider=SQLOLEDB;Server=%s;Database=%s;Trusted_Connection=Yes",
["timdim-w2ks\faust", "adodb_test"]),"","",adConnectUnspecified);
kCmd := CoCommand.Create;
kCmd.Set_CommandText("select * from adodb_test_table where a = @a");
kPrm := CoParameter.Create;
kPrm.Set_Name("a");
//kPrm.Set_Name("@a"); так тоже пробовал
kPrm.Set_Type_(adInteger);
kPrm.Value := 23;
kCmd.Parameters.Append(kPrm);
kCmd.Set_CommandType(adCmdText);
kCmd.Set_ActiveConnection(kCnn);
kRS := kCmd.Execute(kRecordsAffected, EmptyParam, adCmdUnspecified);
end;
При выполнении возникает исключение с текстом сообщения:Must declare the variable "@a"
Что я делаю не так? Или я тебя не так понял и с @ работать не должно?
Тогда, что ты имел в виду?
← →
Ega23 © (2010-12-03 10:17) [97]
> Что я делаю не так? Или я тебя не так понял и с @ работать
> не должно?declare @a int
set @a=1
select * from adodb_test_table where a = @a
Речь о параметрах к ХП шла.
← →
Дмитрий Тимохов (2010-12-03 10:27) [98]
> Речь о параметрах к ХП шла.
это понятно.
вопрос, можно ли в выражении
kCmd := CoCommand.Create;
kCmd.Set_CommandText("select * from adodb_test_table where a = ?");
kPrm := CoParameter.Create;
kPrm.Set_Name("a");
kPrm.Set_Type_(adInteger);
kPrm.Value := 23;
kCmd.Parameters.Append(kPrm);
использовать не символ вопроса, а что-то именованное. Например, какое-то сочетания с a. Например, @a, :a или еще как.
В этом и был у меня вопрос исходный.
← →
Ega23 © (2010-12-03 10:29) [99]Нет. Точнее, можно, но это тебе свою надстройку вертеть придётся, a-la макросы в TRxQuery
← →
Дмитрий Тимохов (2010-12-03 10:31) [100]
> Ega23 © (03.12.10 10:29) [99]
>
> Нет. Точнее, можно, но это тебе свою надстройку вертеть
> придётся, a-la макросы в TRxQuery
собственно все )
надстройка понятно спасет.
думал, что есть.
← →
Ega23 © (2010-12-03 10:33) [101]В AnsiSQL нет, значит и в целом нет.
← →
Дмитрий Тимохов (2010-12-03 11:10) [102]
> В AnsiSQL нет, значит и в целом нет.
А причем тут AnsiSQL? Может я что-то не знаю.
Речь то про конкретный ADODB.Command.
Это же он делает обработку вопросиков.
Страницы: 1 2 3 вся ветка
Форум: "Прочее";
Текущий архив: 2011.03.20;
Скачать: [xml.tar.bz2];
Память: 0.78 MB
Время: 0.008 c