Фільтрація даних в delphi, комп’ютерна документація від а до я

Фільтрація даних в Delphi

Хочу розповісти про декілька корисних способах фільтрації даних, які можна застосувати, щоб досягти ефекту відсіювання даних, у міру введення шуканого рядка. На приклад в таблиці є стовпець "Прізвище", користувач вводить в поле пошуку послідовно букви і дані фільтруються (відсіваються) - користувач ввів першу букву і виводяться всі прізвища, що починаються з першої літери, потім тільки ті які починаються з першої і другої і т. д. поки не знайде. що то корисне або взагалі нічого не знайде. Тобто фільтрація відбувається не за повним збігом, а по частині введеної користувачем рядка. Так само будуть приведені способи фільтрації по декількох полях.

Трохи теорії.

Основна перевага фільтрів в порівнянні з пошуком полягає в тому, що застосовувати фільтр можна без будь-яких підготовчих дій над набором даних, так як фільтри не використовують індекси. Один мінус - фільтрація виконується повільніше ніж пошук заснований на індексах.

Є два варіанти за допомогою яких можна задати умову на фільтрацію. Можна використовувати їх окремо або обидва відразу.
Перший - строкове значення у властивості Filter.
Другий - це умова описане в обробнику події OnFilterRecord.
Починається фільтрація як тільки властивість Filtered. встановлено в True.

Властивість Filter.

Спочатку опишу можливості які надає властивість Filter, а в другій частині глави буде розказано як можна використовувати подія OnFilterRecord. Саме властивість Filter потрібно використовувати в першу чергу, тому що це дає значний виграш в продуктивності при обробці великих за обсягом наборів даних (в порівнянні з подією OnFilterRecord). Особливо це помітно при великій кількості записів в таблиці (

понад 100.000), ну і звичайно багато залежить від потужності машини. При малому обсязі оброблюваних даних, різниця в швидкості не помітна.

Ну що ж приступимо. Припустимо, є таблиця з полями Прізвище, Ім'я, По батькові. Потрібно фільтрувати дані по одному з полів (на вибір), тобто користувач повинен мати можливість вибрати з якого з полів фільтрувати дані. Для тих хто в танку терміни "поле" і "стовпець" - означають одне і теж.
Завдання стоїть таке відсівати дані в міру введення шуканого рядка в Edit.

Кидаємо на форму компоненти Combobox і Edit. Combobox буде використовуватися для вибору стовпця, а в Edit буде вводиться шукане слово. Заповнюємо властивість Items в Combobox іменами стовпців. Ставимо властивість Style у Combobox рівним csDropDownList (щоб що попало не вводили). Потім в обробнику події Edit1Change пишемо наступне:

procedure TForm1.Edit1Change (Sender: TObject);
begin
if Length (Edit1.Text)> 0 then
begin
ADOTable1.Filtered: = false;
ADOTable1.Filter: = Combobox1.Text + 'LIKE' + # 39 + Edit1.Text + '%' + # 39;
ADOTable1.Filtered: = true;
end
else ADOTable1.Filtered: = false;
end;

Тиснемо F9 - вуаля. Як бачите, все досить просто. Код простий і зрозумілий. Про всяк випадок переводжу на український - якщо Edit не порожній (при порожньому вилетить помилка), відключаємо фільтрацію, формуємо рядок фільтра, запускаємо фільтрацію, якщо Edit порожній - відключаємо фільтрацію.

У найпростішому випадку, якщо не потрібно давати можливість вибору певного стовпчика, а фільтрувати по одному конкретному полю (наприклад "ПРІЗВИЩЕ"), рядок фільтра виглядала б наступним чином:

ADOTable1Fil.Filter: = 'ПРІЗВИЩЕ LIKE' + # 39 + Edit1.Text + '%' + # 39;

Рядок-умова фільтра означає наступне - вибрати ті записи з шпальти 'ПРІЗВИЩЕ', які починаються з тих же символів, що і набрані в Edit1.Text.
Особливу увагу зверніть на прогалини - "втратите" пробіл - не буде працювати.

Ключове слово LIKE дозволяє за заданим шаблоном порівнювати рядки. При цьому потрібно знати наступне:
символ '_' (підкреслення) - замінює будь-який одиночний символ,
символ '%' (відсоток) - замінює будь-яку послідовність із символів.

Знак # 39 - означає номер символу '(одинарна лапка) в кодової таблиці ASCII. Справа в тому, що значення на фільтрацію потрібно вказувати в одинарних лапках, а так як одинарні лапки використовуються в Delphi для обмеження рядків, то щоб усередині рядка поставити одинарні лапки, її потрібно поставити двічі. Конструкція # 39 + Edit1.Text + '%' + # 39 ідентична '' '' + Edit1.Text + '%' + '' ''. Правда ж перший варіант якось попонятнее. Можна також використовувати спец. функцію QuotedStr, яка повертає рядок облямовану одинарними лапками.
Загалом такі три варіанти формування рядки абсолютно однаково працюють і який варіант простіший використовувати, вирішуйте самі.

ADOTable1.Filter: = 'ПРІЗВИЩЕ LIKE' + '' '' + Edit1.Text + '%' + '' '';
ADOTable1.Filter: = 'ПРІЗВИЩЕ LIKE' + QuotedStr (edit1.Text + '%');
ADOTable1Fil.Filter: = 'ПРІЗВИЩЕ LIKE' + # 39 + Edit1.Text + '%' + # 39;

Йдемо далі. Якщо, наприклад, потрібно шукати будь-яке входження шуканого рядка в записах, а не починаючи з першого символу, то рядок фільтра виглядала б наступним чином:

ADOTable1.Filter: = 'ПРІЗВИЩЕ LIKE' + # 39 + '%' + Edit1.Text + '%' + # 39;

Помітили '%' перед Edit1.Text? Перекладається так - вибрати ті записи з шпальти 'ПРІЗВИЩЕ', в яких є послідовність символів, які набрані в Edit1.Text.

Все це прекрасно, але часто потрібно фільтрувати за кількома стовпцями. Тут застосовується трохи ін. Конструкція.
Додамо ще два компонента Edit на форму. Combobox використовувати не будемо, його можна видалити, тому що у нас кожне поле (Edit) буде відповідати за введення інформації за певним стовпцем. Ще додамо кнопочку, вона буде запускати фільтрацію. Здавалося б наступна конструкція повинна коректно працювати:

procedure TForm1.Button1Click (Sender: TObject);
begin
ADOTable1.Filtered: = false;
ADOTable1.Filter: = 'ПРІЗВИЩЕ LIKE' + # 39 + Edit2.Text + '%' + # 39 + 'AND' + 'ІМ'Я LIKE' + # 39 + Edit3.Text + '%' + # 39 + 'AND' + 'ОТЧЕСТВО LIKE' + # 39 + Edit4.Text + '%' + # 39;
ADOTable1.Filtered: = true;
end;

Вона і працює, але тільки в тому випадку, якщо дані введені в усі три поля. Якщо одне з полів введення (Edit) пусте - вилітає помилка "Аргументи мають невірний тип".
Втім, є спосіб з цим боротися. Спочатку код:

procedure TForm1.Button1Click (Sender: TObject);
var filtr, // формована рядок фільтра
add: string;
begin
ADOTable1.filtered: = false;
filtr: = '';
if length (edit2.text)> 0 then
filtr: = 'ПРІЗВИЩЕ LIKE' + # 39 + Edit2.Text + '%' + # 39;

if length (edit3.text)> 0 then
begin
if length (filtr)> 0 then add: = 'and' else add: = '';
filtr: = filtr + add + 'ІМ'Я LIKE' + # 39 + Edit3.Text + '%' + # 39;
end;

if length (edit4.text)> 0 then
begin
if length (filtr)> 0 then add: = 'and' else add: = '';
filtr: = filtr + add + 'ОТЧЕСТВО LIKE' + # 39 + Edit4.Text + '%' + # 39;
end;

if length (filtr)> 0 then
begin
ADOTable1.Filter: = filtr;
ADOTable1.filtered: = true;
end
else Showmessage ( 'Все поля порожні!');

Сенс такої, потрібно провести перевірку, пусте поле введення чи ні, а потім вже формувати рядок фільтра. Причому починаючи з другого поля введення потрібно перевіряти і рядок фільтра. Це потрібно для подальшого правильного формування рядка.
Наприклад якщо поле Edit2 (Прізвище) порожнє, то відповідно і рядок фільтра буде порожня (filtr: = ''). а, наприклад, поле Edit3 (ІМ'Я) заповнене, це означає рядок фільтра повинна мати вигляд 'ІМ'Я LIKE' + # 39 + Edit3.Text + '%' + # 39.
а не 'and' + 'ІМ'Я LIKE' + # 39 + Edit3.Text + '%' + # 39. (Тобто без 'and' спочатку).
Наступний фрагмент коду це і виконує:


if length (edit3.text)> 0 then
begin
if length (filtr)> 0 then add: = 'and' else add: = '';
filtr: = filtr + add + 'ІМ'Я LIKE' + # 39 + Edit3.Text + '%' + # 39;
end;
.

Знову перекладаю - якщо Edit3 не порожнє, то перевіряємо рядок фільтра (filtr) якщо вона не порожня, то додаємо до Сторк фільтра 'and' + 'ІМ'Я LIKE' + # 39 + Edit3.Text + '%' + # 39, якщо порожня , то просто 'ІМ'Я LIKE' + # 39 + Edit3.Text + '%' + # 39.

Усе. В кінці присвоюємо сформований рядок (filtr) властивості ADOTable1.Filter і запускаємо фільтрацію.


if length (filtr)> 0 then
begin
ADOTable1.Filter: = filtr;
ADOTable1.filtered: = true;
end
else Showmessage ( 'Все поля порожні!');
.

Відповідно, якщо на цьому етапі filtr = '', значить жодне з полів не було заповнене, про що і виводимо відповідний напис.

Пояснення трохи заплутане, але варто уважно подивитися на код і все стане ясно. Забігаючи вперед, скажу, що якщо використовувати подія OnFilterRecord, то необхідність у перевірці наявності символів на дисплеї відпадає (тобто. Його зручніше і простіше використовувати), але при цьому падає швидкість фільтрації. Втім це помітно тільки при обробці великих за обсягом наборів даних. Наостанок скажу, крім операції and можна використовувати і інші логічні операції or, not, xor. при формування рядка-умовифільтрації за кількома стовпцями, хоча така необхідність виникає досить рідко.
Тут можна скачатьрабочій приклад фільтрації на основі властивості Filter.

Розберемося з подією OnFilterRecod.
Подивимося на заголовок процедури

procedure TForm1.Table1FilterRecord (DataSet: TDataSet; var Accept: Boolean);

DataSet - ім'я фильтруемого набору даних. Accept має тип Boolean, а це значить може приймати два значення true - прийняти запис, false - відхилити. Хто не дружить з англійською перекладаю: accept -Приймати, брати, погоджуватися.
Принцип такий - в обробнику OnFilterRecord послідовно перебираються всі записи певного поля (полів) таблиці даних на предмет відповідності умові фільтрації.
Для наочності кілька прикладів:
Нагадаю ще раз, що Accept - змінна булевского типу, а це значить можна застосовувати логічні оператори (=, <>, <,>, <=,>=, And, or, not) для присвоєння їй деякого значення.
Припустимо, в таблиці є поля РІК і МІСЯЦЬ які мають цілочисельний тип і містять рік і місяць народження когось.

TForm1.Table1FilterRecord (DataSet: TDataSet; var Accept: Boolean);
begin
Accept: = (DataSet [ 'РІК']> 1973);
end;
Де то в програмі
...
Table1.Filtered: = true;
...

В результаті в DBGrid відобразяться записи, у яких значення поля РІК більше 1973.
Якщо написати в обробнику такий рядок:

Accept: = (DataSet [ 'РІК']> 1973) and ((DataSet [ 'МІСЯЦЬ']> 5) and (DataSet [ 'МІСЯЦЬ'] <9));

то відобразяться записи тих людей, які народилися після 1973 року влітку.
По-моєму все зрозуміло - DataSet [ 'ім'я_стовпця'] "витягує" чергове значення записи зазначеного стовпця, а далі Accept: = некоторое_логіческое_условіе;
Заради експерименту поставте Accept: = true; і виведуться всі записи, якщо Accept: = false; - жодної.

Якщо з якоїсь причини Ви хочете звернеться до DataSet не по імені стовпця, а по його номеру, то можна використовувати конструкцію DataSet.Fields [номер_стовпчика] .AsString замість DataSet [ 'ім'я_стовпця'].

З теорією покінчено, переходимо до практики.

Припустимо, в таблиці є поле ПРІЗВИЩЕ (тип рядок) і є поле введення шуканого рядка - Edit1.
Нам потрібно, щоб при введенні чергового символу відбувалася фільтрація (спочатку по першому символу, потім по першому і другому і т.д.). Іншими словами, потрібно визначити деякий умова в OnFilterRecord, а в обробнику події OnEdit1Change запустити фільтрацію - Table1. Filtered: = true ;.
Отже почнемо ліпити OnFilterRecord.

Залишилося тільки активізувати наш обробник, як уже говорилося вище в подію OnEdit1Change приблизно так:

procedure TForm1.Edit1Change (Sender: TObject);
begin
table1.Filtered: = false;
table1.Filtered: = true;
end;

От і все. Майже. Є ще один спосіб "посимвольной" фільтрації, але він працює трохи не так як попередній приклад. Спочатку наведу код:

procedure TForm1.Table1FilterRecord (DataSet: TDataSet; var Accept: Boolean);
begin
accept: = pos (Edit1.Text, DataSet [ 'ПРІЗВИЩЕ'])<>0);
end;

Функція Pos здійснює пошук входження підрядка в рядок, якщо підрядок знайдена то повертається позиція входження зазначеної послідовності символів в зазначений рядок, але це нам не цікаво, цікаво те, що якщо в зазначеному рядку немає заданої послідовності символів, то функція повертає нуль. Тобто якщо в будь-якому місці рядка містить прізвище (а не тільки починаючи з першої літери, в цьому і відмінність) буде знайдена шукана комбінація символів, то запис приймається.
Якщо фільтрація повинна відбуватися без урахування регістру літер, то рядок повинен мати такий вигляд:

Так, ще іноді потрібно знати, скільки записів знаходиться в відфільтрованому DataSet, для цього можна використовувати властивість RecordCount. Воно повертає поточне число записів в наборі даних. Наприклад можна застосувати наступну конструкцію:

В даному випадку її можна розмістити в обробнику події OnEdit1Change відразу після активізації процедури фільтрації.
Тут можна завантажити робочий приклад фільтрації на основі події OnFilterRecord.

PS. Кожен із способів має свої переваги і недоліки. Властивість Filter дає відчутний виграш в швидкості обробки даних, а подія OnFilterRecord набагато "гнучкіше" і зручніше у використанні. Застосовуйте властивість Filter в тих випадках якщо можна обійтися без використання OnFilterRecord.
Якщо ж Ви застосовуєте обидва способи, то варто пам'ятати, що обробник події доповнює, а не заміщає властивість Filter, тобто якщо включена фільтрація і св-во фільтр містить значення фільтра, то обробник події OnFilterRecord і фільтр (Filter) пов'язані логічним відношенням " AND "(виконуються обидва).


Додати закладку на матеріал: