Фильтр AdoDB для объединенных столбцов

Итак, у меня есть AdoTable, подключенный к базе данных (mdb) и DataSource с его помощью. Этот источник данных используется DBGrid...

Я попытался отфильтровать AdoTable на основе ввода пользователя. Есть 3 важных столбца: имя, фамилия и ID. Я придумал что-то вроде этого как временное решение:

AdoTable.filter:='surname like ' +
      QuotedStr('%'+edit1.text+'%')+' or name like ' +
      QuotedStr('%'+edit1.text+'%')+' or ID like ' +
      QuotedStr('%'+edit1.text+'%');
AdoTable.filtered:=true;

Он работает, но он не делает точно то, что я хотел бы, чтобы он делал... (при поиске имени И конечно же имени он ничего не найдет, так как выглядит только в одном столбце). Итак, позже я изменил свой код в это:

AdoTable.filter:='surname & " " & name like ' +
      QuotedStr('%'+edit1.text+'%')+' or name & " " & surname like ' +
      QuotedStr('%'+edit1.text+'%')+' or ID like ' +
      QuotedStr('%'+edit1.text+'%');
AdoTable.filtered:=true;

Теперь это будет делать именно то, что я хочу, но это вызывает исключение (EOleException: Аргументы имеют неправильный тип, находятся вне допустимого диапазона или конфликтуют друг с другом). Это довольно удивляет меня, так как я думал, что он должен вести себя как пункт where в команде sql (и он отлично работает как команда).

Я попытался заменить '&' на '+'. Я мог бы разделить вводимый текст, но я не хочу этого делать (это будет плохо работать, если у вас будут имена, такие как Робин ван Перси, Ахмад ибн Ханбал и т. Д.)

В качестве альтернативы я мог бы переписать всю программу, чтобы использовать запросы вместо таблиц, но я на самом деле не хочу этого делать (это также означало бы, что я получу новый recordSet КАЖДЫЙ пользователь изменил бы edit1.text вместо простой фильтрации).

Есть идеи?

редактировать: так команда, которая работает, выглядит следующим образом

select * from person where surname & " " & name like '%John Smith%' or name & " " & surname like '%John Smith%' or ID like '%John Smith%'

Фильтр выглядит так (и он вызывает исключение)

surname & " " & name like '%John Smith%' or name & " " & surname like '%John Smith%' or ID like '%John Smith%'

Обратите внимание, что вместо "Джона Смита" может быть "hn Smith", поэтому он также найдет "Kan Smithers" и т. Д.

2 ответа

Решение

Приведенный ниже код отлично работает с AdoTable, который обращается к employee стол в Дельфи dbdemos.mdb база данных. Мой AdoConnection использует Microsoft Jet 4.0 OLE DB Водитель.

procedure TForm1.Button1Click(Sender: TObject);
var
  FilterExpr : String;
begin
  AdoTable1.Filtered := not AdoTable1.Filtered;
  if AdoTable1.Filtered then begin
    FilterExpr := 'FirstName like ' + QuotedStr('%' + Edit1.Text + '%') + ' or LastName like ' + QuotedStr('%' + Edit1.Text + '%');
    AdoTable1.Filter := FilterExpr;
  end;
end;

Я думаю, что ваша ошибка, вероятно, заключается в том, что вы использовали тот специфический синтаксис Access, который вы упомянули Вы получаете доступ к таблице через уровень ADO, и этот AFAIK ожидает того же синтаксиса, который вы использовали бы, например, для серверной части Sql Server.

Из вашего комментария кажется, что вы хотите охватить случай, когда пользователь вводит в ваш Edit1.Text фрагмент имени, за которым следует пробел, за которым следует фрагмент или фамилия. Следующее сделает это:

procedure TForm1.Button1Click(Sender: TObject);
var
  FilterExpr : String;
  P : Integer;
  S1,
  S2 : String;
begin
  AdoTable1.Filtered := not AdoTable1.Filtered;
  if AdoTable1.Filtered then begin
    P := Pos(' ', Trim(Edit1.Text));
    if P > 0 then begin
      S1 := Copy(Trim(Edit1.Text), 1, P - 1);
      S2 := Copy(Trim(Edit1.Text), P + 1, MaxInt);
      FilterExpr := '(FirstName like ' + QuotedStr('%' + S1 + '%') + ')';
      FilterExpr := FilterExpr + ' or (LastName like ' + QuotedStr('%' + S2 + '%') + ')';
    end
    else
      FilterExpr := 'FirstName like ' + QuotedStr('%' + Edit1.Text + '%') + ' or LastName like ' + QuotedStr('%' + Edit1.Text + '%');
    AdoTable1.Filter := FilterExpr;
  end;
end;

Обновление: если вы хотите, чтобы пользователь мог ввести что-то вроде

Смит

тогда вы могли бы использовать подобное событие FilterRecord вместо приведенного выше кода.

procedure TForm1.ADOTable1FilterRecord(DataSet: TDataSet; var Accept: Boolean);
var
  S : String;
begin
  S := LowerCase(DataSet.FieldByName('FirstName').AsString + ' ' + DataSet.FieldByName('LastName').AsString);
  Accept := Pos(LowerCase(Edit1.Text), S) > 0; 
end;

Преобразование в LowerCase, очевидно, должно игнорировать любую заглавную букву, которую мог использовать пользователь.

Я нашел это: с помощью оператора LIKE для фильтрации и использовал принятый ответ, и он работает просто отлично. (Не могу найти его раньше, так как вопрос совсем другой)

На табличный фильтр:

procedure TDataModule1.ADOTableFilterRecord(DataSet: TDataSet;
  var Accept: Boolean);
var
  nameSurname :string;
  surnameName :string;
begin
  nameSurname:= DataSet.FieldByName('name').AsString+' '+DataSet.FieldByName('surname').AsString;
  surnameName:= DataSet.FieldByName('surname').AsString+' '+DataSet.FieldByName('name').AsString;

  if assigned(MainForm) then
    Accept := (Pos(MainForm.edit1.Text, nameSurname) > 0)
  or (Pos(MainForm.edit1.Text, surnameName) > 0)
  or (Pos(MainForm.edit1.Text, DataSet.FieldByName('ID').AsString) > 0);
end;

при редактировании изменения:

procedure TMainForm.edit1Change(Sender: TObject);
begin
    DataModule1.AdoTable.Filtered:=false;
    if edit1.Text<>'' then
      DataModule1.AdoTable.Filtered:=True;
end;

Спасибо за ваше время... Я оставлю это здесь.. Я думаю, в конце концов, кому-то это может понадобиться

Другие вопросы по тегам