Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > поиск по базе


Автор: Chyslyvchyk 26.7.2004, 13:03
В поле для поиска Edit вводится слово.
Нужно:
1) пройтись ПО ВСЕМ полям базы и проверить есть ли такое слово
2) перейти к той записи, которая содержит такое слово в любом поле
3) перейти к следующей записи, которая содержит такое слово в любом поле
....

Это работает:
Код

...
var LookUpResults: Variant;
...
LookUpResults:=Table.Lookup('Surname', SearchEd.Text, 'Surname;Name');
If VarIsArray(LookUpResults) then
  begin
    If LookUpResults[0] <> Null then SurnameEd.Text:=LookUpResults[0];
    If LookUpResults[1] <> Null then NameEd.Text:=LookUpResults[1];


А так - не хочет:

Код
...
...
LookUpResults:=Table.Lookup('Surname;Name', SearchEd.Text, 'Surname;Name');
...

Ругается: Variant is not an array

Такая же проблема с этим:
Код
Table.Locate('Surname;Name', SearchEd.Text, [loCaseInsensitive])

Автор: x77 26.7.2004, 13:57
ты не путай. если в FieldNames перечисляются несколько полей, то и в FieldValues надо перечислять несколько значений. а стандартного поиска по всем полям нет. но его достаточно легко реализовать:

Код

 Found := FALSE;
 Table.First;
 while not Table.Eof do begin
   for i := 0 to Table.Fields.Count - 1 do
     if Table.Fields [i].AsString = MyText then begin
       Found := TRUE;
       Break;
     end;
   if Found then
     Break;
   Table.Next;
 end;


писал навскидку, так что отлаживать придётся самому smile.gif

Автор: Chyslyvchyk 26.7.2004, 15:42
Тут возникает проблема в том, что Table.Fields [i].AsString и MyText должны полностью совпадать.
А если человек помнит только часть поля? Например, название фирмы "Рога и копыта", а человек помнит только слово "рога"...... smile.gif

Автор: Orient 26.7.2004, 15:58
Тогда используй Locate:

Found := FALSE;
Table.First;
while not Table.Eof do begin
for i := 0 to Table.Fields.Count - 1 do
if Table.Locate( Table.Fields [i].AsString, MyText, [ loPartialKey ] ) then begin
Found := TRUE;
Break;
end;
if Found then
Break;
Table.Next;
end;


Кстати, когда используешь Locate для поиска по нескольким полям, то писать надо вот как: Table.Locate( 'Field1;Field2', VarArrayOf( [ FieldValue1, FieldValue2 ], [] ) )

Автор: x77 26.7.2004, 15:58
Chyslyvchyk,

поиск по части: if Pos (MyText, Table.Fields [i].AsString) <> 0 then ...
поиск по началу: if Pos (MyText, Table.Fields [i].AsString) = 1 then ...
поиск без регистра: if Pos (AnsiUpperCase (MyText), AnsiUpperCase (Table.Fields [i].AsString)) <> 0 then ...
поиск без регистра по началу: if Pos (AnsiUpperCase (MyText), AnsiUpperCase (Table.Fields [i].AsString)) = 1 then ...

и т.д.

Автор: Orient 26.7.2004, 16:00
Кстати, не плохо было бы проверять тип поля, чтобы искать только по текстовым, а не по всем.

Автор: x77 26.7.2004, 16:09
Orient, а ты когда-нибудь пробовал locate по каждому полю, если полей так штук 100, а записей - миллион - другой?

Автор: Orient 26.7.2004, 16:19
x77.
Честно? Нет. Но! Я немного изменил именно твой код, где перебираются именно 100 полей и вагон записей. Хочешь сказать, что банальный перебор записей быстрее чем Locate? smile.gif В принципе, на самом деле, мне хотелось бы знать живую задачу, когда нужно делать поиск по всем полям. Может тут имеются проблемы со структурой БД, и все можно сделать проще? Если конечно можно.

Автор: Chyslyvchyk 26.7.2004, 16:33
Живая задача:
Форма с уймой полей: данные про клиентов. Пользователю нужно найти все данные про клиента по ключевому слову и вывести это на форму. Но сам пользователь в одном случае помнит только имя клиента, в другом - его телефон, в третьем - фирму и т.д.
Решение задачи?
Locate:
Метод Locate ищет первую запись, удовлетворяющую критерию поиска, и если такая запись найдена, делает ее текущей. В этом случае в качестве результата возвращается True. Если запись не найдена - False.


Значицца так - вот что у меня получилось с помощью x77:
Код

var i:integer;
     Found:boolean;
begin
 Found := FALSE;
 while not Table.Eof do
 begin
    for i := 0 to Table.Fields.Count - 1 do
    begin
      if Pos (AnsiUpperCase(SearchEd.Text), AnsiUpperCase(Table.Fields [i].AsString)) <> 0 then
      begin
      SurnameEd.Text := Table.FieldByName('Surname').AsString;
      ...
      Found := TRUE;
      Break;
      end;
    end;
  Table.Next;
  end;

      if Found = FALSE then
      begin
      ShowMessage('no record');
      end;
end;

Но поиск перебирает все поля, которые совпадают с SearchEd.Text и останавливается на последнем. Как его остановить на первом? Может break де-то не там находится?

Добавлено @ 16:36
Цитата(Orient @ 26.7.2004, 15:58)
.....
Кстати, когда используешь Locate для поиска по нескольким полям, то писать надо вот как: Table.Locate( 'Field1;Field2', VarArrayOf( [ FieldValue1, FieldValue2 ], [] ) )

А что не заметно, что попытка использовать Locate уже была? smile.gif

Смотри первое мое сообщение. Тама было:
Цитата

.....
Такая же проблема с этим:
Код

Table.Locate('Surname;Name', SearchEd.Text, [loCaseInsensitive])

Автор: Orient 26.7.2004, 16:42
И чем же Locate то не подходит?
Можно же по введеным данным от оператора узнать по каким полям надобно искать, закатываешь это все в Locate и находишь именно первую запись.


Попытка использования Locate была, я помню. Просто я привел пример как искать по несколким полям.
Добавлено @ 16:47
А не пробовал генерить динамический SQL-запрос?
Будет там тебе список записей. Составлять его в зависимости от полей поиска.

Автор: x77 26.7.2004, 18:49
Chyslyvchyk, что значит "останавливается на последнем поле"??? на уровне таблицы нет понятия "текущее поле", это понятие грида. соответственно делаем:

Код

 if Pos (...) then begin
   DbGrid1.SelectedField := Table1.Fields [i];
   Found := TRUE;
   ...


Автор: Chyslyvchyk 27.7.2004, 13:44
Цитата
Chyslyvchyk, что значит "останавливается на последнем поле"??? на уровне таблицы нет понятия "текущее поле", это понятие грида.

Ээээээ, как это нет такого понятия??? Но записи ж нумеруются!
TableClient.RecNo := 10 ---> перейти к записи с номером 10
А если после этой записи больше нет записей - значит она последняя. Разве не так?

Цитата
... это понятие грида. соответственно делаем:

У меня нема грида, у меня все данные таблицы выводятся в Эдиты, Комбобоксы и ДейтТаймПикеры.



Проблема поиска уже решена. Спасибо всем за помошь!!
thumbs-up.gif

Автор: x77 27.7.2004, 15:20
Chyslyvchyk, не путай поля и записи smile.gif ты сказал, что "останавливается на последнем поле", поля можно делать "текущими" только в гриде, но не в таблице, таблицы оперируют с записями.

Автор: Chyslyvchyk 27.7.2004, 15:42
Цитата(x77 @ 27.7.2004, 15:20)
Chyslyvchyk, не путай поля и записи smile.gif ты сказал, что "останавливается на последнем поле", поля можно делать "текущими" только в гриде, но не в таблице, таблицы оперируют с записями.

Да, да, да!! Сорри!!! adv/yes.gif
Таблица состоит из записей, а запись из полей.

Автор: Ingvar Riga 27.7.2004, 20:51
Грм, Уважаемые, а слабо использовать конструкцию SELECT ..... LIKE ?
Вообще, при таких размерах таблиц Дурильон на Дофига желательно использовать СУБД с SQL. При этом выбор данных производиться гораздо шустрее...

Автор: Chyslyvchyk 28.7.2004, 11:41
Цитата(Ingvar @ 27.7.2004, 20:51)
Грм, Уважаемые, а слабо использовать конструкцию SELECT ..... LIKE ?
Вообще, при таких размерах таблиц Дурильон на Дофига желательно использовать СУБД с SQL. При этом выбор данных производиться гораздо шустрее...

Дык ведь не Дурильон на Дофига то...
А с SQL мы пока не дружим.... adv/54.gif



Кому интересно, задача была решена так:
Код
procedure ...
var i:integer;
     label GoToCircul;

begin
     // так как для поиска используется только одна кнопка "Поиск", то если не использовать первые две строки процедурка будет постоянно находить первую запись и exit
     Table.RecNo:=StrToInt(Label.caption); // в Label выводился номер текущей записи
     Table.Next;
     GoToCircul:
      while not Table.eof do
      begin
        for i := 0 to Table.Fields.Count - 1 do
        begin
          if Pos (AnsiUpperCase(SearchEd.Text), AnsiUpperCase(Table.Fields[i].AsString)) <> 0 then
         begin
          ... выполняю че мне надо, если нашло
          exit;
          end;
        end;
      Table.Next;
      end;
         // если последняя запись базы - начать сначала            
          if Table.eof then
           begin
           Table.First;
           goto GoToCircul;
           end;
end;
         


Автор: 78125 28.7.2004, 11:52
вот сморю я на код с безусловным переходом и меня аж передергивает! hehe.gif

Автор: Chyslyvchyk 28.7.2004, 12:06
Цитата(78125 @ 28.7.2004, 11:52)
вот сморю я на код с безусловным переходом и меня аж передергивает! hehe.gif

qstn.gif

Автор: Girder 28.7.2004, 12:17
А что тебя передергивает? Думаеш если у тебя нет переходов, то их и не будет в скомпилированной программе?

Автор: Akella 30.7.2004, 09:02
Процедура поиска по всем полям

Код
Procedure FindRec(What,Mes:String;DS:TDataSet;sFields:array of String;
                            foFromBegin,foCaseSansitive,ShowMes:Boolean);
{
What            - строка для поиска (editFind.Text)
DS              - таблица (TTable,TQuery и т.д.)
Fields          - список полей, по которым нужно вести поиск (['Field1','Field3','Field7'])
foFromBegin     - поиск от начала таблицы (true - от начала или False от текущей записи)
foCaseSansitive - поиск с зависимостью от регистра символов (true - зависит от регистра или False не зависит от регистра)
}
  Function FieldInFields(_Field:String;_Fields:array of String):Boolean;//
  Var
   x:byte;
  begin//содержиться ли текущее поле в масива полей, предназначенных для поиска
    Result:=False;
    For x:=Low(_Fields) to High(_Fields) do
      if _Field = _Fields[x] then begin
        result:=true;
        exit;
      end;//if
  end;//func

Var
i,q,f,w:integer;
begin
ds.DisableControls;//для ускорения отключаем таблицу
w:=ds.RecNo;
try
  //если поиск сначала таблицы
    if foFromBegin then q:=0 else q:=ds.RecNo;
  //идем по всем записям
    For i:=q to ds.RecordCount-1 do begin
  //пробег по всем полям
      For f:=0 to ds.FieldCount-1 do begin
  //если текущее поле содержится в массиве полей, предназначенных для поиска
        if FieldInFields(ds.Fields[f].FieldName,sFields) then begin
  //проверка на регистр
          if (foCaseSansitive = True) AND (POS(What,ds.Fields[f].AsString)<>0) then exit;
          if (foCaseSansitive = False) AND (POS(AnsiUpperCase(What),AnsiUpperCase(ds.Fields[f].AsString))<>0) then exit;
        end;//if FieldInFields(ds.Fields[f].FieldName,sField) then begin
      end;//For f:=0 to ds.FieldCount-1 do begin
      ds.Next;
    end;//For i:=q to ds.RecordCount-1 do begin
    ds.RecNo:=w;
    if ShowMes then ShowMessage(mes);
finally
  ds.EnableControls;//даже если произойдет исключение, то таблицу надо включить
end;//try-finally
end;//proc




Пример использования
[/code]
FindRec(edFindText.Text,'',dm.tSpis,['Dirname','Type','PathName','Prim'],False,cbRegister.Checked,False);[CODE]

Автор: 78125 30.7.2004, 09:57
Girder Я имел ввиду неоткомпилированный код....
Безусловный переход всегда можно заменить циклами и условными переходами и тд.
А скомпилированный код меня не интересцует впринципе....

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)