Лекція 32
У цій лекції ми закінчимо роботу над проектом "Бібліотечний каталог". Навчимося створювати індекси, проводити за ними фільтрацію (пошук) даних, виводити дані до звіту. Навчимося створювати підстановочні поля - вводити в таблицю нове поле, з іншої таблиці.
Індекси
Первинний (основний) ключ програми служить у тому, щоб записи у таблиці сортувалися за зростанням цього ключа. Якщо ми не вказали первинного ключа, дані будуть виводитися в тому порядку, в якому їх ввів користувач.
Однак буває, коли необхідно відсортувати дані по одному або іншому полю, для цього використовують вторинні ключі -індекси.Індексівможе бути багато, вони можуть містити одне поле таблиці або кілька. Якщо ви вказуєте віндексілише одне поле, то при застосуваннііндексутаблиця буде відсортована цим полем. Якщо ви вказуєте два індексні поля, то спочатку сортування буде здійснено по першому, потім по другому полю. Втім, індексацію таблиці краще вивчати з прикладу.
Якщо зараз у вас відкрита Delphi з нашою програмою - закрийте її. Нам потрібно буде змінити структуру таблиці, і просто закриття програми не допоможе, до закриття Delphi таблиця буде у використанні, і змінити її структуру не вдасться.
Тепер відкрийте утиліту Database Desktop, та завантажте в неї нашу таблицю d:\data\books.db
Далі вибираємо команду Table – Restructure. Відкриється те саме вікно, в якому ми вводили назви та типи полів таблиці.
Перед тим, як ми встановимо індекси для полів, змінимо мовний драйвер таблиці. Якщо цього не зробити, то при використанні індексування текстовим полем, де міститься текст українськоюмові, можуть виникнути проблеми. Для зміни мовного драйвера в полі Table Properties виберіть Table Language, а у вікні Pdox ANSI Cyrillic.
Тепер ось що ще важливо – щоб індекси працювали, необхідно мати головний ключ, тому якщо у вас поле Key1 не встановлено, як ключове, зробіть це. До речі, поле Key2 у другій таблиці також має бути ключовим.
Далі приступимо до створення індексів. Оскільки сортування ми робитимемо по полю "Автор" та по полю "Назва", то й індексів у нас має бути два. У полі Table Properties виберіть Secondary Indexes і натисніть кнопку Define. Відкриється вікно, у лівій частині якого будуть всі поля нашої таблиці, а в правій – індексні поля. Права частина поки що пуста. Встановіть галочку "Maintained", без цього ви зможете скористатися індексом тільки в режимі читання, при додаванні нового запису буде виходити помилка. Установка цієї галочки говорить про те, що надалі індексація даних буде автоматичною.
Виділіть поле Avtor і кнопкою зі стрілкою праворуч скопіюйте його в праву частину. Таким чином ми вказали це поле як вторинний індекс.
Далі натискаємо кнопку OK. Вийде віконце із запитом імені індексу, вкажемо там "Sort_Avtor". Називати індекси тими самими іменами, як і таблиця, не рекомендується – індексів може бути багато, та й вам легше заплутатися в однакових назвах. Після цього індекс Sort_Avtor повинен з'явитися у вікні нижче кнопки Define. При виділенні цього індексу стануть доступними ще дві кнопки Modify і Erase. Першою кнопкою ви зможете змінити цей індекс, другою видалити його. Але нам це не потрібне.
Далі точно також робимо інший індекс - натискаємо кнопку Define, копіюємо праворуч поле Nazvanie і зберігаємо індекс на ім'я "Sort_Nazvanie". Теперу віконці ви повинні бачити два індекси. Збережіть таблицю та вийдіть із утиліти. Подивіться на каталог з базою даних - він збільшився на 4 файли, по 2 файли на кожен індекс. Саме тому бази даних у серйозних додатках бувають досить великими та містять сотні взаємопов'язаних файлів.
"Сортування – За назвою книги" міститиме
Збережіть проект, скомпілюйте його та подивіться, як він працює.
Підрахунок даних
Покращимо приклад, підрахувавши загальну кількість книг та їх суму. Для цього в модулі DM створіть змінну закладку. Вона нам потрібна для того, щоб після підрахунку повертатися до запису, звідки викликана процедура перерахунку. І змінна має бути там, де визначені компоненти Table, тому що закладки описуються в цих модулях. Змінна має бути глобальною:
bm: TBookmarkStr; //закладка
Далі, у головному модулі в розділі Private опишемо нашу процедуру:
Напишемо цю процедуру в самому низу:
all: Integer; //для заг. кількість книг
summ: Real; //для заг. суми
//переміщуємося від початку до кінця і зберігаємо результат:
while not fDM.TBooks.Eof do begin
all := all + fDM.TBooks['Exemp'];
summ: = summ + fDM.TBooks['Exemp'] * fDM.TBooks['Cena'];
//Знову переходимо на закладку і прибираємо її:
Label1.Caption := 'Усього книг:' + IntToStr(all);
Label2.Caption := 'На загальну суму: ' +
FormatFloat('0,000.00', summ) + 'пн.'; //14 лекція
Згенеруйте подію onShow для головної форми і там викличте нашу процедуру:
Також додайте її виклик із команди меню "Редагування - Додати книгу". Тепер ми можемо бути впевнені, що при додаванні книги перерахунок буде правильним.
Підстановкові поля
Створення нового поля можливе лише за неактивної таблиці, тому закрийте таблицю TBooks (властивість Active переведіть у False).
Мал. 32.1. Створення нового поля
Далі займемося пошуком потрібного запису за допомогою фільтрів.
Фільтрація даних
Фільтрування даних призначене для полегшення та прискорення пошуку необхідного запису. Якщо в базі всього близько 100-200 записів, то знайти потрібний запис нескладно шляхом звичайного перебору записів, з першого до останнього, або поки не буде знайдено потрібний запис. А от якщо записів мільйон? Чи сто мільйонів? Тоді час пошуку помітно збільшиться, і тут допоможе фільтрація.
Приступимо до програмування. На панель головної форми, яка знаходиться під меню, встановіть один компонент Label, на якому напишіть "Знайти книгу:", та один Edit, у якого видаліть текст і зробіть його трохи довшим. Коли ми будемо вводити назву книги у полі введення Edit, на сітці відображатимуться лише відфільтровані дані.
Для компонента Edit створіть подію onChange, де напишіть такий код:
/ / Якщо порожньо - не фільтруємо.
if Length(Edit1.Text) = 0 then begin
else fDM.TBooks.Filtered := True;
//індексуємо за назвою, щоб вони були за абеткою
Тут ми спочатку перевіряємо, чи є текст? Якщо ні, то прибираємо фільтр (Filtered: = False) і виходимо з процедури. Якщо текст є, то вказуємо, що таблиця повинна фільтруватися: Filtered := True. Далі, щоб швидше знайти потрібну книгу зі списку відфільтрованих записів, робимо сортування за назвою книги.
І, нарешті, встановлюємо сам фільтр. Функція QuotedStr() повертає текст, укладений у лапки. Можна було б лапки встановити самостійно, проте тут є складнощі –доводиться вважати, скільки ж лапок потрібно ставити. Справа в тому, що умова фільтрації вимагає такого запису:
А саме значення може бути в такому вигляді:
Тобто всередині рядка буде ще один рядок, який необхідно поставити в одинарні лапки. Щоб усередині тексту вказати одинарну лапку, її потрібно написати двічі, так що рядок перетворитися на такий вигляд:
fDM.TBooks.Filter := 'Nazvanie>='''+ Edit1.Text+'''';
Щоб уникнути таких складностей підрахунку лапок, існує функція QuotedStr().
Звітність
Для створення звітності існує багато способів. Найбільш " просунутий " , а й найскладніший – висновок звіту з допомогою компонентів звітності, як-отQuick Report. Інший професійний спосіб – виведення звіту у файл MS Excel чи MS Word. Ми розберемо найпростіший спосіб звітності – виводитимемо звіт у компонент Memo, а за бажання – зберігати його у файл.
Створіть нову форму. У властивості Name вкажіть fOtchet, у властивості Caption - "Звіт", а саму форму збережіть у модулі Otchet.
Далі, у верхню частину форми встановимо Memo, і розтягнемо його по всій вершині (властивість Align = alTop).
У нижній частині помістимо кнопку та компонент SaveDialog. На кнопці напишемо "Зберегти у файл".
У Memo приберемо весь текст, і встановимо вертикальне прокручування. Не забудемо додати до цієї форми модуль DM за допомогою команди Uses Unit, а в головній формі додаємо нове вікно Otchet.
Після натискання кнопки напишемо такий код:
if SaveDialog1.Execute then
Тепер нам необхідно написати код, яким Memo буде заповнюватися даними. Це найкраще зробити за подією головної форми onShow:
//робимо від першого до останнього запису:
while notfDM.TBooks.Eof do begin
//Збираємо дані в змінну s:
s := 'Автор: '+ GetFIO(fDM.TBooks['Avtor'])+#13+#10;
s := s + 'Назва: '+ fDM.TBooks['Nazvanie']+#13+#10;
s := s + 'Примірників: '+ IntToStr(fDM.TBooks['Exemp'])+#13+#10;
s := s + 'Вартість: '+ FloatToStr(fDM.TBooks['Cena'])+#13+#10;
s := s + 'Дата поставки: '+DateToStr(fDM.TBooks['Date'])+#13+#10;
//переходимо на закладку:
function GetFIO(i : Integer):String;
while not fDM.TAvtors.Eof do begin
if fDM.TAvtors['Key2'] = i then begin
if Length(Result)= 0 then
Result := 'Автор не знайдено';
Мережева база даних працює приблизно так само, як локальна, тільки сама база даних знаходиться на мережному ресурсі, і з її даними працюють кілька клієнтських програм. Такий підхід слід використовувати для невеликих мережевих додатків, коли співробітників, які працюють з даними, теж небагато. Якщо записів у таблиці перевалить за десяток тисяч, обробка такої бази даних дуже сильно завантажить мережу.
Клієнт - серверна база даних працює інакше. Дані зберігаються на сервері. Саме там зберігаєтьсяСУБД (Система Управління Базами Даних). Користувач працює не безпосередньо з даними, а отримує до них доступ ізСУБД за допомогоюSQL– запитів. Сервер ці дані обробляє, і користувачеві повертається результат – лише дані, які необхідні. А у попередньому випадку кожен клієнт отримував свою копію повної БД. Неважко збагнути, що клієнт - серверний підхід значно прискорює роботу з БД і знижує навантаження на мережу.
Однак починати програмування БД слід все ж таки з локальних додатків. У міру накопичення досвіду ви рухатиметеся далі, до мережевих та клієнтів – серверних БД.