TQuery, синтаксис SQL , глюк или фича?
Такой запрос работает правильно:
MyQuery->SQL->Text="Update buff set idx=idx+1";
MyQuery->ExecSQL();
А вот такой:
MyQuery->SQL->Text="Update buff set idx=idx+:i";
MyQuery->ParamByName("i")->AsFloat=m;
MyQuery->ExecSQL();
помещает в поле idx нули.
Я, конечно, вышел из положения, сформировав строку запроса, не используя ParamByName, но хотелось бы узнать, что это - тонкости синтаксиса(скорее всего) или глюк?
Спасибо.
MyQuery->SQL->Text="Update buff set idx=idx+:i";
MyQuery->ParamByName("i")->AsFloat=m;
MyQuery->ExecSQL();
Ничего крамольного в синтаксисе не вижу. Должно работать. А что за база используется?
Ничего крамольного в синтаксисе не вижу. Должно работать. А что за база используется?
База пока все та же - Paradox (помня нашу с вами дискуссию планирую перейти на InterBase, но это пока планы).
Мне интересно - неужели m в моем примере передается в базу как параметр, а не заменяется значением до того, как запрос будет передан базе?
Видимо так, потому что если формировать запрос следующим образом:
MyQuery->SQL->Text="Update buff set idx=idx+"+IntToStr(m);
все работает.
Мне интересно - неужели m в моем примере передается в базу как параметр, а не заменяется значением до того, как запрос будет передан базе?
Все сервера баз данных делают именно так: SQL передается отдельно, переменные (параметры) - отдельно.
Отделение мух от котлет продиктовано, в первую очередь соображениями производительности. Дело в том, что разбор SQL тоже занимает некоторое время, а после того, как план выполнения запроса построен, он может выполняться какое угодно количество раз. На этом и базируется использование параметров как переменных.
Сказанное справедливо и для выражения UPDATE, поскольку оно обычно содержит WHERE, также разбираемое и выполняемое.
Только вот вопрос реализации для конкретной базы данных - это дело самой базы данных. На серверах обычно проблем с этим не возникает - там просто другого способа манипулирования данными нет. А для BDE - не факт, как может быть реализовано. Может быть, реализация для Paradox и не дает возможность использования параметров при обновлении данных.
Большое спасибо! Очень хорошо объясняете.
Не могли бы вы, если не трудно, в двух словах прояснить еще один маленький вопрос (я не халявщик и честно читаю доки :), просто начинающий пока)?
Есть у меня след. кусок кода:
MyQuery->SQL->Text="Insert into buff select * from tree where tree.idx=:i";
for (i=0;i<SNodes->Count;i++)
{
ttn=(TTreeNode *)SNodes->Items;
if (ttn->getNextSibling()) kk=ttn->getNextSibling()->AbsoluteIndex;
else kk=TreeView1->Items->Count;
for (j=ttn->AbsoluteIndex;j<kk;j++)
{
MyQuery->ParamByName("i")->AsFloat=*(int *)TreeView1->Items->Item[j];
MyQuery->ExecSQL();
cg->Progress=int(++ppp*100.0/ccc);
}
}
Я хотел убыстрить его немного, и прочитав в хелпе Borland'a следующее:
Call Prepare to have the BDE and a remote database server allocate resources for the query and to perform additional optimizations. Calling Prepare before executing a query improves application performance.
C++Builder automatically prepares a query if it is executed without first being prepared. After execution, C++Builder unprepares the query. When a query will be executed a number of times, an application should always explicitly prepare the query to avoid multiple and unnecessary prepares and unprepares.
Preparing a query consumes some database resources, so it is good practice for an application to unprepare a query once it is done using it. The UnPrepare method unprepares a query.
Note: When you change the text of a query at runtime, the query is automatically closed and unprepared.
Вставил после MyQuery->SQL->Text="Insert into buff select * from tree where tree.idx=:i";
строчку MyQuery->Prepare();
Т.к. думал, что на сервере будет построен и оптимизирован мой запрос, а затем будет только меняться параметр и это сэкономит время.
Однако программа начала ругаться - "Invalid Parameter".
Почему? Что неверно? Ведь текст запроса не меняется (по крйней мере я так ваш предыдущий ответ понял)?
Прошу прощения, что вмешиваюсь, но у меня так работает без проблем:
Query2->SQL->Text = "\
Update biolife.db \n\
Set \"biolife.db\".\"length (cm)\" = \"biolife.db\".\"length (cm)\" + :dlt_len \n\
Where \"biolife.db\".\"Species No\" = :pID \n\
";
Query2->Prepare();
Query2->ParamByName("pID")->AsInteger = 90020;
Query2->ParamByName("dlt_len")->AsInteger = 1;
for (int i=0; i < 10; i++) Query2->ExecSQL();
// смотрим результат
if (Query1->Active) Query1->Active = false;
Query1->Active = true;
Дело в том, что у вас один Prepare и одно присвоение параметров. Из того куска хелпа, что я привел, я заключил, что это бессмысленно, т.к. PREPARE вызывается автоматически при первом запуске Query (с конкретным запросом). У меня Query вызывается неоднократно (если один раз вызвать - то все работает с Prepare) - проэтому- то я и надеялся на оптимизацию. Smartsoft писал, что запрос и параметры отправляются на сервер отдельно, что ведет к оптимизации. И это очень логично. Однако, видимо, присвоение параметров BDE рассматривает как изменение текста запроса. (Может быть я и неправ).
for (i=0;i<SNodes->Count;i++)
{
ttn=(TTreeNode *)SNodes->Items;
if (ttn->getNextSibling()) kk=ttn->getNextSibling()->AbsoluteIndex;
else kk=TreeView1->Items->Count;
for (j=ttn->AbsoluteIndex;j<kk;j++)
{
ttn1=(TTreeNode *)TreeView1->Items->Item[j];
MyQuery->ParamByName("i")->AsFloat=*(int *)ttn1->Data;
MyQuery->ExecSQL();
cg->Progress=int(++ppp*100.0/ccc);
Application->ProcessMessages();
}
Однако, непонятки с Prepare остаются.
В таком варианте все продолжает работать и простой просмотр в пошаговом режиме показывает, что Prepare выполняется только 1 раз
Query2->Prepare();
//Query2->ParamByName("pID")->AsInteger = 90020;
Query2->ParamByName("dlt_len")->AsInteger = 1;
for (int i=0; i < 10; i++)
{ Query2->ParamByName("pID")->AsInteger = 90020 + 10 * (i & 1);
Query2->ExecSQL();
}
Если закомментировать Prepare то TQuery.PrepareSQL будет вызываться 10 раз
Параметры дествительно передаются отдельно от запроса (в этом можно убедиться запустив, например, SQL монитор). Иногда это ускоряет, а иногда тормозит. Другое дело, что Paradox это не SQL'евский сервер и я очень сомневаюсь, что он что то оптимизирует.