C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

стр.: (2) < [1] 2 >
Копирование строк из Грида в таблицу БД
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 = "";
   }
 }
}

Вместо добавления данных в ЛистБокс их по-строчно нужно добавить в таблицу БД. Как это сделать, подскажите? У меня копируется только последняя строка. sad.gif
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();
     }
 }

Не вставляет. Если убрать цикл, то вставляется одно значение из первого столбца. Если можно, поправьте этот код, чтобы он заработал. sad.gif
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



Если честно, то я уже сдаюсь. Столько вариантов перебрала, но ни..че..го не получается. sad.gif Последний код вообще не поняла. Не думала, что пойдут такие сложности.
Очень прошу, если возможно, скинте готовый код. Уже устала так, что наверное очевидного не понимаю. wink.gif
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



QUOTE
SQL — база

SQL какая база? Ms..., My... или что то другое. Ладно. Это не важно.
У меня то-же с Insert/Append какая то ерунда творится — вызов метода разрушает программу.
Если терпит — или разберусь или перепишу на команду SQL, но это уже завтра.
** laifik
Отправлено: 25.01.2005, 19:46


Не зарегистрирован







My SQL. До завтра подожду. Все равно сегодня нужно заканчивать Зациклилась.
Мне очень нужен этот код. По всему интернету и намека нет. А уж в дибильных книгах, которые у меня имеются типа Архангельского, такие решения задач — роскошь. Поэтому, очень благодарна, что Вы помогаете. smile.gif
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


Не зарегистрирован







Ничего себе оказался вопросик. ohmy.gif Пока рыскала по интернету в поисках решения, думала, что, наверное, все так просто, что нигде нет ответа. Все все знают, кроме меня. Сейчас мне не стыдно.
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 можно взять здесь

User Attached Image Скачать файл
laif_ado.rar


** 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) то тип поля вас заботить не должен. Это головная боль серверы и драйвера. smile.gif


Эще один вариант я вам приводил в одном из ранних своих постов
>>Вариант ...
>>вместо параметрического запроса генерировать текст 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 проверю и для него.
стр.: (2) < [1] 2 >
Вернуться в Вопросы программирования в C++Builder