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

 
Поиск в базе по фрагменту текста
O.Lena
Отправлено: 27.12.2004, 10:15


Ученик-кочегар

Группа: Участник
Сообщений: 21



Здравствуйте, все! С Наступающим Новым Годом smile.gif
Подскажите, пожалуйста как можно сделать поиск в заданном поле по фрагменту. Есть база MS SQLServer. В ней таблица с организациями-клиентами. Есть поле name, в котором хранятся названия организаций.
Есть DBGrid, Edit и кнопка. В коде программы ввела
TVarRec q(Edit1->Text);
DM->TableUL->FindNearest(&q, 0);
Поиск идет только по первым сиволам. А как сделать чтобы искала введенный текст и в середине названия организации и в конце? И чтобы было что-то типа "Найти дальше"
AVC
Отправлено: 27.12.2004, 10:32


Ветеран

Группа: Модератор
Сообщений: 1583



В вашем случае поможет только программирование "руками" (по опыту — пишется за пол-дня используется не один год)
А вообще если уж вы работаете с SQL'ским сервером, то такие задачки нужно ставить ему. Он для этого просто предназначен.
olegenty
Отправлено: 27.12.2004, 11:14


Ветеран

Группа: Модератор
Сообщений: 2412



это когда записей много.
а когда с гарантией мало, то всё это делается локально на клиенте. например, с помощью свойства STFilter компонента TDBGridEh (при этом надо не забыть включить соответствующий *.hpp и залинковать (pragma link) соответствующий *.obj из подкаталога DataService библиотеки EhLib)

всю жизнь работал только ограничением выборки с сервера, но в данном случае — просто тащусь от EhLib. И тебе локальная сортировка, и тебе сортировка по нескольким колонкам, и тебе и фильтрация — и всё это БЕЗ НАПИСАНИЯ ПРОГРАММНОГО КОДА.
avc*
Отправлено: 27.12.2004, 11:22


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







Абсолютно согласен. Универсального решения не бывает. Но в вопросе было DBGrid
O.Lena
Отправлено: 27.12.2004, 11:27


Ученик-кочегар

Группа: Участник
Сообщений: 21



AVC! Извините, но знаний у меня по этой части маловато. Подскажите, пожалуйста, что именно надо сделать?
AVC
Отправлено: 27.12.2004, 11:47


Ветеран

Группа: Модератор
Сообщений: 1583



Определить два события Искать и Продолжить поиск. Сделать под них кнопку / пункт контекстного меню / "горячую клавишу" (у меня все сразу).
Обработать события примерно так:

Искать
CODE

void __fastcall TF_UniEdit::QryMain_Seek (void)       // Seek
{
if (!FQryMain_PK) return;

TField *fld = Grd_Main->SelectedField;
if (!fld)   return;

AnsiString fname = fld->FieldName.Trim().UpperCase();
AnsiString title = Grd_Main->CurColumn()->Title->Caption.Trim();
if (title.IsEmpty()) title = fname;

if (fname == "???CAN_WORK" ) return;

FSeekFieldName = fname;

AnsiString str;
TStringField *sfld = dynamic_cast<TStringField*>(fld);
TDateTimeField *dtfld = dynamic_cast<TDateTimeField*>(fld);
TNumericField *nfld = dynamic_cast<TNumericField*>(fld);
TBooleanField *bfld = dynamic_cast<TBooleanField*>(fld);
//str += "\r\n<" + title + ">";
//----
if  (sfld ) str = "Искать по началу текста (*текст — по фрагменту)";
else if (dtfld) str = "Искать по началу текста (*текст — по фрагменту)";
else if (nfld ) str = "Искать по значению (*цифры — по началу)";
else if (bfld ) str = "Искать по значению";
else   return;

str += "\r\nполя <" + title + ">";
//----//

if (AskString (str, FSeekString, "Искать") <= 0)
{ FSeekString  = "";
FSeekFieldName = "";
SetToolBars();
return;
}

FSeekString = FSeekString.Trim();


LblSeekInfo_Sinxr(1, "Ищу");

FInfosRecord->UpdAdd("Time_SeekRecord", double(0))->Format = "0.00";
TDateTime tmbeg  = Now();

TWaitSQL wcur;
QryMain->DisableControls();
try {
QryMain_SeekNext(false);
}
//catch (Exception& xcp) { ShowMessage(xcp.Message); }
catch (Exception& xcp) { OEMessage_Show(xcp.Message); }

QryMain->EnableControls ();

FInfosRecord->UpdAdd("Time_SeekRecord", (double)(Now() — tmbeg) * (24*60*60));
LblSeekInfo_Sinxr  (0);

SetToolBars    ();
Grd_Main->SetFocus  ();
}


Продолжить поиск
QUOTE

void __fastcall TF_UniEdit::QryMain_SeekNext (bool isnext)
{
if (!FQryMain_PK   ) return;
if (FSeekFieldName.IsEmpty()) return;
if (FSeekString.IsEmpty() ) return;
if (QryMain->Eof   ) return;

TField *fld = QryMain->FindField(FSeekFieldName);
if (!fld) return;

TStringField *sfld = dynamic_cast(fld);
TDateTimeField *dtfld = dynamic_cast(fld);
TNumericField *nfld = dynamic_cast(fld);
TNumericField *snfld = NULL;
TBooleanField *bfld = dynamic_cast(fld);
//----
AnsiString   sval = FSeekString.UpperCase();
double    nval;
bool    bval;

bool isaster = false;
bool ok  = true;

if (sval[1] == '*' && sval.Length()> 1)
{ isaster = true;
sval = sval.SubString(2,sval.Length());
}

if  (sfld ) ;
else if (dtfld) ;
else if (nfld )
{ if (isaster) { snfld = nfld; nfld = NULL; }
else
 { try   { nval = sval.ToDouble(); }
 catch (...) { ok = false;    }
 }
}
else if (bfld )
{ if (sval.Length() < 1) ok = false;
else bval = AnsiString("1TYДXХ").Pos(sval[1])> 0;
}
else ok = false;

if (!ok) return;


ok = true;
int oldpk = FQryMain_PK;

QryMain->DisableControls();
try {

if (isnext) QryMain->Next ();
else  QryMain->First();

for (NULL; !QryMain->Eof; QryMain->Next())
{ ok = true;
if (IsBreakPoint()  ) break;

if  (sfld )
 { if (isaster){ if (sfld->AsString.UpperCase().Pos(sval)>  0) break; }
 else  { if (sfld->AsString.UpperCase().Pos(sval) == 1) break; }
 }
else if (dtfld)
 { if (isaster){ if (dtfld->AsString.Pos(sval)>  0)  break; }
 else  { if (dtfld->AsString.Pos(sval) == 1)  break; }
 }
else if (nfld )
 { if (nval == nfld->AsFloat )     break;
 }
else if (snfld )
 { if (snfld->DisplayText.Trim().Pos(sval) == 1) break;
 }
else if (bfld )
 { if (bval == bfld->AsBoolean )     break;
 }
ok = false;
}
}
//catch (Exception& xcp) { ShowMessage(xcp.Message); }
catch (Exception& xcp) { OEMessage_Show(xcp.Message); }

if (!ok) QryMain->Locate(FQryMain_PKName, oldpk, TLocateOptions());
QryMain->UpdateCursorPos();
QryMain->EnableControls ();

if (!ok) ShowMessage("Строка не найдена");

Grd_Main->SetFocus ();
}


Извиняюсь за лень. Это рабочий, не причесанный код, выдранный из окна редактирования Builder'а
olegenty
Отправлено: 27.12.2004, 13:12


Ветеран

Группа: Модератор
Сообщений: 2412



читаю я его, и думаю: если ты не посвятишь в свои соглашения, то хрен там вопрошающий разберётся, он же грит — опыта мало.

if (fname == "???CAN_WORK" ) return; — пахнет внутренним соглашением.../заплаткой/использованием св-в не по прямому назначению.

жестокий ты smile.gif
O.Lena
Отправлено: 27.12.2004, 13:20


Ученик-кочегар

Группа: Участник
Сообщений: 21



Да-а smile.gif Без некоторых разъяснений я здесь разобраться не смогу, это точно.
AVC
Отправлено: 27.12.2004, 13:29


Ветеран

Группа: Модератор
Сообщений: 1583



Согласен дать любые разъяснения. Я же извинислся за рабочий код smile.gif

if (fname == "???CAN_WORK" ) return; — означает заготовку, куда я подставлю имя поля для которого этот алгоритм работать не должен.

В коде соглашения практически не используются, а на всякую мишуру типа
FInfosRecord->UpdAdd("Time_SeekRecord", (double)(Now() — tmbeg) * (24*60*60)); — подсчет времени, затраченного на поиск
LblSeekInfo_Sinxr (0); — вывод какой то информации о прохождении поиска
SetToolBars (); — красивый поджиг и гашение кнопок и прочие синхронизации
не стоит обращать внимание. Важен принцип.
1. Определиться по какому полю искать (можно и по нескольким)
2. Как искать — по части / по началу или еще как-то
3. Откуда искать — с начала или от текущей записи
4. Сам поиск — сравнение и анализ

Отредактировано AVC — 27/12/2004, 13:32
AVC
Отправлено: 27.12.2004, 13:40


Ветеран

Группа: Модератор
Сообщений: 1583



"Лишние" строки
LblSeekInfo_Sinxr(1, "Ищу");
SetToolBars ();
TWaitSQL wcur;
Показывают в каких местах можно "навести красоту" на экране

FInfosRecord->UpdAdd("Time_SeekRecord", double(0))->Format = "0.00";
TDateTime tmbeg = Now();
Замеры времени

"Нужные" функции:
if (AskString (str, FSeekString, "Искать") <= 0)
Форма, задающая вопрос и ожидающая ответа

IsBreakPoint()  — проверяет, а не надоело ли пользователю ждать (в простом случае — нажимался ли ESC с последней проверки)

Кажется все. biggrin.gif
olegenty
Отправлено: 27.12.2004, 13:49


Ветеран

Группа: Модератор
Сообщений: 2412



короче я всё равно тащусь от TDBGridEh + примочки, и для себя лучше разберусь, как встроить поиск/фильтрацию на больших наборах прямо в грид, чем сделаю что-то ещё. уж зело мне нравиться ничего не делать, а только св-ва компонентов выставлять. я от этого делаюсь умиротворённый и производительность отнюдь не падает.

правда, для этого нужно один раз родить рабочий универсальный код. )
AVC
Отправлено: 27.12.2004, 14:04


Ветеран

Группа: Модератор
Сообщений: 1583



QUOTE
всё равно тащусь от TDBGridEh + примочки, и для себя лучше разберусь, как встроить поиск/фильтрацию на больших наборах прямо в грид

Так я и не спорю. Ну жалко мне бросать свою сетку smile.gif (по возможностям она не хуже DBGridEh). А поисковый механизм просто поленился в неё вставить (делается же для личного использования).
olegenty
Отправлено: 27.12.2004, 14:10


Ветеран

Группа: Модератор
Сообщений: 2412



если своё универсально — нечего и дёргаться.
я сейчас такой расслабленный ТОЛЬКО потому, что у меня максимальное число записей в выборке по текущей задаче < 4000. я их нагло лью на клиента, и он над ними выколбашивается, как хочет smile.gif
O.Lena
Отправлено: 28.12.2004, 12:44


Ученик-кочегар

Группа: Участник
Сообщений: 21



Спасибо за помощь AVC и Olegenty!!! Я сделала поиск с DBGridEh. Так оказалось очень просто. А как можно сделать, чтобы окно диалога поиска было на русском? Вариант, предложенный AVC, наверно был бы очень полезен для дальнейшего использования, но к сожалению разобраться в нем мне не по силам.
avc*
Отправлено: 28.12.2004, 12:55


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







Послал вам письмо с разъяснениями. Вы его не получили или там то же плохо изложено?
avc*
Отправлено: 28.12.2004, 12:57


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







Все. Получил подтверждение о получении. smile.gif

Вернуться в Работа с базами данных в C++Builder