Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Базы данных и репортинг > Сложная фильтрация


Автор: MacTep 11.10.2004, 08:14
У меня такой вопрос: допустим в базе данных есть несколько полей (Фамилия, Имя, Отчество, Хобби). Одно из них (например, Хобби) содержит текст, состоящий из нескольких слов (например, "коллекционирование марок"). Как отфильтровать таблицу таким образом, чтобы условием фильтрации могла быть подстрока, которая входит в строку поля, например, Хобби? Т.е. например подстрока: "кционир". Тогда программа должна отфильтровать нбор данных так, чтобы остались лишь записи, у которых в строке, которая находится в поле Хобби была такая строка, в которой есть введенная подстрока. Очень надо, а вот с какой стороный подойти, поке не додумался! Помогите, пожалуйста.

Автор: Alex 11.10.2004, 08:33
SELECT ... FROM ... WHERE UPPER(Где ищем COLLATE PXW_CYRL) CONTAINING что ищем (приведенное к верхнему регистру с помощью AnsiUpperCase)

Автор: MacTep 11.10.2004, 10:41
а через функции компонента TTable как-нибудь можно? А если черех SQL, то можно подробнее, например, весь запрос целиком. Please!

Автор: Akella 11.10.2004, 13:10
s:=AnsiUpperCase(Edit1.Text);
s:=StringReplace(s,'*', '%', [rfReplaceAll]);//если пользователь вводить для поиска коллекц* или *колл*

Query1.Close;
Query1.SQL.Clear;
Query1.AQL.Add('SELECT * FROM Table WHERE UPPER(Field1) LIKE '+QuotedStr(s)));
Query1.Open;

Автор: x77 11.10.2004, 13:19
MacTep, через функции TTable - смотри OnFilterRecords. в нём делаешь обычный Pos (...).

Автор: Akella 11.10.2004, 13:24
но учти, что на медленных машинах событие OnFilterRecords работает очень медленно

Автор: MacTep 11.10.2004, 13:52
Цитата
но учти, что на медленных машинах событие OnFilterRecords работает очень медленно
Машина вроде ничего, думаю, тормозит сильно не будет.
Цитата
через функции TTable - смотри OnFilterRecords. в нём делаешь обычный Pos (...).
Могу попросит примерчик? Делаю Pos, а потом выставляю переменную Accept?

Автор: x77 11.10.2004, 14:26
MacTep, да. например, Accept := (Pos (...) <> 0); при необходимости параметры Pos приводи к одному регистру через AnsiUpperCase.

Автор: MacTep 11.10.2004, 15:54
x77, спасибо! А насколько этот процесс может быть медленным. Приблизительно на каких машинах и при каких размерах базы данных можно заметить тормоза?

Автор: x77 11.10.2004, 16:13
это зависит впервую очередь от самого обработчика. для достаточно простых, типа Pos, будет работать довольно шустро, вполне сравнимо со скоростью классической фильтрации через свойство Filter. а вот если ты в этом обработчике будешь использовать "громоздкие" по времени операторы и функции, типа вызовов RecordCount или поиска по другой таблице, тормоза будут огромадными. в принципе для таблицы до 100000 записей особых тормозов быть не должно. но это очень расплывчато. надо пробовать для конкретной базы и смотреть.

использование условия в TQuery.SQL быстрее потому, что результирующий НД не будет фетчиться целиком, вся обработка произойдёт на стороне сервера (для парадокса его роль выполняет сама БДЕ), а в TTable - будет.

Автор: Akella 12.10.2004, 13:52
bFilterFilials - глобальная переменная
если true, то можно фильтровать

Код
procedure TfmMain.tZayFilterRecord(DataSet: TDataSet; var Accept: Boolean);
begin
 if bFilterFilials then
   Accept := DataSet['ZFilial'] = lcbFilials.KeyValue;
end;

procedure TfmMain.tZayFilterRecord(DataSet: TDataSet; var Accept: Boolean);
begin
 if   DataSet['ZFilial'] = 'филиал' then
 Accept := true;
end;

если Accept = true, то запись попадет в набор данных
и так попадут в набор данных все записи, которые имеют значение 'филиал' в поле 'ZFilial'


это событие срабатывает тогда, когда ты выполняешь фильтрацию, причем в свойсве
Filter не обязательно должно быть выражение фильтрации, просто выполняешь
Код
Table1.Filtered:=True

т.е. здесь можно фильтровать записи с помощью язывка высокого уровня, а не пятью операторами SQL hehe.gif


Автор: MacTep 12.10.2004, 22:15
dsergey, вот ето круто, а чуть подробнее, а то слышу звон, а не знаю, где он smile.gif

Автор: Akella 18.1.2005, 16:33
Кстати в DRKB мало примеров на тему "Событие OnFilterRecord".
Событие OnFilterRecord срабатывает каждый раз, когда выполняется код Table1.Filtered:=True;

вот пример для одновления фильтра
Код

procedure UpdateFilter(DataSet: TDataSet);
var
 FR: TFilterRecordEvent;
begin
 with DataSet do
 begin
   FR := OnFilterRecord;
   if Assigned(FR) and Active then
   begin
     DisableControls;
     try
       OnFilterRecord := nil;
       OnFilterRecord := FR;
     finally
       EnableControls;
     end;
   end;
 end;
end;

использовать можно так
Код

Table1.Filtered := TRUE;
UpdateFilter(Table1);


сюда можно писать код сравнения, условий и т.д.
Код

procedure TForm1.tZayFilterRecord(DataSet: TDataSet; var Accept: Boolean);
begin
//пример
  Accept := DataSet['BooleanField'] = True;
end;

при фильтрации на каждой записи вызывается это событие, а по сему каждую запись, можно своими силами отфильтровать или нет.

есть переменная Accept, если эта переменная в итоге получает значение True, то текущая запись будет показана, TTable переходит к следующей запси и все с начала
вот еще пример (бессмысленные, просто ради примера)
Код

procedure TForm1.tZayFilterRecord(DataSet: TDataSet; var Accept: Boolean);
var
i:integer;
begin
I:=random(100);
if i=50 then begin
 if DataSet.FieldByName('MyField').AsInteger = 50 then Accept = True;//запись будет показана
end else begin
 if DataSet.Fields[0] = 100 then accept = false;//запись не будет показана
end;
end;


Добавлено @ 16:40
чем сложнее условия, сообветственно медленее будет работать

еще пример, фильтрация на частичное совпадение.
Кстати, если работать (фильтровать) со строками, то могут быть проблемы с кодировкой и функцией Upper в SQL, а здесь SQL не применяется smile
Код

procedure TForm1.tZayFilterRecord(DataSet: TDataSet; var Accept: Boolean);
Var
s:string;
begin
 //Можно применить AnsiUpperCase
 //т.е. фильтрация без учета регистра и с частичным совпадением
 s:=AnsiUpperCase(DataSet.FieldByName('MyStringField').AsSting);
 accept:= POS(AnsiUpperCase(Edit1.Text), s) <> 0;
end;


Автор: Bes 20.1.2005, 11:22
Хе... А на чем база-то?

Автор: Akella 21.1.2005, 10:36
У меня Paradox, но похожий алгоритм можно, наверное и на другие переложить
Добавлено @ 10:38
По крайней мере в IBQuery и IBTable тоже есть такие же события, соответственно smile

Автор: Bes 21.1.2005, 11:25
2dsergey: Да, конечно....
Если ты можешь пользоваться запросами, то что тебе мешает использовать LIKE ? Логично и по-моему оптимально

Автор: Akella 21.1.2005, 13:08
Like не везде подойдет.
У меня база IB, "лежит под" линуксом. Доступ осуществляется как из под линукса, так и из под виндузы. При создании БД кодировка вообще не указывалась. Поэтому UPPER в єтом случае бессильна.
Базу и программы писал не я, поэтому не стоит писать что-то типа "переделвай так или вот так". Исходник нет и автор не хочет их отдавать, сам понимаешь - это его хлеб.
Я просто "выдираю" и "впихиваю" туда инфо програмным путем, уже своими программами. Я задавал на форуме вопрос с фильтрацией в таком идиотском случае, но так и не получил внятного ответа.

Автор: Bes 21.1.2005, 13:43
2dsergey:
Интересно, в каких случаях может не подойти LIKE?
Задавай еще раз свой вопрос - интересно.

Автор: Akella 22.1.2005, 10:18
ты наверное забыл о регистронезависимом поиске

не спорь, это уже проверено

Автор: Bes 24.1.2005, 07:56
2dsergey:
В споре рождается истина.
Я не знаю, что такое регистронезависимый поиск.
Если это всего лишь большие или маленькие буквы, то решается очень просто
like.....Lcase(:AD)
params[0].asstring:=LowerCase('GjdjDklDduDmd')

Автор: Akella 24.1.2005, 10:40
Цитата(Bes @ 24.1.2005, 07:56)
like.....Lcase(:AD)
params[0].asstring:=LowerCase('GjdjDklDduDmd')

Перевести сравниваемое значение в верхний или нижний регистр не проблема.

А "SELECT UPPER(имя поля)" ты пробовал сделать запрос?
Так вот в результате запроса в сетку попадают такие значения, какие находятся в этом поле, т.е. значения не переводятся в верхний регистр. Я специально проверил (IBConsole)

Автор: Bes 24.1.2005, 13:17
2dsergey:
вообще не совсем понял, что значит "такие значения, какие находятся в этом поле".
И какое отношение имеет к лайку селект, точнее зачем ты путаешь оператор при применении его к полю и к значению поля.
Если ты переведешь параметр скажем в верхний регистр и будешь искать значения, верхний регистр которых (не сами, а верхний регистр которых) будет равен параметру то на поиск
"Av"
выпадет
Av,aV,av,AV а что еще надо?
Проверено в парадоксе, на скул-сервере такой проблемы вообще нет, да и на других базах думаю решается аналогично.

Автор: Akella 24.1.2005, 16:08
Цитата(Bes @ 24.1.2005, 13:17)
Проверено в парадоксе,

При чем здесь парадокс?? smile smile

Я писал:
Цитата(dsergey @ 21.1.2005, 13:08)
У меня база IB, "лежит под" линуксом.
smile

Когда создавалась БД, то программист не указывал CharSet(символы хранятся как есть), а значит такие функции как Upper нормально работать не будут. smile

Автор: Bes 24.1.2005, 16:47
2dsergey:
Ладно, не буду спорить про линукс - не видел,
но все равно, хранятся - не значит, что с ними нельзя работать и применять какие-либо операции и функции.
Ты ведь не преобразовываешь их, не изменяешь, а просто обрабатываешь их некоторой функцией, которая всего лишь возвращает некоторое значение от аргумента, являясь своего рода переводчиком, а не переделывальщиком.
А посему, я считаю, что для любой базы в любых системах, элементарные функции работы со строками должны выполняться для этого я и сказал про парадокс, где функции перевода символов отличаются от скульных Ucase и Upper.
Может ты просто не то пишешь?
И какая связь м/у настройками базы и работой элементарных функций.... smile

Автор: Akella 24.1.2005, 17:04
смотри, что получается, обрати внимание на тексты запросов

Автор: Akella 24.1.2005, 17:07
---

Автор: Bes 25.1.2005, 07:12
2dsergey:
Блин!!!! Да, да, я тебя понял!
Хм... а теперь напиши
select remark from apartments where
ucase([remark]) like '%E%'
или у тебя upper
Интересно, что получится?
Если не трудно, пришли картинку.

Автор: Alex 25.1.2005, 08:52
Цитата(dsergey @ 21.1.2005, 13:08)
У меня база IB, "лежит под" линуксом. Доступ осуществляется как из под линукса, так и из под виндузы. При создании БД кодировка вообще не указывалась.

База, к которой имеется доступ как из под как из под линукса, так и из под виндузы при своем создании не имеет НИКАКИХ ограничений на указание кодировки.

Добавлено @ 08:56
Цитата(dsergey @ 21.1.2005, 13:08)
У меня база IB, "лежит под" линуксом.

И не важно под чем крутится сервак для базы данных это ВСЕРАВНО.

Автор: Alex 25.1.2005, 09:25
dsergey Ознакомтесь со статьей http://www.ibase.ru/devinfo/ibrusfaq.htm

И настоятельно советую попробывать выполнить вот такой запрос:
Код

SELECT * FROM MYTABLE
WHERE UPPER(MYFIELD COLLATE PXW_CYRL) = 'ВАСЯ'

Автор: Akella 26.1.2005, 16:55
Цитата(Bes @ 25.1.2005, 07:12)
select remark from apartments where
ucase([remark]) like '%E%'
или у тебя upper
Интересно, что получится?
Если не трудно, пришли картинку.


ucase - неизвестная функция для IB

Автор: Bes 26.1.2005, 16:57
"или у тебя Upper"

Автор: Akella 26.1.2005, 16:58
Alex
Цитата(Alex @ 25.1.2005, 09:25)
И настоятельно советую попробывать выполнить вот такой запрос:

Код

SELECT * FROM MYTABLE
WHERE UPPER(MYFIELD COLLATE PXW_CYRL) = 'ВАСЯ'


вот, что получилось



Цитата

Dynamic SQL Error
SQL error code = -204
Data type unknown
COLLATION PXW_CYRL is not valid for specified CHARACTER SET
Statement: select remark from apartments where
UPPER(remark collate pxw_cyrl) like '%Я%'


обрати внимание "COLLATION PXW_CYRL is not valid for specified CHARACTER SET"
Добавлено @ 17:02
пробовал так
Код

select remark from apartments where
UPPER(remark) like '%СВОЯ%'


вроде нормально

Автор: Лиходей 5.6.2008, 11:52
Здесь можно почитать как использовать событие OnFilterRecord, можно скачать простенький пример  http://delphibd.sk6.ru/

Автор: Magnetto 28.2.2009, 12:48
Akella, с простой фильтрацией по одному столбику разобрался , код прекрасно работает
Код

s:=AnsiUpperCase(Dataset.FieldByName('NameID').AsString);
accept := POS(AnsiUpperCase(Form4.Edit1.Text), s) <> 0;


а как сделать с помощью события фильтр рекорд выборку по нескольким столбцам?
пробовал так
Код


procedure TDataModule1.Table1FilterRecord(DataSet: TDataSet;
  var Accept: Boolean);
  var
  s:string;
begin
if (form4.CheckBox1.Checked = true) then
 Begin
  s:=AnsiUpperCase(Dataset.FieldByName('NameID').AsString);
  accept := POS(AnsiUpperCase(Form4.Edit1.Text), s) <> 0;
 End;
if (Form4.CheckBox3.Checked = true) then
 Begin
  s:=AnsiUpperCase(Dataset.FieldByName('GenreID').AsString);
  case (Form4.Radiogroup1.ItemIndex) of
   0 : accept := POS(AnsiUpperCase('Мужской'), s) <> 0;
   1 : accept := POS(AnsiUpperCase('Женский'), s) <> 0;
  end; 
 End;
end;


в этом варианте вообще неправильный фильтр происходит : эти две выбрки работают как бы независимо друг от друга =\

Автор: Akella 28.2.2009, 13:05
Цитата(Magnetto @  28.2.2009,  12:48 Найти цитируемый пост)
события фильтр рекорд

это как?

Добавлено через 1 минуту и 28 секунд
Цитата(Magnetto @  28.2.2009,  12:48 Найти цитируемый пост)
по нескольким столбцам?

Чекбоксы не используй, нафиг они? Что, нельзя проверить Edit`ы пустые/непустые и фильтровать только по непустым?

Добавлено через 4 минуты и 33 секунды
вот по нескольким полям
Код

acept := ((edit1.text <> '') and (dataset.fieldbyname('pole1').asstring := edit1.text)) and
               ((edit2.text <> '') and (dataset.fieldbyname('pole2').asstring := edit2.text)) and
               ((edit3.text <> '') and (dataset.fieldbyname('pole3').asstring := edit3.text)) and


можно поставить только один чекбокс, где пользователь будет выбирать тип фильтрации: по условию И или по условию ИЛИ.

Автор: Magnetto 28.2.2009, 13:10
Цитата(Akella @  28.2.2009,  13:05 Найти цитируемый пост)
это как?

OnFilterRecord , неясно выразился

Цитата(Akella @  28.2.2009,  13:05 Найти цитируемый пост)
Что, нельзя проверить Edit`ы пустые/непустые и фильтровать только по непустым? 

окей , допустим есть 4 edit'a, каждый относится в отдельному стоблику  , каким образом их слепить в один фильтр?

и еще один вопрос , в учебнике прочитал об фильтре по диапазону значений
Код

Datamodule1.Table1.SetRangeStart;
Datamodule1.Table1.FieldByName('AgeID').AsInteger := 5;
Datamodule1.Table1.SetRangeEnd;
datamodule1.Table1.FieldByName('AgeID').AsInteger := 20;
Datamodule1.Table1.ApplyRange;
Datamodule1.Table1.Filtered := true;

100% известно , что столбик AgeID типа Number и так же 100% известно , что в нем присутствуют записи в диапазоне 5-20 , но таблица становится пустой после приминения фильтра, данные в этот столбик добавлялись кодом
Код

Datamodule1.Table1.fields[1].AsInteger := strtoint(Edit2.Text);

может быть я неправильно добавил их , соответственно фильтр думает , что это не те значения\не того типа?

Автор: Akella 28.2.2009, 13:21
Я тебе советую, и многие тоже посоветуют бросить эту затею и заняться изучением SQL, я сейчас жалею, что с самого начала изучения баз данных НЕ начал с изучения SQL. Ttable - это вчерашний день.

Скачай и установи Firebird. Купи книгу Хеллен Борри и будет тебе счастье.

Автор: Akella 28.2.2009, 13:38
Код

Datamodule1.Table1.SetRangeStart;

//так
Datamodule1.Table1.KeyExlusive := true


Datamodule1.Table1.FieldByName('AgeID').AsInteger := 5;


SetRangeStart - устанавливает нижнюю границу диапазона

Автор: Magnetto 28.2.2009, 13:39
skip

Автор: Akella 28.2.2009, 13:40
а перед SetRangeStart разве не нужно IndexName указывать?

Добавлено через 44 секунды
Цитата(Magnetto @  28.2.2009,  13:10 Найти цитируемый пост)
Datamodule1.Table1.SetRangeStart;
Datamodule1.Table1.FieldByName('AgeID').AsInteger := 5;
Datamodule1.Table1.SetRangeEnd;
datamodule1.Table1.FieldByName('AgeID').AsInteger := 20;
Datamodule1.Table1.ApplyRange;
Datamodule1.Table1.Filtered := true;

а зачем тут последняя строка?
Datamodule1.Table1.Filtered := true;
закоментируй её

Добавлено через 3 минуты и 6 секунд
Ты или используй диапазоны, или фильтр, или OnFilterRecord

Автор: Magnetto 28.2.2009, 13:46
Цитата(Akella @  28.2.2009,  13:40 Найти цитируемый пост)
а перед SetRangeStart разве не нужно IndexName указывать?

в этом и была вся моя ошибка , работает smile 

Автор: Akella 28.2.2009, 13:46
Попробуй ещё
Код

Datamodule1.Table1.SetRange(['AgeID', 5], ['AgeID', 20])


Добавлено через 1 минуту и 38 секунд
Цитата(Magnetto @ 28.2.2009,  13:46)
Цитата(Akella @  28.2.2009,  13:40 Найти цитируемый пост)
а перед SetRangeStart разве не нужно IndexName указывать?

в этом и была вся моя ошибка , работает smile

А если указываешь диапазон по нескольким полям, то нужно создавать составной индекс. Кстати индекс можно на лету создавать. На форуме есть примеры.

Автор: Magnetto 28.2.2009, 14:00
Цитата(Akella @  28.2.2009,  13:46 Найти цитируемый пост)
Кстати индекс можно на лету создавать. На форуме есть примеры. 

насчет динамического создания индексов проблем не возникло...а как понимать составной индекс? т.е динамическое создание понятно
Код

Form1.table1.IndexDefs.Add('НазваниеИндекса','Поле1;поле2',[ixCaseInsensitive]); 

а как работает составной индекс?...
отсортируется таблица по одной колонке , а по второй как она будет сортироваться?...абсурд  или я не понимаю





упд

при попытке использовать следующий код
Код

procedure TDataModule1.Table1FilterRecord(DataSet: TDataSet;
  var Accept: Boolean);
  var
  s:string;
begin
accept := ((form4.edit1.text <> '') and (dataset.fieldbyname('NameID').asstring := form4.edit1.text)) and
               ((form4.edit2.text <> '') and (dataset.fieldbyname('AgeID').asstring := form4.edit2.text));
end;

получил следующую ошибку
Код

[Error] Unit1.pas(44): Operator not applicable to this operand type
[Error] Unit1.pas(45): Operator not applicable to this operand type
[Fatal Error] Unit1.pas(6): Could not compile used unit 'Unit1'

Автор: Akella 28.2.2009, 14:14
Никакого абсурда. Да сначала будут записи отсортированы по первому полю, а затем внутри этой сортировки по второму полю.

Автор: ЧиоЧигас 6.1.2010, 19:28
Хорошая статья по теме - http://delphibd.sk6.ru/index.php?action=filter

Автор: Akella 7.1.2010, 14:56
ЧиоЧигас, то уже прошлый век. Сейчас все стараются работать с SQL

Автор: eyelesss 19.1.2010, 10:12
или я не понял вопроса, или... SELECT * FROM ИмяТаблицы WHERE (Хобби LIKE "%'+подстрока+'%") ORDER BY Хобби

Автор: Akella 20.1.2010, 12:02
eyelesss, какого вопроса?

Автор: eyelesss 22.1.2010, 07:48
Akella, вопроса автора поста... smile

Автор: Akella 23.1.2010, 13:36
Автор задавал вопрос в 2004 году. Ему твой ответ, как мёртвому припарка.

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