Программирование баз данных в Delphi

Автор работы: Пользователь скрыл имя, 26 Января 2013 в 14:13, лекция

Краткое описание

Программирование баз данных - очень большой и серьезный раздел самого что ни на есть практического программирования. На предыдущем курсе "Введение в программирование на Delphi" мы лишь коснулись этой темы, затронули даже не верхушку айсберга под названием Базы Данных, а только его макушку. Между тем, многие программисты большую часть своего времени тратят именно на проектирование баз данных и разработку приложений, работающих с ними. Это неудивительно - в настоящее время каждая государственная организация, каждая фирма или крупная корпорация имеют рабочие места с к

Вложенные файлы: 1 файл

Программирование баз данных в делфи.doc

— 2.17 Мб (Скачать файл)

С модифицирующими командами дело обстоит иначе. Команда CREATE TABLE принадлежит к той части SQL, которая называется DDL (Data Definition Language) - Язык Определения Данных. Этот язык предназначен для изменения структуры базы данных. Команды INSERT, DELETE, UPDATE относятся к DML (Data Manipulation Language) - Язык Обработки Данных, предназначенный для модификации данных. Эти команды объединяет то, что они не возвращают результирующий набор данных. Чтобы выполнить эти команды, нужно присвоить соответствующий SQL-запрос свойству SQL, а затем вызвать метод ExecSQL.

Синтаксис создания таблицы несложный:

CREATE TABLE <TableName>

(<ColumnName> <DataType> [<Size>], …) 

Здесь, TableName - имя таблицы; ColumnName - имя столбца; DataType - тип данных и Size - размер, который указывается для некоторых типов данных, например, строки. Описания столбцов таблицы разделяются запятыми. В различных СУБД синтаксис и типы данных SQL могут отличаться. Поэтому запрос, прекрасно работающий в одной СУБД, может вызвать ошибку в другой. Чтобы избежать ошибок, рекомендуется везде использовать типы ANSI, являющиеся стандартом SQL. Увы, но этих типов очень немного. Рассмотрим их:

Таблица 9.3 . Типы ANSI

Тип

Описание 

CHAR (CHARACTER) TEXT

Строковые типы данных. Обычно имеют размер до 255 символов. Требуют указания размера.

INT (INTEGER)

Целое число. Размер не указывается.

SMALLINT

Короткое целое. Размер не указывается.

FLOAT REAL

Вещественные  числа. Размер не указывается.


Как видите, многих типов просто нет. Вместо логического типа, вероятно, придется использовать строковый тип  с размером в один символ; при  этом 'Y' или '1' будут означать истину, а 'N' или '0' - ложь. Программисту придется самостоятельно делать проверку на это значение. Нет типа Memo. Нет автоинкрементного типа. Однако стандартные типы непременно будут корректно работать в любой СУБД.

Ниже приведен пример создания и открытия простой таблицы. В приложении должен иметься компонент ADOQuery, а если есть сетка и навигатор, то и DBSource. Для подключения к нужному провайдеру данных желательно использовать компонент TADOConnection. В его свойство ConnectionString нужно прописать строку подключения, например:

Provider=MSDASQL.1;Persist Security Info=False;Data Source=Файлы dBASE

Эту строку можно ввести программно, или создать подключение при  проектировании (я так и сделал). Поставщик данных в примере оставлен по умолчанию - Microsoft OLE DB Provider for ODBC Drivers, а в качестве источника данных (вкладка "Подключение" редактора связей TADOConnection) используются файлы dBase. Не следует забывать и про свойство LoginPrompt, которое следует переводить в False, чтобы программа не запрашивала имя пользователя и пароль при каждом подключении. А также нужно сразу открыть TADOConnection, установив его свойство Connected в True. В свойстве Connection компонента TADOQuery следует выбрать ADOConnection1.

Пример реализован, как событие нажатия на кнопку:

procedure TfMain.Button1Click(Sender: TObject);

var

s: String;

begin

  {Создаем текст запроса}

  s := 'CREATE TABLE MyTab(Key1 INT, Name CHAR(20), '+

       ' MyFloat FLOAT, MyDate DATE)';

 

  {Создаем таблицу}

  ADOQuery1.SQL.Clear;

  ADOQuery1.SQL.Add(s);

  ADOQuery1.ExecSQL;

  {Открываем таблицу}

  ADOQuery1.SQL.Clear;

  ADOQuery1.SQL.Add('SELECT * FROM MyTab');

  ADOQuery1.Open;

Как видите, создается четыре поля - целый тип, строковый размером 20 символов, вещественный и тип Дата. Последний тип не входит в стандартное описание ANSI-типов, тем не менее, работает в большинстве СУБД. Можете также поэкспериментировать и с типом BOOLEAN (Логический).

Итак, в переменную s мы вносим строку записи SQL-запроса. Затем очищаем свойство SQL, на случай, если там уже имелся запрос. Далее этот запрос мы заносим в свойство SQL, и методом ExecSQL выполняем его. С открытием таблицы мы уже неоднократно сталкивались. В результате выполнения кода создается и открывается файл MyTab.dbf, который находится в той же папке, что и приложение.

10. Лекция: Сохранение древовидных  структур в базе данных: версия  для печати и PDA

 
Эта лекция посвящена сохранению древовидных структур в базе данных. Вы узнаете, как можно сохранить дерево с неограниченным количеством ветвей в таблицу, и как считать его обратно в компонент TreeView.

Древовидные структуры не относятся  напрямую к программированию баз  данных, тем не менее, программисту нередко приходится "изобретать велосипед", придумывая различные  решения сохранения таких структур в таблице, и обратное их считывание в дерево.

Типичный пример дерева - всем знакомое дерево каталогов. Примеров таких структур множество - это могут быть отделы в каком-либо учреждении или разделы  библиотеки. Посмотрим на рисунок  с фрагментом дерева разделов библиотеки:

 
Рис. 10.1.  Дерево разделов

Основная сложность хранения деревьев в таблице - это то, что мы не знаем  заранее, какова будет глубина вложенности разделов. Можно было бы создать таблицу с 10 полями, например. Но если вложенных разделов будет меньше, то таблица будет неэффективна - останется много пустых полей. А если больше - ограничивать пользователя?

Самый простой способ сохранения структуры дерева и ее считывания обратно - воспользоваться тем, что дерево - это список узлов, и имеет хорошо знакомые нам методы:

  //сохраняем в файл:

  TreeView1.SaveToFile('myfile.txt');

  //читаем из файла:

  TreeView1.LoadFromFile('myfile.txt');

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

Когда программист впервые сталкивается с необходимостью хранения древовидных  структур в базе данных, обычно он первым делом подключается к Интернету и ищет какой-нибудь компонент, который бы позволил это делать. Но не все нестандартные компоненты работают качественно, да и зачем искать какой-то новый компонент, когда имеется стандартный TreeView на вкладке Win32 Палитры компонентов? Именно с этим компонентом мы и будем работать в данной лекции.

Рецептов работы с деревьями  в базах данных много, мы рассмотрим лишь один из них, достаточно эффективный  и в то же время простой. Смысл  этого способа состоит в том, чтобы в каждой записи таблицы  сохранять номер узла раздела, номер его родителя, если он есть, и название узла. В случае если узел не имеет родителя (главный узел, например, "Художественная литература" в рисунке 10.1), то в соответствующее поле запишем ноль.

Подготовка проекта

Для реализации примера нам потребуется  новая база данных. Загрузите MS Access и создайте базу данных "TreeBD", а в ней таблицу "Razdels". Вообще-то, в базе данных MS Access как таблицы, так и поля могут иметь русские названия, однако мы будем использовать средства SQL, который не всегда корректно обрабатывает русские идентификаторы. Кроме того, данный способ можно использовать в любой СУБД, а далеко не все из них так предупредительны, как MS Access, поэтому название таблицы и ее полей выполним латиницей.

Таблица будет иметь три поля:

Таблица 10.1 . Поля таблицы "Разделы"

Имя поля

Тип поля

Дополнение 

1

R_Num

Счетчик

Ключевое поле

2

R_Parent

Числовой 

Целое

3

R_Name

Текстовый

Длина 50 символов


Созданную базу данных сохраните в  папке, где будем разрабатывать  наш проект (не забудьте сделать  резервную копию пустой базы данных на всякий случай.).

Далее создадим в Delphi новый проект и простую форму:

 
Рис. 10.2 .  Форма для работы с деревом

Как всегда, назовите форму fMain, в свойстве Caption напишите "Реализация сохранения дерева в БД", модуль формы сохраните как Main, а проект в целом назовите, например, TreeToBD. Сделанная база данных TreeBD должна быть в той же папке, что и проект.

Далее установите компонент TreeView (дерево) с вкладки Win32. Его свойству Align присвойте alLeft, чтобы дерево заняло весь левый край. Затем можете установить сплиттер - разделитель, ухватившись за который пользователь сможет менять ширину дерева. Компонент Splitter находится на вкладке Additional и его свойство Align по умолчанию равно alLeft - разделитель "прилепится" к правому краю дерева.

Правее установите сетку DBGrid с вкладки Data Controls, и его свойству Align присвойте alClient, чтобы сетка заняла все оставшееся место. Ни главное меню, ни панель инструментов нам здесь не потребуются, используем лишь два всплывающих PopupMenu - первый для дерева, второй для сетки (выберите соответствующие PopupMenu в свойстве PopupMenu этих компонентов).

Далее с вкладки ADO нам потребуется компонент ADOConnection для соединения с базой данных, таблица ADOTable и запрос ADOQuery для вспомогательных нужд. С вкладки Data Access - компонент DataSource, для связи сетки с таблицей. Подключите ADOConnection к базе данных и откройте соединение (см. лекцию №2). Таблицу подключите к ADOConnection (свойство Connection), затем выберите в свойстве TableName нашу таблицу "Razdels", а свойство Name переименуйте в tRazdels - так будем обращаться к таблице. Для удобства отображения названия полей откройте редактор полей таблицы (дважды щелкнув по ней), добавьте все поля и у каждого поля измените свойство DisplayLabel, соответственно, на "№", "Родитель" и "Название". Не забудьте открыть таблицу.

Компонент DataSource подключите к tRazdels, а сетку - к DataSource, в сетке должны отобразиться поля. Кроме того, переименуйте свойство Name запроса ADOQuery1 в Q1, ведь нам часто придется обращаться к нему по имени. Запрос также подключите к ADOConnection, но делать его активным не нужно.

На этом приготовления закончены.

Создание и сохранение в таблицу дерева разделов

Работа с деревьями состоит  из двух этапов:

  1. Сохранение дерева в таблицу.
  2. Считывание дерева из таблицы.

В этом разделе лекции разберем первый этап. Щелкните дважды по компоненту PopupMenu1, который "привязан" к дереву, и создайте в нем следующие разделы:

  • Создать главный раздел
  • Добавить подраздел к выделенному
  • Переименовать выделенный
  • Удалить выделенный
  • -
  • Свернуть дерево
  • Развернуть дерево

Все эти команды относятся к  работе с разделами дерева. Прежде всего, создадим обработчик для команды "Создать главный раздел". Листинг процедуры смотрите ниже:

{Создать главный раздел}

procedure TfMain.N1Click(Sender: TObject);

var

  s: String; //для получения  имени раздела (подраздела)

  NewRazd: TTreeNode; //для создания  нового узла дерева

begin

  //вначале очистим s

  s:= '';

  //Получим в s имя  нового раздела:

  if not InputQuery('Ввод имени  раздела',

    'Введите заголовок  раздела:', s) then Exit;

  //снимаем возможное  выделение у дерева:

  TreeView1.Selected:= nil;

  //создаем главный раздел (ветвь):

  NewRazd:= TreeView1.Items.Add(TreeView1.Selected, s);

  //Сразу же сохраняем его в базу:

  tRazdels.Append; //добавляем  запись

  tRazdels['R_Parent']:= 0; //не имеет  родителя

  //присваиваем значение  созданного раздела:

  tRazdels['R_Name']:= NewRazd.Text;

  //сохраняем изменения  в базе:

  tRazdels.Post;

end;

Разберем код. Переменная NewRazd имеет тип TTreeNode, к которому относятся все разделы и подразделы (узлы) дерева. В текстовую переменную s с помощью функции InputQuery() мы получаем имя нового главного узла. Функция имеет три строковых параметра:

  1. Заголовок окна.
  2. Пояснительная строка.
  3. Переменная, куда будет записан введенный пользователем текст.

Если переменная, передаваемая в  качестве третьего параметра, пуста, то поле ввода будет пустым. Если же в ней содержался текст - он будет выведен как текст "по умолчанию". Функция возвращает True, если пользователь ввел (или изменил) текст, и False в противном случае. В результате работы функции для пользователя будет выведено простое окно с запросом:

 
Рис. 10.3 .  Окно функции InputQuery()

Далее строкой

TreeView1.Selected:= nil;

мы снимаем выделение, если какой  либо раздел был выделен, ведь мы создаем главный раздел, не имеющий родителя. Свойство Selected компонента TreeView указывает на выделенный узел и позволяет производить с ним различные действия, например, получить текст узла:

TreeView1.Selected.Text;

А присваиваемое значение nil (ничто) снимает всякое выделение, если таковое было. Далее мы создаем сам узел:

NewRazd:= TreeView1.Items.Add(TreeView1.Selected, s);

Разберем эту строку подробней. Переменная NewRazd - это новый узел дерева. Каждый узел - объект, обладающий своими свойствами и методами. Все узлы хранятся в списке - свойстве Items дерева TreeView, а метод Add() этого свойства позволяет добавить новый узел. У метода два параметра - выделенный узел (у нас он равен nil) и строка текста, которая будет присвоена новому узлу. Таким образом, в дереве появляется новый главный узел.

Затем мы сохраняем его в базу данных, предварительно добавив в  таблицу новую запись:

  tRazdels.Append; //добавляем  запись

  tRazdels['R_Parent']:= 0; //не имеет  родителя

  //присваиваем значение  созданного раздела:

  tRazdels['R_Name']:= NewRazd.Text;

  //сохраняем изменения  в базе:

  tRazdels.Post;

Вы помните, что такие методы, как Append или Insert автоматически переводят таблицу в режим редактирования, поэтому вызывать метод Edit излишне?

Обратите внимание на то, что  мы сохраняем ноль в поле "R_Parent", так как это - главный раздел, не имеющий родителя. Свойство Text нового узла NewRazd содержит название нового узла, которое мы присваиваем полю "R_Name".

Информация о работе Программирование баз данных в Delphi