laifik |
Отправлено: 25.01.2005, 09:17 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 70
|
Есть стандартный пример:
CODE | void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (DBGrid1->SelectedRows->Count > 0)
{
AnsiString s = "";
TDataSet *pDS = DBGrid1->DataSource->DataSet;
for (int i=0; i < DBGrid1->SelectedRows->Count; i++)
{
pDS->GotoBookmark((void *)DBGrid1->SelectedRows->Items[i].c_str());
for (int j = 0; j < pDS->FieldCount; j++)
{
if (j>0)
s = s+", ";
s = s + pDS->Fields->Fields[j]->AsString;
}
ListBox1->Items->Add(s);
s = "";
}
}
}
|
Вместо добавления данных в ЛистБокс их по-строчно нужно добавить в таблицу БД. Как это сделать, подскажите? У меня копируется только последняя строка. |
|
full_lamer |
Отправлено: 25.01.2005, 09:38 |
|
Машинист паровоза
Группа: Участник
Сообщений: 225
|
а сам DataSet ни с какой таблицей БД не связан?
если связан можно непосредственным запросом из одной таблицы в другую... да кстати какая СУБД... возможно Вы забываете сохранять изменения.
Отредактировано full_lamer — 25/01/2005, 10:44
|
|
AVC |
Отправлено: 25.01.2005, 09:51 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
Если вы заменили строку ListBox1-Items-Add(s) то все закономерно.
Нужно изменить эту часть
CODE |
for (int i=0; i < DBGrid1->SelectedRows->Count; i++)
{
pDS->GotoBookmark((void *)DBGrid1->SelectedRows->Items[i].c_str());
for (int j = 0; j < pDS->FieldCount; j++)
{
if (j>0)
s = s+", ";
s = s + pDS->Fields->Fields[j]->AsString;
}
ListBox1->Items->Add(s);
s = "";
}
|
Примерно так
CODE |
for (int i=0; i < DBGrid1->SelectedRows->Count; i++)
{
pDS->GotoBookmark((void *)DBGrid1->SelectedRows->Items[i].c_str());
Добавить_запись_в_таблицу_приемник
}
|
Коды для "Добавить_запись_в_таблицу_приемник" зависят от СУБД.
Для SQL это генерация запроса Insert;
Для файловой БД и работе через Table это Append в DataSet приемника;
Подробности нужны? Тогда для какого варианта.
Отредактировано AVC — 25/01/2005, 09:55 |
|
laifik |
Отправлено: 25.01.2005, 10:02 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 70
|
QUOTE | Для SQL это генерация запроса Insert;
|
База SQL, доступ ADO. Вставить и записать не проблема. Не могу додумать код вместо QUOTE | Добавить_запись_в_таблицу_приемник | .
CODE | ADOQuery1->Fields->Fields[j]->AsString=??? | |
|
laifik |
Отправлено: 25.01.2005, 10:03 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 70
|
QUOTE (full_lamer @ 25/01/2005, 10:40) | а сам DataSet ни с какой таблицей БД не связан?
если связан можно непосредственным запросом из одной таблицы в другую... да кстати какая СУБД... возможно Вы забываете сохранять изменения. |
Этот вариант не подойдет, т.к. копируется не вся DataSet , а строки, выделенные в таблице Грид. |
|
full_lamer |
Отправлено: 25.01.2005, 10:17 |
|
Машинист паровоза
Группа: Участник
Сообщений: 225
|
CODE | ADOQuery1->Fields->Fields[j]->AsString=pDS->Fields->Fields[j]->AsString; | по-моему так
|
|
laifik |
Отправлено: 25.01.2005, 10:42 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 70
|
Да, это строчка правильная. Но мне не организовать цикл.
CODE | if (DBGridEh2->SelectedRows->Count > 0)
{
AnsiString s = "";
TDataSet *pDS = DBGridEh2->DataSource->DataSet;
for (int i=0; i < DBGridEh2->SelectedRows->Count; i++)
{
pDS->GotoBookmark((void *)DBGridEh2->SelectedRows->Items[i].c_str());
ModuleLoad->ADOBookmark->Edit();
for (int j = 0; j < pDS->FieldCount; j++)
{
ModuleLoad->ADOBookmark->Fields->Fields[j]->AsString=pDS->Fields->Fields[j]->AsString;;
}
ModuleLoad->ADOBookmark->Post();
}
} |
Не вставляет. Если убрать цикл, то вставляется одно значение из первого столбца. Если можно, поправьте этот код, чтобы он заработал. |
|
AVC |
Отправлено: 25.01.2005, 10:45 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
Добавить_запись_в_таблицу_приемник
Вариант 1
CODE |
перед циклом
TDataSet *gds = DBGrid1->DataSource->DataSet;
AnsiString sqltext = "Insert into ваша_таблица (";
AnsiString str = "" values (";
for (int i = 0; i < gds->FieldCount; i++)
{ if (i > 0) { sqltext += ", "; str += ", "; }
sqltext += gds->Fields->Fields[i]->FieldName;
str += ":" + gds->Fields->Fields[i]->FieldName;
добавить параметр с именем gds->Fields->Fields[i]->FieldName в компонент, через который будут проводиться вставки
}
sqltext += ")" + str + ")";
в цикле просмотра маркированных записей
циклом по всем полям заполнить параметры запроса
|
Вариант 2
вместо параметрического запроса генерировать текст Insert. Подводные камни — нужен анализ символов на совместимость с SQL
Вариант 3
Если какое-либо поле основной таблицы можно использовать как ключевое сгенерировать запрос вида
Insert into новая_таблица Select * From старая_таблица Where первичный_ключ in (value1, value2, ..., valueN)
т.е. для отобранных записей сгенерировать строку "значение_PK1, значение_PK2, ..., значение_PKN" и добавить в текст запроса
подводные камни:
для текстовых полей нужен анализ символов
может встретиться ограничение на число значений в строке in (для Oracle 1000) — нужно будет делать несколько раз, но это легко. |
|
AVC |
Отправлено: 25.01.2005, 10:47 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
QUOTE |
ModuleLoad->ADOBookmark->Edit();
|
Почему Edit а не Insert? |
|
laifik |
Отправлено: 25.01.2005, 11:55 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 70
|
Если честно, то я уже сдаюсь. Столько вариантов перебрала, но ни..че..го не получается. Последний код вообще не поняла. Не думала, что пойдут такие сложности.
Очень прошу, если возможно, скинте готовый код. Уже устала так, что наверное очевидного не понимаю. |
|
avc* |
Отправлено: 25.01.2005, 12:55 |
|
Не зарегистрирован
|
примерно так (вариант через Table)
CODE |
TDataSet *newds = открытая и модифицируемая таблица_приемник
TDataSte *gds = DBGrid1->DataSource->DataSet;
TField *fld;
for (int i=0; i < DBGrid1->SelectedRows->Count; i++)
{
gds->GotoBookmark((void *)DBGrid1->SelectedRows->Items[i].c_str());
newds->Insert();
for (int j = 0; j < gds->FieldCount; j++)
{ // поля в gds и newds сопоставляются друг другу по именам
fld = newds->FindField(gds->Fields->Fields[i]->FieldName);
// в таблице приемнике есть такое поле
if (fld) fld->Value = gds->Fields->Fields[i]->Value;
}
newds->Post();
}
|
|
|
laifik |
Отправлено: 25.01.2005, 15:06 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 70
|
ри запуске кода выдается сообщение:
"Не удается вставить пустую строку. Требуется хотя бы один столбец значений".
Как это исправить? |
|
avc* |
Отправлено: 25.01.2005, 15:18 |
|
Не зарегистрирован
|
А если заменить Insert на Append?
А может у вас ругается на Post? Если нет полей с одинаковыми именами добавляем пустую запись.
А может у вас где-то стоят ограничения на новые строки? |
|
laifik |
Отправлено: 25.01.2005, 15:59 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 70
|
Вот весь мой код: (половина Вашего)
CODE | String TableName = "Load_Bookmark";
String newSqlTable ="CREATE TABLE dbo."+TableName+" (CardID int PRIMARY KEY CLUSTERED IDENTITY(1, 1) NOT NULL, KodSyst int,";
String newSqlTable1 = "GostID char (14), Project_Def char (10), NameRD char (250), Gruppa char (10), Nomer char (6),";
String newSqlTable2 = "Sapis int, Massa real, PlechoX real, PlechoY real, PlechoZ real,";
String newSqlTable3 = "Department char (3), DataSapoln datetime, Prim char (400), Slujebn ntext,";
//String newSqlTable4 = "dmhP real, Prozent real, Developer char (20), Mx_R float, My_R float, Mz_R float, ImageArh int)";
String newSqlTable4 = "dmhP real, Prozent real, Developer char (20))";
ModuleLoad->ADOCreateUniversal->SQL->Clear();
ModuleLoad->ADOCreateUniversal->SQL->Add(newSqlTable);
ModuleLoad->ADOCreateUniversal->SQL->Add(newSqlTable1);
ModuleLoad->ADOCreateUniversal->SQL->Add(newSqlTable2);
ModuleLoad->ADOCreateUniversal->SQL->Add(newSqlTable3);
ModuleLoad->ADOCreateUniversal->SQL->Add(newSqlTable4);
ModuleLoad->ADOCreateUniversal->ExecSQL();
ModuleLoad->ADOCreateUniversal->Close();
ModuleLoad->ADOBookmark->SQL->Clear();
ModuleLoad->ADOBookmark->SQL->Add("SELECT * FROM dbo.Load_Bookmark");
ModuleLoad->ADOBookmark->Open();
TDataSet *newds = ModuleLoad->ADOBookmark;
TDataSet *gds = DBGridEh2->DataSource->DataSet;
TField *fld;
for (int i=0; i < DBGridEh2->SelectedRows->Count; i++)
{
gds->GotoBookmark((void *)DBGridEh2->SelectedRows->Items[i].c_str());
newds->Append();
for (int j = 0; j < gds->FieldCount; j++)
{
fld = newds->FindField(gds->Fields->Fields[i]->FieldName);
if (fld) fld->Value = gds->Fields->Fields[i]->Value;
}
newds->Post();
} |
Так, ограничения нигде не стоит. Может открытие запроса ADOBookmark не на месте?
Отредактировано laifik — 25/01/2005, 17:01 |
|
avc* |
Отправлено: 25.01.2005, 16:08 |
|
Не зарегистрирован
|
Сейчас попробую сымитировать у себя. И все таки о какой СУБД идет речь? |
|
laifik |
Отправлено: 25.01.2005, 16:30 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 70
|
QUOTE (avc* @ 25/01/2005, 17:10) | И все таки о какой СУБД идет речь? |
SQL — база |
|
AVC |
Отправлено: 25.01.2005, 18:22 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
SQL какая база? Ms..., My... или что то другое. Ладно. Это не важно.
У меня то-же с Insert/Append какая то ерунда творится — вызов метода разрушает программу.
Если терпит — или разберусь или перепишу на команду SQL, но это уже завтра. |
|
** laifik |
Отправлено: 25.01.2005, 19:46 |
|
Не зарегистрирован
|
My SQL. До завтра подожду. Все равно сегодня нужно заканчивать Зациклилась.
Мне очень нужен этот код. По всему интернету и намека нет. А уж в дибильных книгах, которые у меня имеются типа Архангельского, такие решения задач — роскошь. Поэтому, очень благодарна, что Вы помогаете. |
|
AVC |
Отправлено: 26.01.2005, 09:54 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
Есть хорошие новости — формальная замена ADOQuery на ADOTable устраняет проблему Insert и дальше все работает нормально.
Сделаю вариант с параметрическим запросом — тогда выложу тест.
PS. Из за снегопада у провайдера рвется связь — могу пропасть надолго. |
|
AVC |
Отправлено: 26.01.2005, 14:06 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
Продолжение.
Пока отлаживал запрос вариант с ADOTable тоже перестал работать. Вызов метода Insert снова приводит к краху приложения.
Вариант с параметризованным запросом пока сбить не удалось.
Обещанный код:
На форме две сетки, DataSour'цы, ADOConnection, ADOCommand и кнопки
Первая сетка — источник данных. Разрешен multiselect. Связана с ADOQuery1 (Select * From OldTable)
Вторая сетка — контроль приемника. Связана с ADOQuery2 (Select * From NewTable) или с ADOTable2 (NewTable)
ADOConnection — соединение через ODBC с сервером Sybase ASA6
ADOCommand2 — для выполнения команд SQL (можно заменить на ADOQuery)
кнопки "Открыть" — открывает/переоткрывает запросы
и "Копировать" — поместить помеченные записи из OldTable в NewTable
CODE |
//---------------------------------------------------------------------------
/* для создания рабочей таблицы был выполнен такой скрипт
--Drop table AVC.OldTable;
--go
Create table AVC.OldTable
(oldtableID integer primary key
,oldtable_Name varchar(40)
,price double
,massa double
);
go
Delete From AVC.OldTable;
go
Insert Into AVC.OldTable
Select
table_id
,Table_name
,Round((count / 100.0), 2) as price
,(last_page — first_page + 1.0) as massa
From sys.SysTable
Where count > 0;
go
*/
//---------------------------------------------------------------------------
void __fastcall TForm1::AxADOClose (TDataSet *ds)
{
if (!ds) return;
if (ds->Active)
{ try { ds->Active = false; }
catch(...) {; }
}
}
//---------------------------------------------------------------------------
// Refresh запросов
//---------------------------------------------------------------------------
void __fastcall TForm1::Bt_OpenClick(TObject *Sender)
{
TADOQuery *ds = dynamic_cast<TADOQuery*>(DBGrid1->DataSource->DataSet);
AxADOClose(ds);
ds->SQL->Text = "Select * From AVC.OldTable";
if (!ADOConnection1->Connected) ADOConnection1->Connected = true;
ds->Active = true;
ds = ADOQuery2;
AxADOClose(ds);
ds->SQL->Text = "Select * From AVC.NewTable";
ds->Active = true;
AxADOClose(ADOTable2);
ADOTable2->Active = true;
}
//---------------------------------------------------------------------------
// вариант вставки отобранных записей
// через параметризованный запрос
// (текст запроса готовит функция CreateNewTable)
//---------------------------------------------------------------------------
void __fastcall TForm1::Bt_CloneClick(TObject *Sender)
{
if (DBGrid1->SelectedRows->Count == 0) return;
CreateNewTable();
TDataSet *gds = DBGrid1->DataSource->DataSet;
TADOCommand *cmd = ADOCommand2;
AxADOClose(DBGrid2->DataSource->DataSet);
TField *fld;
TParameter *prm;
bool needcmd;
bool isinserting = false;
for (int i=0; i < DBGrid1->SelectedRows->Count; i++)
{ gds->GotoBookmark((void *)DBGrid1->SelectedRows->Items[i].c_str());
needcmd = false;
for (int j = 0; j < gds->FieldCount; j++)
{ fld = gds->Fields->Fields[j];
prm = cmd->Parameters->FindParam(fld->FieldName);
if (prm)
{ prm->Value = fld->Value;
needcmd = true;
}
} // for j < gds->FieldCount
if (needcmd)
{ cmd->Execute();
isinserting = true;
}
} // for i < DBGrid1->SelectedRows->Count
if (isinserting)
{ cmd->CommandText = "Commit"; // по желанию
cmd->Execute();
}
DBGrid2->DataSource->DataSet->Active = true;
}
//---------------------------------------------------------------------------
// Создать новую таблицу и подготовить запрос для вставки
// ParamCheck должно быть true
//---------------------------------------------------------------------------
void __fastcall TForm1::CreateNewTable (void)
{
if (DBGrid1->SelectedRows->Count == 0) return;
AxADOClose(ADOQuery2);
AxADOClose(ADOTable2);
TADOCommand *cmd = ADOCommand2;
cmd->CommandText = "Drop table AVC.NewTable";
try { cmd->Execute(); }
catch(...) {; }
//ShowMessage("after drop table");
cmd->CommandText = AnsiString("") +"\
Create table AVC.NewTable \n\
(oldtableID integer \n\
,oldtable_Name varchar(40) \n\
,price double \n\
--,massa double \n\
); \n\
";
cmd->Execute();
//ShowMessage("after create table");
cmd->CommandText = AnsiString("") +"\
Insert Into AVC.NewTable (oldtableID, oldtable_Name, price) \n\
Values (:oldtableID, :oldtable_Name, :price) \n\
";
ADOQuery2->SQL->Text = "Select * From AVC.NewTable";
ADOTable2->TableName = "NewTable";
}
//---------------------------------------------------------------------------
| |
|
** laifik |
Отправлено: 26.01.2005, 16:16 |
|
Не зарегистрирован
|
Ничего себе оказался вопросик. Пока рыскала по интернету в поисках решения, думала, что, наверное, все так просто, что нигде нет ответа. Все все знают, кроме меня. Сейчас мне не стыдно.
AVC, огромное спасибо за помощь. Это надо переварить и, конечно же, повторить. Буду пробовать. А вообще, решение не из легких... |
|
xim |
Отправлено: 27.01.2005, 10:53 |
|
Станционный диспетчер
Группа: Участник
Сообщений: 143
|
Мне кажется, что
CODE |
CardID int PRIMARY KEY CLUSTERED IDENTITY(1, 1) NOT NULL
|
не стоит заполнять в цикле CODE | for (int j = 0; j < gds->FieldCount; j++)...
|
Хотя, зачем нужен этот цикл???
CODE | //тоже без CardID — identity все-таки
ado_command->CommandText="insert into dbo.Load_Bookmark(KodSyst,GostID,Project_Def,NameRD,Gruppa,Nomer,Sapis,Massa,PlechoX,PlechoY,PlechoZ,Department,DataSapoln,Prim,Slujebn) values(:KodSyst,:GostID,:Project_Def,:NameRD,:Gruppa,:Nomer,:Sapis,:Massa,:PlechoX,:PlechoY,:PlechoZ,:Department,:DataSapoln,:Prim,:Slujebn)";
ado_command->Prepare();
for (int i=0; i < DBGridEh2->SelectedRows->Count; i++)
{
gds->GotoBookmark((void *)DBGridEh2->SelectedRows->Items[i].c_str());
TParameter *prm=ado_command->Parameters->FindParam(WideString(gds->Fields->Fields[i]->FieldName));
if(prm&&!VarIsNull(gds->Fields->Fields[i]->Value))prm->Value=gds->Fields->Fields[i]->Value;
ado_command->ExecSQL();
}
|
|
|
xim |
Отправлено: 27.01.2005, 11:01 |
|
Станционный диспетчер
Группа: Участник
Сообщений: 143
|
Прошу прощения — накосячил выше (цикл нужен):
CODE | //тоже без CardID — identity все-таки
ado_command->CommandText="insert into dbo.Load_Bookmark(KodSyst,GostID,Project_Def,NameRD,Gruppa,Nomer,Sapis,Massa,PlechoX,PlechoY,PlechoZ,Department,DataSapoln,Prim,Slujebn) values(:KodSyst,:GostID,:Project_Def,:NameRD,:Gruppa,:Nomer,:Sapis,:Massa,:PlechoX,:PlechoY,:PlechoZ,:Department,:DataSapoln,:Prim,:Slujebn)";
ado_command->Prepare();
for (int i=0; i < DBGridEh2->SelectedRows->Count; i++)
{
gds->GotoBookmark((void *)DBGridEh2->SelectedRows->Items[i].c_str());
for (int j = 0; j < gds->FieldCount; j++)
{
TParameter *prm=ado_command->Parameters->FindParam(WideString(gds->Fields->Fields[j]->FieldName));
if(prm&&!VarIsNull(gds->Fields->Fields[j]->Value))prm->Value=gds->Fields->Fields[i]->Value;
}
ado_command->ExecSQL();
}
| |
|
AVC |
Отправлено: 27.01.2005, 13:38 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
Вот в дополнение к первому варианту еще два: Insert'ом и при помощи поля, обладающего свойствами превичного ключа (мой любимый).
CODE |
//---------------------------------------------------------------------------
// вариант вставки отобранных записей при помощи параметризованного запроса
//---------------------------------------------------------------------------
void __fastcall TForm1::Bt_ClonePSQLClick(TObject *Sender)
{
// есть что копировать ?
if (DBGrid1->SelectedRows->Count == 0) return;
TDataSet *gds = DBGrid1->DataSource->DataSet; // источник записей
TADOCommand *cmd = ADOCommand2; // для команд SQL
// Создать таблицу приемник
CreateNewTable();
// Подготовить текст запроса
cmd->ParamCheck = true;
cmd->Prepared = false;
cmd->CommandText = AnsiString("") +"\
Insert Into NewTable (oldtableID, oldtable_Name, price) \n\
Values (:oldtableID, :oldtable_Name, :price) \n\
";
TField *fld;
TParameter *prm;
bool needcmd;
bool isinserting = false;
// для всех помеченных записей
for (int i=0; i < DBGrid1->SelectedRows->Count; i++)
{ gds->GotoBookmark((void *)DBGrid1->SelectedRows->Items[i].c_str());
needcmd = false;
// просмотреть все поля источника
for (int j = 0; j < gds->FieldCount; j++)
{ fld = gds->Fields->Fields[j];
prm = cmd->Parameters->FindParam(fld->FieldName);
// если имя поля источника совпадает с именем параметра — задать значение параметра
if (prm)
{ prm->Value = fld->Value;
needcmd = true;
}
} // for j < gds->FieldCount
if (needcmd) // были заполнены парамтры — нужно выполнить запрос Insert
{ cmd->Execute();
isinserting = true;
}
} // for i < DBGrid1->SelectedRows->Count
// была хотя бы одна вставка — подтвердить транзакцию
// (зависит от используемой системы транзакций)
//if (isinserting)
// { cmd->CommandText = "Commit";
// cmd->Execute();
// }
// посмотреть резудьтат в сетке 2
DBGrid2->DataSource->DataSet = ADOTable2;
DBGrid2->DataSource->DataSet->Active = true;
}
//---------------------------------------------------------------------------
// вариант вставки отобранных записей использованием метода DataSet->Insert
// (у меня при работе с ADO вызов ADOTable(ADOQuery)->Isert() иногда разрушает приложение
// подозреваю, что это связано с заблокированной ошибкой при закрытии пустого dataset)
//---------------------------------------------------------------------------
void __fastcall TForm1::Bt_CloneInsertClick(TObject *Sender)
{
// есть что копировать ?
if (DBGrid1->SelectedRows->Count == 0) return;
// Создать таблицу приемник
CreateNewTable();
TDataSet *gds = DBGrid1->DataSource->DataSet; // источник записей
TDataSet *tds = ADOTable2; // приемник записей
DBGrid2->DataSource->DataSet = tds; // для наблюдения за работой
// активизировать приемник
tds->Active = true;
TField *ofld; // поле источника
TField *nfld; // поле приемника
bool needpost;
// для всех помеченных записей
for (int i=0; i < DBGrid1->SelectedRows->Count; i++)
{ gds->GotoBookmark((void *)DBGrid1->SelectedRows->Items[i].c_str());
tds->Insert();
needpost = false;
// просмотреть все поля источника
for (int j = 0; j < gds->FieldCount; j++)
{ ofld = gds->Fields->Fields[j];
// если имя поля источника совпадает с именем поля приемника -
// заполнить значение у приемника
nfld = tds->FindField(ofld->FieldName);
if (nfld)
{ nfld->Value = ofld->Value;
needpost = true;
}
} // for j < gds->FieldCount
if (needpost) tds->Post(); // сохранить вставку
else tds->Cancel(); // отменить вставку
} // for i < DBGrid1->SelectedRows->Count
}
//---------------------------------------------------------------------------
// вариант вставки отобранных записей с использованиием поля,
// обладающего характеристиками первичного ключа
// запросом типа Insert into NewTable Select ... From OldTable Where PK in (...)
// Для простоты кода предпологаю что список значений PK не выйдет
// за возможности сервера. Если это не так переделка для подсчета
// числа вставляемых записей и выполнение запроса при достижении предела
// очень проста.
//---------------------------------------------------------------------------
void __fastcall TForm1::Bt_ClonePKClick(TObject *Sender)
{
// есть что копировать ?
if (DBGrid1->SelectedRows->Count == 0) return;
// Создать таблицу приемник
CreateNewTable();
TDataSet *gds = DBGrid1->DataSource->DataSet; // источник записей
// поле OldTableID удовлетворяет моим требованиям и имеет тип "целое"
TField *fld = gds->FindField("OldTableID");
if (!fld) return; // такого поля нет (почему бы?)
AnsiString sqltext;
// для всех помеченных записей
for (int i=0; i < DBGrid1->SelectedRows->Count; i++)
{ gds->GotoBookmark((void *)DBGrid1->SelectedRows->Items[i].c_str());
if (i != 0) sqltext += ",";
sqltext += fld->AsString;
} // for i < DBGrid1->SelectedRows->Count
TADOCommand *cmd = ADOCommand2;
cmd->ParamCheck = true;
cmd->Prepared = false;
cmd->CommandText = AnsiString("") +"\
Insert Into NewTable \n\
Select OldTableID, OldTable_Name, Price \n\
From OldTable \n\
Where OldTableID in (" + sqltext + ") \n\
";
cmd->Execute();
// посмотреть резудьтат в сетке 2
DBGrid2->DataSource->DataSet = ADOTable2;
DBGrid2->DataSource->DataSet->Active = true;
}
//---------------------------------------------------------------------------
|
PS.
Insert стабильно работает если не зарывать пустую таблицу. (Подозреваю, что мне нужно поискать обновления для ADO).
Вариант c PK как хотел xim без второго цикла
Полный работающий текст примера для MSAcces можно взять здесь
|
|
** laifik |
Отправлено: 27.01.2005, 16:43 |
|
Не зарегистрирован
|
Есть еще один работающий вариант:
CODE | String TableName = "Load_Bookmark"; //Запрос, в который копируются данные
String newSqlTable ="CREATE TABLE dbo."+TableName+" (CardID int, KodSyst int,";
String newSqlTable1 = "GostID char (14), Project_Def char (10), NameRD char (250), Gruppa char (10), Nomer char (6),";
String newSqlTable2 = "Sapis int, Massa float, PlechoX real, PlechoY real, PlechoZ real,";
String newSqlTable3 = "Department char (3), DataSapoln datetime, Prim char (400), Slujebn ntext,";
String newSqlTable4 = "dmhP real, Prozent real, Developer char (20), Mx_R float, My_R float, Mz_R float, ImageArh int)";
ADOBookmark->SQL->Clear();
ADOBookmark->SQL->Add(newSqlTable);
ADOBookmark->SQL->Add(newSqlTable1);
ADOBookmark->SQL->Add(newSqlTable2);
ADOBookmark->SQL->Add(newSqlTable3);
ADOBookmark->SQL->Add(newSqlTable4);
ADOBookmark->ExecSQL();
ADOBookmark->Close();
if (DBGridEh2->SelectedRows->Count > 0)
{
AnsiString Misc="",s = "";
TDataSet *pDS = DBGridEh2->DataSource->DataSet;
for (int i=0; i < DBGridEh2->SelectedRows->Count; i++)
{
pDS->GotoBookmark((void *)DBGridEh2->SelectedRows->Items[i].c_str());
for(int j=0;j<pDS->FieldCount;j++)
{TField* Fl=pDS->Fields->Fields[j];
// если строго, то нужно делать проверку на тип поля
if (j)
{
Misc=Misc+","+Fl->FieldName;
s =s+",:P"+Fl->FieldName;//P на всякий случай, чтобы отличались от поля
}
else
{
Misc="insert into dbo.Load_Bookmark ("+Fl->FieldName;
s ="values (:P"+Fl->FieldName;//P на всякий случай, чтобы отличались
}
}
Misc=Misc+")";
s=s+")";
ADOBookmark->SQL->Clear();
ADOBookmark->SQL->Add(Misc);
ADOBookmark->SQL->Add(s);
ADOBookmark->Parameters->ParseSQL(ADOBookmark->SQL->Text,true);
for(int k=0;k<pDS->FieldCount;k++)
{TField* FP=pDS->Fields->Fields[k];
ADOBookmark->Parameters->Items[k]->Value=FP->Value;
ShowMessage(FP->Value);
}
ADOBookmark->ExecSQL();
}
} |
Этот код работает только для полей типа int и char. Как только появляется в исходной таблице отя бы одно поле real, float или datetime, вставка не осуществляется. Выдается сообщение QUOTE | [ODBC SQL Server Driever] Дополнительная возможность не реализована. | . Мне думается, что параметр для числового поля должен быть в формате "0.00", а он передается с запятой как "0,00". Почему не вставляется формат даты, объяснений найти не могу. И еще, копирования не происходит, если в каком-то поле нет значения (оно не обязательно для заполнения). Кто-нибудь может помочь исправить код? |
|
AVC |
Отправлено: 27.01.2005, 17:24 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
QUOTE |
for(int j=0; j < pDS->FieldCount; j++)
{TField* Fl=pDS->Fields->Fields[j];
// если строго, то нужно делать проверку на тип поля
if (j)
{
Misc=Misc+","+Fl->FieldName;
s =s+",:P"+Fl->FieldName; //P на всякий случай, чтобы отличались от поля
}
else
{
Misc="insert into dbo.Load_Bookmark ("+Fl->FieldName;
s ="values (:P"+Fl->FieldName;//P на всякий случай, чтобы отличались
}
}
Misc=Misc+")";
s=s+")";
ADOBookmark->SQL->Clear();
ADOBookmark->SQL->Add(Misc);
ADOBookmark->SQL->Add(s);
ADOBookmark->Parameters->ParseSQL(ADOBookmark->SQL->Text,true);
for(int k=0;kFieldCount;k++)
{TField* FP=pDS->Fields->Fields[k];
ADOBookmark->Parameters->Items[k]->Value=FP->Value;
ShowMessage(FP->Value);
}
ADOBookmark->ExecSQL();
|
Не понял. При чем тут зависимость от типа поля. Вы в каждой итерации цикла строите параметризованный запрос заново (у меня это сделано один раз вне цикла). Этого делать незачем, так как структура записей набора записей неизменна. А пока вы работаете через variant (value) то тип поля вас заботить не должен. Это головная боль серверы и драйвера.
Эще один вариант я вам приводил в одном из ранних своих постов
>>Вариант ...
>>вместо параметрического запроса генерировать текст Insert.
>>Подводные камни — нужен анализ символов на совместимость с SQL
Помните?
Он работает практически для всех простых типов.
Его удобнее реализовывать так
вне цикла
определяем постоянную часть запроса
делам список нужных полей в нужном порядке
в цикле по помеченным записям (по желанию)
.для всех полей из списка (из набора источника)
..AsSting,
..если нужно проверка на соответствие синтаксису,
..добавление полученной строки к переменной части запроса
.выполнение insert'а
Именно из-за нежелания останавливаться на синтаксическом разборе,
сильно зависящем от правил сервера я не стал включать этот вариант
в свой пример.
Отредактировано AVC — 27/01/2005, 17:38 |
|
AVC |
Отправлено: 27.01.2005, 18:05 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
Чисто формально так должно работать.
все ошибки кроме синтаксических лежат на совести сервера
если совсем ни чего не помогает попробуйте через TADOTable
- у меня работает стабильно (впрочем как и все прочие варианты)
CODE |
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
// дабы не плодить пустых таблиц лучше проверять тут
if (DBGridEh2->SelectedRows->Count > 0) return;
TDataSet *pDS = DBGridEh2->DataSource->DataSet;
AnsiString TableName = "Load_Bookmark";
TADOQuery *qry = ADOBookmark;
qry->ParamCheck = true;
qry->Prepared = false;
qry->SQL->Text = "CREATE TABLE dbo." + TableName + " (CardID int, KodSyst int, \
GostID char (14), Project_Def char (10), NameRD char (250), Gruppa char (10), Nomer char (6) \
Sapis int, Massa float, PlechoX real, PlechoY real, PlechoZ real \
Department char (3), DataSapoln datetime, Prim char (400), Slujebn ntext \
dmhP real, Prozent real, Developer char (20), Mx_R float, My_R float, Mz_R float, ImageArh int";
qry->ExecSQL();
{ // построить запрос
AnsiString str1 = "Insert Into dbo. + TableName (";
AnsiString str2 = "Values (";
for(int j=0; j < pDS->FieldCount; j++ )
{ if (j) { str1 += ","; str2 += ","; }
str1 += pDS->Fields->Fields[j]->FieldName;
str2 += ":" + pDS->Fields->Fields[j]->FieldName; // не зачем отличаться
}
qry->SQL->Text = str1 + ") " + str2 + ")";
}
// пройтись по bookmark и выполнить запрос
// цикл заполнения параметров
// должен быть эквивалентен
// цикл генерации запрса
for (int i=0; i < DBGridEh2->SelectedRows->Count; i++)
{ pDS->GotoBookmark((void *)DBGridEh2->SelectedRows->Items[i].c_str());
for(int k=0; k < pDS->FieldCount; k++)
qry->Parameters->Items[k]->Value = pDS->Fields->Fields[k]->Value;
qry->ExecSQL();
}
}
|
|
|
** laifik |
Отправлено: 27.01.2005, 20:00 |
|
Не зарегистрирован
|
Последний вариант у меня почему-то не создает таблицу. Нужно внимательно посмотреть. Уже не сегодня.
Предпоследний вариант (laif.rar) интересен. У меня работает, если подключена база laif.mdb. Уже несколько часов пытаюсь подключить базу My SQL. Для этого импортировала 2 таблицы из Вашей базы в свою, сделала подключение к базе через ODBC и запустила. При действии кнопки Open/Refresh при компеляции выдается сообщение, что не может конвентировать Null в тип String. Если запустить просто exe-шник, база подключается. Но тогда не выполняется копирование методами. При нажатии на эти кнопки при этом удаляется таблица NewTable. В варианте с mdb этого не происходит.
Как зависит выполнение этого кода от типа базы? |
|
laifik |
Отправлено: 28.01.2005, 09:26 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 70
|
Вчера до поздна крутила последний вариант.
Если CODE | if (DBGridEh2->SelectedRows->Count > 0) return; |
то таблица не создается. Наверное, это так нужно, потому что копирование не происходит.
На куске
CODE | for (int i=0; i < DBGridEh2->SelectedRows->Count; i++)
{
pDS->GotoBookmark((void *)DBGridEh2->SelectedRows->Items[i].c_str());
for(int k=0; k < pDS->FieldCount; k++)
qry->Parameters->Items[k]->Value = pDS->Fields->Fields[k]->Value;
qry->ExecSQL();
} | вылетает ошибка "Line 1: Incorrect syntax near '+'".
Я ошибки в коде не вижу.
И второе, что-то мне кажется, загвоздка где то в подключении через ODBC. Вариант с базой laif.mbd работает прекрасно, но не получается его перевести на базу SQL. |
|
AVC |
Отправлено: 28.01.2005, 09:53 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
1. if (DBGridEh2->SelectedRows->Count> 0) return; Моя ошибка при переписке кода. Очевидно, что условие должно быть обратным.
if (DBGridEh2->SelectedRows->Count == 0) return;
2. Прежде чем создавать таблицу её нужно удалить. В оригинале я использовал код
ADOCommand->CommandText = "Drop table ???";
try { ADOCommand->Execute(); }
catch(...) {; }
3. Вы пытались использовать хотя бы элементарную отладку? Типа ShowMessage в ключевых точках (в 99.999% случаев её вполне достаточно для поимки логической ошибки).
Что будет на экране если написать так:
qry->SQL->Text = "CREATE TABLE dbo."...
ShowMessage(qry->SQL->Text)
qry->ExecSQL();
ShowMessage("table created");
...
...
qry->SQL->Text = str1 + ") " + str2 + ")";
ShowMessage(qry->SQL->Text);
Правильно ли генерятся тексты запросов?
4.>вылетает ошибка "Line 1: Incorrect syntax near '+'".
Это ошибка в операторе SQL? Да? Если да — надо видеть текст запроса.
5. Cейчас у меня нет под рукой MySQL. Есть Access (проверено), Sybase (проверено), Oracle, MaxDB, FB. Проверю для них. Если сумею быстро достать и установить MySQL проверю и для него. |
|
|