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

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

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

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

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

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

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

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

{Добавить подраздел к  выделенному разделу(подразделу)}

procedure TfMain.N2Click(Sender: TObject);

var

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

  z: String; //для формирования  заголовка окна

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

begin

  //Проверим - есть ли  выделенный раздел?

  //Если нет - выходим:

  if TreeView1.Selected = nil then Exit;

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

  s:= '';

  //сформируем заголовок  окна запроса:

  z:= 'Раздел "' + TreeView1.Selected.Text +

     '"';

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

  if not InputQuery(PChar(z), 'Введите  заголовок подраздела:',

                    s) then Exit;

  //создаем подраздел:

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

 

  //перед сохранением подраздела в базу, прежде получим

  //номер его родителя:

  Q1.SQL.Clear;

  Q1.SQL.Add('select * from Razdels

   where R_Name='''+

                  NewRazd.Parent.Text+'''');

  Q1.Open;

 

  //Теперь сохраняем  его в базу:

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

  //присваиваем № родителя:

  tRazdels['R_Parent']:= Q1['R_Num'];

  //присваиваем название узла:

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

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

  tRazdels.Post;

end;

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

Далее, мы ввели строковую переменную z, чтобы сформировать запрос. Ведь пользователю будет удобней, если в окне InputQuery() он сразу увидит, к какому именно разделу он добавляет подраздел.

Затем, при добавлении дочернего  узла вместо метода Add() мы используем метод AddChild().

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

  Q1.SQL.Add('select * from Razdels where R_Name='''+

                  NewRazd.Parent.Text+'''');

Запрос формирует набор данных с единственной строкой - записью  родителя добавляемого элемента. Поле Q1['R_Num'], как вы понимаете, хранит номер этого родителя в запросе.

Код процедуры переименования выделенного  раздела выглядит так:

{Переименовать выделенный  раздел (подраздел)}

procedure TfMain.N3Click(Sender: TObject);

var

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

  z: String; //для формирования заголовка окна

begin

  //Проверим - есть ли  выделенный раздел?

  //Если нет - выходим:

  if TreeView1.Selected = nil then Exit;

  //получаем текущий текст:

  s:= TreeView1.Selected.Text;

  //формируем заголовок:

  z:= 'Редактирование "' + s + '"';

  //если не изменили, выходим:

  if not InputQuery(PChar(z), 'Введите  новый заголовок:', s) then Exit;

  //находим эту запись  в таблице, учитывая, что ее  по каким то

  //причинам может и  не быть:

  if not tRazdels.Locate('R_Name', TreeView1.Selected.Text, [])

then begin

    ShowMessage('Ошибка! Указанный  раздел не существует в таблице.');

    Exit;

  end; //if

  //если до сих пор  не вышли из процедуры, значит  запись найдена,

  //и является текущей.  изменяем ее:

  tRazdels.Edit;

  tRazdels['R_Name']:= s;

  tRazdels.Post;

  //теперь меняем текст  выделенного узла:

  TreeView1.Selected.Text := s;

end;

Здесь комментарии достаточно подробны, чтобы вы разобрались с кодом. Следует обратить внимание на то, что  вначале мы исправляем запись в таблице, и только потом - в узле. Если бы мы сначала исправили текст узла, как бы затем нашли старую запись в таблице? Пришлось бы вводить дополнительную переменную для хранения старого текста.

Удаляется выделенный узел еще проще:

{Удалить выделенный раздел (подраздел)}

procedure TfMain.N4Click(Sender: TObject);

var

  s: String; //для строки  запроса

begin

  //Проверим - есть ли  выделенный раздел?

  //Если нет - выходим:

  if TreeView1.Selected = nil then Exit;

  //иначе формируем строку запроса:

  s:= 'Удалить "' +

TreeView1.Selected.Text + '"?';

  //запросим подтверждение у пользователя:

  if Application.MessageBox(PChar(s), 'Внимание!',

      MB_YESNOCANCEL+MB_ICONQUESTION) <> IDYES then Exit;

  //если не вышли - пользователь желает удалить раздел.

  //найдем и удалим  его вначале из таблицы:

  if tRazdels.Locate('R_Name', TreeView1.Selected.Text, []) then

     tRazdels.Delete;

  //теперь удаляем раздел  из дерева:

  TreeView1.Items.Delete(TreeView1.Selected);

end;

Далее нам осталось сгенерировать  процедуры для сворачивания и  разворачивания дерева. Делается это одной строкой:

{свернуть дерево}

TreeView1.FullCollapse;

 

{развернуть дерево}

TreeView1.FullExpand;

Итак, метод FullCollapse дерева TreeView сворачивает его узлы, а метод FullExpand разворачивает.

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

Чтение древовидной структуры  из таблицы

Прежде всего, создадим пункты для  второго всплывающего меню, которое "привязано" к сетке DBGrid. Пункты будут такими:

  • Очистить дерево
  • -
  • Заполнить дерево

Для очищения дерева нам требуется  просто очистить его свойство Items, делается это одной строкой:

TreeView1.Items.Clear;

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

Создайте обработчик команды "Заполнить  дерево". Код обработчика будет  таким:

{Заполнить дерево}

procedure TfMain.N10Click(Sender: TObject);

begin

  //если таблица пуста,  сразу выходим:

  if tRazdels.IsEmpty then Exit;

  //если в старом дереве есть узлы, очистим их:

  TreeView1.Items.Clear;

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

  Q1.SQL.Clear;

  Q1.SQL.Add('select * from Razdels where R_Parent=0');

  Q1.Open;

  if Q1.IsEmpty then Exit; //если НД пуст, выходим.

  //теперь занесем их в дерево:

  while not Q1.Eof do begin

    TreeView1.Selected := nil;

    TreeView1.Items.Add(TreeView1.Selected,

Q1.FieldByName('R_Name').AsString);

    Q1.Next;

  end; //while

 

  //делаем запрос, выводящий  пару: Родительский узел - Дочерний  узел

  //и поочередно прописываем  их в дерево процедурой TreeViewAddChild:

  Q1.SQL.Clear;

  Q1.SQL.Append('select r.R_Name, d.R_Name '+

                    'from Razdels r, Razdels d '+

                    'where r.R_Num=d.R_Parent');

  Q1.Open;

  if Q1.IsEmpty then Exit; //если нет  вложенных узлов, выходим

  Q1.First;

  while not Q1.Eof do begin

    TreeViewAddChild(Q1.Fields[0].AsString, Q1.Fields[1].AsString);

    Q1.Next;

  end; //while

 

  //распахиваем дерево:

  TreeView1.FullExpand;

end;

 

Разберем этот код. В самом начале мы проверяем - не пуста ли таблица, и если это так, то выходим из процедуры, ничего не делая. Затем мы очищаем  старые данные из дерева. Конечно, у  нас предусмотрена очистка дерева во втором всплывающем меню, но ведь пользователь может вызвать команду "Заполнить дерево" дважды, и тогда у нас могут возникнуть проблемы.

Далее мы создаем запрос:

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

  Q1.SQL.Clear;

  Q1.SQL.Add('select * from Razdels where R_Parent=0');

  Q1.Open;

  if Q1.IsEmpty then Exit; //если НД пуст, выходим.

Здесь после выполнения метода Open мы получаем все разделы, не имеющие родителя. Иначе говоря, главные ветви дерева. Потом мы проверяем - а есть ли главные узлы в таблице? Ведь таблица может быть пуста или испорчена, и тогда дальнейшее выполнение программы не имеет смысла.

Если таблица не пуста и главные  разделы в ней есть, то мы обходим  полученный запросом набор данных от первой до последней записи, сразу же добавляя эти главные узлы в дерево:

  while not Q1.Eof do begin

    TreeView1.Selected := nil;

    TreeView1.Items.Add(TreeView1.Selected,

Q1.FieldByName('R_Name').AsString);

    Q1.Next;

  end; //while

В результате, в наше дерево пропишутся все главные разделы. После этого нам нужно будет сделать еще один запрос, который выведет все записи, имеющие родителя, в виде "Раздел - подраздел". Запрос формируется следующим образом:

  Q1.SQL.Clear;

  Q1.SQL.Append('select r.R_Name, d.R_Name '+

                    'from Razdels r, Razdels d '+

                    'where r.R_Num=d.R_Parent');

  Q1.Open;

  if Q1.IsEmpty then Exit; //если нет  вложенных узлов, выходим

Обратите внимание, в запросе  мы используем две копии одной  и той же таблицы! Подробнее о  псевдонимах таблиц в запросах смотрите лекцию № 8. В результате этого запроса мы получим примерно такой набор данных:

 
Рис. 10.4 .  Полученный набор данных

Далее мы обрабатываем полученный НД от первой до последней записи:

  Q1.First;

  while not Q1.Eof do begin

    TreeViewAddChild(Q1.Fields[0].AsString, Q1.Fields[1].AsString);

    Q1.Next;

  end; //while

Здесь мы использовали обращение к  полю не по имени, а по индексу, то есть, Q1.Fields[0] - это первое поле. Как видно из рисунка, дважды обращаясь в запросе к одному и тому же полю, мы получим разные названия этих полей (R_Name и R_Name1). Поэтому обращаться к полю по его имени не получится. В цикле мы двигаемся от первой записи к последней, вызывая процедуру TreeViewAddChild, которой у нас еще нет. И в конце процедуры мы распахиваем все узлы полученного дерева.

Теперь сделаем процедуру, которой  будем передавать все полученные подразделы. В начале модуля, в разделе private, объявите следующую процедуру:

  private

    { Private declarations }

    procedure TreeViewAddChild(rod, doch: String);

Здесь, в параметре rod мы будем передавать название родительского раздела, а в doch - название подраздела. Не убирая курсор с названия процедуры, нажмите <Ctrl + Shift + C>. Эта комбинация клавиш автоматически генерирует тело объявленной процедуры. Код процедуры следующий:

procedure TfMain.TreeViewAddChild(rod, doch: String);

var i : Integer; //счетчик

begin

  //ищем родительский  узел в дереве и выделяем  его:

  for i := 0 to TreeView1.Items.Count-1 do begin

    //если родитель найден, выделяем его и прерываем цикл:

    if TreeView1.Items[i].Text = rod then begin

      TreeView1.Items[i].Selected := True;

      Break;

    end; //if

  end; //for

  //теперь родитель имеет  выделение и мы можем добавить  к нему

  //наш узел:

  TreeView1.Items.AddChild(TreeView1.Selected, doch);

end;

Здесь мы вначале циклом for обходим дерево, ища родительский узел. Если узел найден, мы выделяем его в дереве и прерываем цикл. Теперь к выделенному родительскому узлу мы добавляем подраздел:

  TreeView1.Items.AddChild(TreeView1.Selected, doch);

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

Данный прием работы с древовидными структурами можно использовать в любой СУБД для самых разных целей.

11. Лекция: Отчеты. Quick Report: версия для печати и PDA  
На этой лекции вы научитесь создавать профессиональные отчеты, как простые, так и связанные. Изучите назначение основных компонентов из набора Quick Report.

Отчеты - это один из основных результатов работы проекта с базами данных. Для чего и создаются базы данных, как не для получения отчетов? Отчет подразумевает, что программа выбирает необходимые данные из НД (TTable, TQuery и т.п.) и выводит их на экран в удобном виде. Сам отчет можно не только просматривать, но и выводить его на печать. Лист профессионального отчета представляет собой сгенерированное графическое изображение, картинку, другими словами, данные в отчете редактировать уже нельзя. Не получится также выделить и скопировать в буфер обмена текст отчета. Готовый отчет выводит информацию, разбитую на страницы, и подготовленную к печати.

Отчеты создаются специальными наборами компонентов. Имеется очень много отчетов сторонних разработчиков, как платных, так и бесплатных, которые можно найти в Internet. В этой лекции мы разберем стандартный набор компонентов Quick Report, который поставляется вместе с Delphi.

Установка Quick Report.

Quick Report представляет собой стандартный набор компонентов для создания отчетов. Он поставляется вместе с Delphi, но не устанавливается в палитру компонентов автоматически. Нам придется установить его самостоятельно.

Если пакет Quick Report у вас еще не установлен (на палитре компонентов отсутствует вкладка QReport), то загрузите Delphi и закройте все открытые проекты (File -> Close All).

Выбрерите пункт меню "Component -> Install Packages".

Нажмите кнопку "Add" и выберите пакет "dclqrt70.bpl", который по умолчанию устанавливается по адресу:

c:\Program Files\Borland\Delphi7\bin\dclqrt70.bpl

и нажмите кнопку "Открыть". Далее, нажмите кнопку "ОК" - пакет  компонентов Quick Report установится, и его вкладка будет самой последней на Палитре компонентов. При желании можно перетащить ее мышью на другое место, поближе к началу.

Простой отчет

Создадим простой отчет на основе программы для отдела кадров из предыдущих лекций. Чтобы не менять старый проект, скопируйте его целиком в новую  папку и откройте. Кнопок на панели инструментов у нас здесь достаточно, для работы с отчетами создадим главное меню. Добавьте компонент MainMenu и создайте разделы:

Таблица 11.1. Разделы главного меню

Раздел 

Подразделы 

Файл 

Выход

Отчеты 

Кадры По телефонам  По адресам


Для самого отчета нам потребуется  новая форма. Создайте ее, свойству Name присвойте значение fRepKadr, а модуль сохраните под именем RepKadr. Сразу же командой "File -> Use Unit" подключим к этой форме модуль данных DM, а к главной форме - только что созданный новый модуль. В палитре компонентов перейдем на вкладку QReport. Самым первым компонентом на вкладке является QuickRep - основа всех отчетов. Установите его на новую форму, и он примет вид разлинованного листа. Это своего рода холст, на котором мы будем собирать различные части нашего отчета:

 
Рис. 11.1.  Пустой "холст" QuickRep

Выделите QuickRep и обратите внимание на его свойства. В самом верху находится свойство Bands (Ленты, полосы - англ.). Это раскрывающееся свойство, оно содержит шесть параметров. Щелкните по плюсу слева от свойства, чтобы раскрыть его. По умолчанию, все параметры имеют значение False, то есть, не установлены. Если какой-либо параметр перевести в значение True, на холсте появится соответствующая полоса. Попробуйте установить все параметры. Разберемся с их назначением.

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