Работа с сериями. Компонент TChartSeries
Компонент TChartSeries является родительским типом для серий графика (для TLineSeries, TAreaSeries, TPointSeries, TBarSeries, THorizBarSeries, TPieSeries, TChartShape, TFastLineSeries, TArrowSeries, TGanttSeries, TBubbleSeries).
Построение графиков. Компонент TDBChart
Для построения графиков используется компонент TDBChart, расположенный на странице Data Controls палитры компонентов Delphi
Компоненты для построения отчетов
В Delphi 3 на странице палитры компонентов QReport расположено около двух десятков компонентов, применяемых для построения отчетов. "Главным" компонентом, несомненно, является TQuickRep, определяющий поведение отчета в целом. Другие компоненты определяют составные части отчета:
TQRBand -
заготовка для расположения данных, заголовков, титула отчета и др.; отчет, в основном, строится из компонентов TQRBand, которые реализуют:• область заголовка отчета; • область заголовка страницы; • область заголовка группы; • область названий столбцов отчета; • область детальных данных, предназначенную для отображения данных самого нижнего уровня детализации; • область подвала группы; • область подвала страницы; • область подвала отчета.
• TQRSubDetail -
определяет область, в которой располагаются данные подчиненной таблицы при реализации в отчете связи Master-Detail на основе существующей связи между ТБД;• TQRGroup -
применяется для группировок данных в отчете;• TQRLabel -
позволяет разместить в отчете статический текст;• TQRDBText -
позволяет разместить в отчете содержимое поля набора данных;• TQRExpr -
применяется для вывода значений, являющихся результатом вычисления выражений; алгоритм вычисления выражений строится при помощи редактора формул данного компонента;• TQRSysDate -
служит для вывода в отчете даты, времени, номера страницы, счетчика повторений какого-либо значения и т.д.;• TQRMemo -
служит для вывода в отчете содержимого полей комментариев;• TQRRich Text -
служит для вывода в отчете содержимого полей форматированных комментариев;• TQRDBRich Text -
служит для вывода в отчете содержимого полей форматированных комментариев, источником которых является поле набора данных;TQRShape -
служит для вывода в отчете графических фигур, например, прямоугольников;• TQRImage -
служит для вывода в отчете графической информации, источником которой является поле набора данных;• TQRChwt -
служит для встраивания в отчет графиков.Компонент TQuickRep
Компонент TQuickRep определяет поведение и характеристики отчета в целом. При размещении этого компонента в форме в ней появляется сетка отчета (рис. 14.1). В дальнейшем в этой сетке располагаются составные части отчета, например, группы TQRBand (рис. 14.2).
Рис 141 Пустая сетка отчета Образуется послеразмещения в форме компонента TQuickRep
Рис 14 2 Сетка отчета с размещенными в ней компонентами отчета
Перечислим важнейшие свойства, методы и события компонента TQuickRep.
Свойство property Bands: TQuickRepBands; состоит из множества логических значений (False/True), которые определяют включение в отчет отдельных видов составляющих:
• HasColumnHeader - заголовка столбцов отчета;
• HasDetail - детальной информации;
• HasPageFooter - подвала страницы;
• HasPageHeader - заголовка страницы;
• HasSummary - подвала отчета;
• Has Title - заголовка отчета.
property DataSet: TDataSet;
указывает на набор данных, на основе которого и создается отчет. Обычно для выдачи отчета используется один НД.Если нужно вывести связанную информацию из нескольких таблиц БД, ее объединяют в одном НД при помощи оператора SELECT. В этом случае в качестве НД для отчета может использоваться компонент TQuery. Информацию из нескольких связанных НД можно включать в отчет, если эти наборы данных связаны в приложении отношением Master-Detail. В этом случае в качестве НД отчета указывается Master-набор, а ссылка на соответствующие Detail-наборы осуществляется в компонентах TQRSubDetail.
Если в отчет нужно включить информацию из несвязанных наборов данных, применяют композитный отчет, то есть отчет, составленный из группы других отчетов.
property Frame: TQRFrame;
определяет параметры рамки отчета:• Color - цвет линии рамки;
DrawBottom -
определяет, следует ли выводить линию снизу;DrawLeft-
определяет, следует ли выводить линию слева;DrawRight-
определяет, следует ли выводить линию справа;Draw Top- определяет, следует ли выводить линию сверху;
Style - определяет стиль линии;
Width -
определяет ширину линии в пикселях.property Page: TQRPage;
определяет параметры страницы.property PrinterSettings: TQuickRepPrinterSettings;
определяет параметры принтера.property PrintIfEmpty: Boolean;
указывает (True), что следует печатать отчет даже в том случае, если он не содержит данных.Методы
procedure NewPage; Выполняет переход на новую страницу. Может использоваться в обработчиках событий компонентов отчета BeforePrini или AfterPrint и не может - в обработчиках событий OnPrint, OnStartPage и OnEndPage.
procedure Preview;
выводит отчет в окно предварительного просмотра (рис. 14.3).Чтобы во время разработки отчета просмотреть в окне предварительного просмотра содержимое отчета в том виде, как он будет выводиться на печать, необходимо:
выбрать отчет при помощи мыши;
• нажать правую кнопку мыши;
• во всплывающем меню выбрать элемент Preview.
Следует заметить, что при этом не будут видны некоторые данные, например, значения вычисляемых полей наборов данных. Они будут выводиться только во время выполнения.
procedure Print;
печатает отчет на принтере.procedure PrinterSetup;
обеспечивает установки параметров принтера.События
property AfterPreview : TQRAfterPreviewEvent;
наступает после закрытия окна предварительного просмотра отчета.property AfterPrint: TQRAfterPrintEvent;
наступает после вывода отчета на печать.property BeforePrint: TQRBeforePrintEvent;
наступает в момент генерации отчета, до выдачи окна предварительного просмотра отчета и до вывода отчета на печать.property OnEndPage : procedure(Sender : TObject);
наступает в момент подготовки к генерации последней страницы отчета.property OnStartPage : procedure(Sender : TObject);
наступает в момент подготовки к генерации первой страницы отчета.Компонент TQRBand
Компоненты TQRBand являются основными составными частями отчета и используются для размещения в них статического текста и данных. Месторасположение компонента в отчете и его поведение определяются свойством property BandType : TQRBandType;
Ниже перечислены возможные значения этого свойства.
• rb Title - определяет компонент заголовка отчета. Информация, размещенная в компоненте TQRBand, располагается перед всеми другими частями отчета. Этот вид компонента TQRBand используется для вывода заголовочной информации отчета.
• rbPageHeader - определяет компонент заголовка страницы. Информация, размещенная в компоненте с этим значением свойства BandType, выводится всякий раз при печати новой страницы отчета прежде всех иных частей отчета (но после информации, размещенной в компоненте заголовка отчета - для первой страницы).
• rbDetail - компонент детальной информации. Выводится всякий раз при переходе на новую запись в НД отчета. Отчет печатается для всех записей НД, определяемого свойством отчета DataSet, начиная с первой записи и заканчивая последней. Позиционирование на первую запись и последовательный перебор записей в НД осуществляется компонентом TQuickRep автоматически.
• rbPageFooter - компонент подвала страницы. Выводится для каждой страницы отчета после всех иных данных на странице. rbSummary - компонент подвала отчета. Выводится на последней странице отчета после всей иной информации, но перед подвалом последней страницы отчета.
•
rbGroupHeader - компонент заголовка группы Применяется при группировках информации в отчете Выводится всякий раз при выводе новой группы• rbGroupFooter- компонент подвала группы Применяется при группировках информации в отчете Выводится всякий раз при окончании вывода группы, после всех данных группы
• rbSubDetail - компонент для выдачи детальной информации из подчиненного набора данных, при выводе в отчете информации из двух или более наборов данных, связанных в приложении при помощи механизма Master-Detail. Это значение присваивается компоненту автоматически, когда генерируется компонент TQRBand при размещении в форме компонента TQRSubDetail Программа не должна устанавливать это значение в свойство BandType
• rbColumnHeader - компонент для размещения заголовков столбцов Размещается в отчете на каждой странице после заголовка страницы
• rbOverlay - используется для совместимости с более ранними версиями отчетов
Свойство property Enabled : Boolean; указывает, печатается в отчете (True) или нет (False) информация, содержащаяся в компоненте TQRBand
Свойство property ForceNewPage : Boolean; указывает, должна ли информация в составе TQRBand всегда печататься с новой страницы (True) или нет (False)
Событие property BeforePrint: TQRBeforePrintEvent; наступает перед печатью информации, размещенной в области компонента TQRBand.
Создание простейшего отчета
Компоненты TQuickRep и TQRBand являются минимально достаточными для создания простого отчета, не содержащего внутри себя группировок информации.
Пусть имеется таблица БД Rashod DB, содержащая сведения об отпуске материалов со склада. В состав ТБД входят поля
• N_RASH - уникальный номер события отпуска товара;
• DEN - номер дня;
• MES - номер месяца;
•
GOD - номер года;• TOVAR - наименование отпущенного товара;
• POKUP - наименование покупателя,
• KOLVO - количество единиц отпущенного товара
Заметим, что дата отпуска товара хранится в разбивке на день, год и месяц Сделано так специально, с целью показать, как в отчетах используются выражения и вычисляемые поля
Создадим простейший отчет, состоящий из заголовка и сведений об отпуске товара В отчет включаются все факты отпуска товара Сортировка производится по номеру события отпуска товара
Разместим в форме компонент TTable (имя Table1), свяжем с таблицей БД Rashod DB и откроем его (Active = True) Разместим в форме компонент TQuickRep (имя QuickRep!) Установим в свойство DataSef отчета значениеTable1, назначив таким образом отчету НД, записи из которого будут выводиться в отчет Добавим в отчет компонент TQRBand (имя QRBandl) Свойство BandTvpe компонента QRBandl по умолчанию установлено в значение rb Title, то есть компонент QRBandl определяет заголовок отчета Разместим в пространстве отчета, занимаемом компонентом QRBandl, компонент TQRLabel (статический текст) с именем QRLabell Установим в свойство Caption этого компонента значение 'Отпуск товаров со склада' и установим в свойстве Font жирный наклонный шрифт высотой 16 пунктов Вид отчета показан на рис 14 4Рис 14.4 В отчете определен только его заголовок Рис 14.5 Отчет с заготовкой и группой детальной информации
Теперь разместим в отчете данные, соответствующие текущей записи таблицы Rashod Для этого поместим в отчет новый компонент TQRBand (имя QRBandl) и установим в его свойство BandType значение rbDetail Затем разместим в группе шесть компонентов TQRDBText с именами QRDBText1. QRDBText6. Свяжем данные компоненты соответственно с полями НД -N_RASH, TOVAR, KOLVO, DEN, MES, GOD Для этого в свойство DataSet каждого компонента QRDBText установим значение Table1, а в свойство DataField- значение имени соответствующего поля Вид отчета показан на рис 14.5
Для просмотра получившегося отчета щелкнем по нему правой кнопкой мыши и из всплывающего меню выберем элемент Рreview. Получим окно предварительного просмотра отчета (рис 14.6)
Чтобы окно предварительного просмотра выдавалось при активизации формы, введем в обработчик события OnActivate формы такой код
procedure TForm1.FormActivate(Sender: TObject)
begin
QuickRep!.Preview; end;
Чтобы после выхода из окна предварительного просмотра отчета закрывалась и форма, в которой расположен отчет, введем в обработчик события AfterPreviCK отчета такой код
procedure TForm1.QuickReplAfterPreview (Sender : T0bject);
begin
Form1.Close;
end;
Использование компонента TQRExpr для определения выражений
Из рис 14.6 видно что в простейшем отчете выводится дата, составленная из трех полей - DEN, MES GOD Объединим значения из этих трех полей в одно значение являющееся результатом вычисчения выражения Выражение в отчетах формируется при помощи компонента TQRExpr Удалим из компонента QRBandl компоненты QRDBText4 QRDBText6, связанные с полями DEN, MES, GOD Вместо них разместим в отчете компонент TQRExpr (имя QRExprI)
Для того чтобы войти в редактор формул данного компонента, выберем в инспекторе объектов свойство Expression и нажмем кнопку в поле данных этого свойства
Появится окно редактора формул - Expression builder, (построителя выражений) (рис 14.7)
Группа Constant позволяет включить в выражение константы, при этом комбинированный список предназначен для выбора типа константы - числовая (Numeric) или строковая (String). Группа Function позволяет включить в выражение функцию, при этом выпадающий список Category определяет категорию функции (по умолчанию показываются все функции, значение All). Группа DataField позволяет включить в выражение поля набора данных, причем список DataSet определяет НД, а в списке Fields перечисляются поля текущего НД.
В каждой из указанных групп присутствует кнопка Add. Она позволяет добавить константу, функцию или поле непосредственно в текст выражения (самая нижняя группа без названия).
Выберем в списке функций STR - функцию, преобразующую числовое значение в строковое и нажмем кнопку Add в группе функций. Поскольку функция имеет параметр, редактор формул перейдет в режим ввода параметра. Выберем в группе Data Field's окне DataSet набор данных Table1 и в списке полей - DEN, после чего нажмем кнопку Add и затем Ok. В поле формулы будет сформирована часть формула STR(Table1.DEN). На панели Operators нажмем кнопку '+'. В группе констант установим тип строковой константы (String) и введем точку, после чего нажмем кнопку Add (рис. 14.8).
Аналогичным образом введем другие части формулы, чтобы она в конечном виде была такой:
STR(Table1.DEN) + '.' + STR(Table1.MES) + '.' + STR(Table1.GOD)
Затем-нажмем кнопку Ok и выйдем в инспектор объектов. Установим в свойство AutoSize компонента QRExpr! значениеРа^е и изменим размеры компонента QRExprI так, чтобы он занимал примерно 10 символов, затем установим выравнивание вправо (свойство Alignment = taRightJustify). Запустим режим предварительного просмотра содержимого отчета (рис. 14.9). Как видим, дата отпуска товара приобрела более привычный вид.
ЗАМЕЧАНИЕ.
Другим способом составление значения даты из трех полей могло бы быть создание для НД Table1 вычисляемого поля (например, SymData) и определение алгоритма вычисления его значения в таком обработчике события OnCalcFields:procedure TForm1.Table1CalcFields(DataSet: TDataSet) ;
begin
Table1SymData.Value := Table1DEN.AsString + '.' + Table1MES.AsString + '.' + Table1GOD.AsString;
end;
Использование TQRBand для представления заголовков столбцов
Компонент TQRBand, у которого в свойство BandType установлено значение rbColumnHeader, используется для представления заголовков столбцов. Заголовки столбцов определяются при помощи компонентов TQRLabel.
В отчете, рассмотренном в предыдущих разделах, разместим компонент TQRBand (имя QRBand3) и установим в его свойство BandTypc значение rbColumnHeader. Разместим в пространстве отчета, определяемом QRBand3, четыре компонента TQRLabel (имена QRLabel2 ... QRLabel5) и установим в свойства Caption этих компонентов соответственно значения '№№', 'Товар', 'Количество', 'Дата'. В свойстве Font данных компонентов установим режим подчеркивания шрифта, а сам шрифт определим как наклонный. Вид отчета представлен на рис. 14.10.Рис. 14.10. Определение заголовков столбцов Рис 14.11 В отчете появились заголоаки столбцов
Выйдем в окно предварительного просмотра отчета. Для каждой страницы отчета вверху страницы будут выводиться названия столбцов (рис. 14.11).
Использование TQRBand для показа заголовка и подвала страницы
Компонент TQRBand, у которого в свойство BandType установлено значение rbPageHeader, используется для показа заголовка страницы. Он выводится для каждой новой страницы перед выводом другой информации. Компонент TQRBand, у которого в свойство BandType установлено значение rbPageFooter, используется для показа подвала страницы. Он выводится для каждой страницы после вывода любой иной информации.
Информация в заголовке и подвале страницы может формироваться на основе статического текста (компоненты TQRLabel), значений полей (компоненты TQRDBText) и результатов вычисления выражений (компоненты TQRExpr).
В отчете, рассмотренном в предыдущих разделах, разместим компонент TQRBand (имя QRBand4) и установим в его свойство BandType значение rbPageHeader. Не будем размещать в заголовке страницы никакого текста, просто отчеркнем линию вверху страницы. Для этого установим в свойство компонента заголовка страницы Frame. DrawTop значение True, что обеспечивает вывод линии по верхнему краю области, занимаемой компонентом.
Аналогичным образом определим в отчете компонент подвала страницы (имя QRBand5) и установим в его свойство Frame.DrawBottom значение True, что обеспечивает вывод линии по нижнему краю области, занимаемой компонентом.
Войдя в режим предварительного просмотра отчета, увидим, что вверху и внизу каждой страницы отчета выводятся линии.
Использование компонента TQRSysData для показа вспомогательной и системной информации
Компонент TQRSysData используется для показа вспомогательной и системной информации. Вид показываемой информации определяется свойством property Data : TQRSysDataType;
Ниже указаны возможные значения этого свойства.
qrsColumnNo -
номер текущей колонки отчета (для одноколоночного отчета всегда 1).qrsDate -
текущая дата.arsDate Time-текущие дата и время.
qrsDetailCount -
число записей в НД; при использовании нескольких НД - число записей в master-наборе. Для случая, когда НД представлен компонентом TQuery, эта возможность может быть недоступной, что связано с характером работы компонента TQuery, который возвращает столько записей, сколько необходимо для использования в текущий момент, а остальные предоставляет по мере надобности;qrsDetaiINo -
номер текущей записи в НД. При наличии нескольких наборов - номер текущей записи в master-наборе.qrsPageNumber -
номер текущей страницы отчета.qrsPageCount -
общее число страниц отчета.qrs ReportTitle -
заголовок отчета.ars Time - текущее время.
Разместим в компоненте подвала отчета QRBand5 два компонента TQRSysData (имена QRSysDatal... QRSysData2). В свойство Data первого из них установим значение arsDate (текущая дата), второго - значение qrsPageNumber (номер текущей страницы отчета). Войдем в режим предварительного просмотра результатов отчета. Теперь в подвале каждой страницы отчета выводятся номер страницы и текущая дата (рис. 14.12).
Группировки данных в отчете
Выше мы рассмотрели отчет, в котором информация об отпуске товаров из ТБД Rashod.DB со склада выводилась "как есть". Такое представление информации в отчете не всегда информативно. Пусть, например, в нашем случае требуется сгруппировать информацию по товарам.
Для группировки информации используется компонент TQRGroup. Его свойство Expression указывает выражение. В группу входят записи НД, удовлетворяющие условию выражения. При смене значения выражения происходит смена группы. Для каждой группы, если определены, выводятся заголовок группы и подвал группы. В качестве заголовка группы служит компонент TQRBand со значением свойства BandType, равным rbColumnHeader. В качестве подвала группы служит компонент TQRBand со значением свойства BandType, равным rbGroupFooter.
Свойство FooterBand компонента TQRGroup содержит ссылку на компонент подвала группы.
В заголовке группы, как правило, выводится выражение, по которому происходит группировка, и различные заголовки, если они нужны. В подвале группы обычно выводится агрегированная информация - суммарные, средние и т.п. значения по группе.
Пример.
Построим отчет о расходе товара со склада, в котором информация группируется по наименованию товара. Для этого определим набор данных отчета (компонент TTable, имя Table1). Установим у НД текущим индекс по полю Tovar (в свойстве Fieldlndex Names или IndexNawe). Разместим в отчете:заголовок отчета - компонент TQRBand с именем QRBandl, свойство BandType = rb Title;
• заголовок столбцов - компонент TQRBand с именем QRBandS, свойство BandType = rbColumnHeader,
• группу - компонент TQRGroup с именем QRGroupl.
• область детальной информации - компонент TQRBand с именем QRBand2, свойство BandType = rbDetail;
подвал группы - компонент TQRBand с именем QRBand4, свойство BandType = rbGroupFooter.
В компоненте QRGroupl установим:
в свойство FooterBand значение QRBand4;
• в свойство Expression значение Table1.TOVAR, которое является формулой и строится в редакторе формул.
Поскольку свойство Expression не визуализирует значения выражения, необходимо разместить в группе компонент TQRExr (имя QRExrl) и определить значение его свойства Expression так, чтобы оно содержалоTable1.TOVAR.
В компоненте подвала группы QRBand4 будем подсчитывать сумму по полю Kolvo (сумму отпущенного конкретного товара). Для этого разместим в подвале группы компонент TQRExr (имя QRExr2) и определим значение его свойства Expression так, чтобы оно содержало формулу SUM(Table1.KOLVO).
В группе детальной информации разместим компоненты TQRDBText, связанные с полями Pokup и Kolvo НД. Заполним другие области отчета статическим текстом, как это показано на рис.14.13.
На рис 14.14 показан отчет в режиме предварительного просмотра На рис 14.15 показан подвал одной из групп - там выводится информация о суммарном расходе товара
Множественная группировка данных в отчете
Часто внутри группы должны содержаться другие группы, например, по названию товара и внутри каждой группы - по покупателям В этом случае внутри одной группы определяют другую группу посредством дополнительных компонентов TQRGroup
Пример Пусть требуется представить в отчете сведения о расходе товаров со склада, группируя данные по товарам, а внутри группы - по покупателям
Установим текущий индекс в НД Table1 по полям Tovar, Pokup Общий вид отчета приводится на рис 14.16
Вид отчета в окне предварительного просмотра представлен на рис 14.17
Построение отчета на основе
Если необходимо выдавать отчет на основе более чем одной ТБД, можно поступить двумя способами
1 В рамках компонента TQuery произвести соединение данных из нескольких таблиц БД в один НД, после чего определить в отчете нужные группировки;
2 Создать в приложении по одному НД на каждую таблицу БД, соединить эти наборы между собой связью Master-Detail (используя свойства MasterSource, MasterFields набора данных) и применить в отчете компонент (или несколько компонентов) TQRSubDetail для вывода информации из подчиненного (Detail) НД (или группы подчиненных НД), для вывода информации из основного (Master) НД, как и в обычных отчетах, применяется компонент TQRBand, у которого в свойстве BandType установлено значение rbDetail Построение отчета для первого случая осуществляется аналогично тому, как это описано выше Построение отчета для второго случая имеет некоторые отличительные особенности
Рассмотрим второй способ
Компонент TQRSubDetail предназначен для показа информации в отчете из подчиненного НД Его свойство property DataSet: TDataSet; указывает имя подчиненного НД, информация из которого будет выводиться в пространстве компонента TQRSubDetail. В остальном использование данного компонента аналогично использованию компонента TQRBand, у которого значение в свойство BandType установлено значение rbDetail
Пример
Пусть имеется таблица БД Tovary DB, содержащая помимо прочих поле Tovar (название товара)Пусть также имеется таблица БД Rashod DB, содержащая сведения об отпуске материалов со склада В состав ТБД входят поля
• N_RASH - уникальный номер события отпуска товара,
• DEN-номер дня,
• MES - номер месяца,
• GOD - номер года,
• TOVAR - наименование отпущенного товара,
• POKUP - наименование покупателя,
• KOLVO - количество единиц отпущенного товара
Таблицы Tovary DB и Rashod DB находятся в отношении "один-комногим", то есть одному товару может соответствовать более одного факта отпуска товара со склада
Разместим в форме компонент TTable (имя TovaryTable), ассоциированный с ТБД Tovary DB Разместим в форме компонент TDataSource (имя DS_TovaryTable) и свяжем его с TovaryTable. Разместим в форме компонент TTable (имя RashodTable), ассоциированный с ТБД Rashod DB
Установим связь Master-Detail в приложении между НД TovaryTable и RashodTable Для этого при помощи редактора связей установим в свойство RashodTable MasterSource значение DS_TovaryTable, и в свойство RashodTable MasterFields значение 'TOVAR' (рис 14.18)
Заметим что после установления связей между НД в НД RashodTable текущим индексом должен быть индекс по полю Tovar (свойство RashodTable IndexFieldNames = Tovar)
Приступим к разработке отчета Пусть отчет имеет имя QuickRepI Определим заголовок отчета компонент TQRBand с именем QRBand2 в свойство BandType которого установлено значение rb Title
Установим в качестве основного НД отчета TovaryTable указав QuickRepI DataSet = TovaryTable
Разместим в отчете компонент типа TQRBand с именем QRBand1 и установим в его свойство BandType значение ibDelail Этот компонент будет использоваться для выдачи детальной информации из НД TovaryTable Разместим в области компонента QRBandl компонент TQRDBText (имя QRDBText1) и свяжем его с полем Tovar НД TovaryTable
Разместим в отчете компонент TQRSubDetail (имя QRSubDetaill) Установим в его свойство DataSet значение RashodTable связав таким образом данный компонент с подчиненным НД Разместим в области компонента QRSubDetail1 три компонента TQRDBText (имена QRDBTextl QRDBText4) и свяжем их с полями НД RashodTable соответственно Pokup Kolvo D Поле D опредечено в НД RashodTable как вычисляемое по значениям полей DEN MES GOD
Разместим в области компонента QRBandl заголовки столбцов Вид формы отчета показан на рис 14.19
Войдем в режим предварительного просмотра результатов В результирующем отчете для каждой записи НД TovaryTable выводятся по (чиненные ей записи в НД RashodTable (рис 14 20)
ЗАМЕЧАНИЕ
Если необходимо определить заголовок и подвал для информации группируемой в компоненте TQRSubDetail следует воспользоваться свойством этого компонентаproperty Bands : TQRSubDetailGroupBands,
Данное свойство обладает двумя подсвоиствами указывающими на наличие или отсутствие заголовка и подвала Этоproperty HasHeader . Boolean;
property HasFooter. Boolean;
Построение композитного отчета
Композитный (составной сложный) отчет объединяет в себе несколько простых отчетов При выдаче композитного отчета входящие в его состав простые отчеты выводятся друг за другом
Композитный отчет реализуется при помощи компонента TQR CompositeReport В обработчике события OnAdd'Report ранее определенные простые отчеты добавляются в списковое свойство Report
procedure TCompozitnyjOtchet.QRCompositeReportlAddReports(Sender:TObject) ;
begin
WITH QRCompositeReportI do begin
Reports.Add(ManyGroup.QuickRep1) ;
Reports.Add(Prostoj) .QuickRep1) ;
END;//with
end;
В приведенном выше обработчике композитный отчет составляется из двух отчетов: QuickRep1 (определенный в форме ManyGroup) и QuickRep1 (определенный в форме Prostoj). Печать композитного отчета или его предварительный просмотр осуществляется так же, как для простых отчетов, например
QRCompositeReportI.Previewж Ha рис. 14.21. показан композитный отчет, построенный из двух ранее разработанных нами отчетов - отчета без группировок и отчета с множественными группировками по товару и покупателю.
Рис 14.21. Композитный отчет, составленный из двух простых отчетов
Понятие многомерных данных
В процессе принятия решений во многих сферах приходится анализировать многофакторную информацию о предметной области. Пусть, к примеру, организация занимается продажами товаров. При закупках товаров эта организация должна представлять, насколько устойчив спрос на товары, каковы темпы роста спроса - по товару, по региону, по конкретным организациям, каковы расходы, прибыль и многое другое.
Ситуация, когда аналитику приходится иметь дело с многофакторными данными, осложняется двумя обстоятельствами:
• во-первых, средний человек может одновременно оперировать тремя-четырьмя сущностями, учитывая при этом связи между ними;, с увеличением числа сущностей эффективность обработки информации человеком падает;
во-вторых, чисто психологически человеку, живущему в трехмерном мире, трудно представить пространство размерностью больше 3.
Математики давно оперируют с многомерными пространствами, но это абстрактный подход. В области практических применений наша способность оперировать многомерными данными, к сожалению, является ограниченной.
При возникновении необходимости обработки многофакторной информации применяют или многомерные базы данных (они остаются за рамками нашего рассмотрения), или в реляционных СУБД реализуют такие механизмы организации данных и доступа к ним, которые резко повышают эффективность анализа многофакторных данных.
Метакуб (многомерный куб)
Сущность подхода, при помощи которого Delphi позволяет оперировать с многомерными данными, состоит в следующем. Данные представляются в виде так называемого метакуба (или многомерного куба), где одному фактору соответствует свое измерение.
На рис. 15.1 приведен куб. В нем представлена информация по продажам. Одно измерение соответствует городам, в которых осуществлялись продажи;
второе измерение - фирмам, которым продавался товар; третье - временным периодам.
В конкретной ячейке, как правило, представляются агрегированные данные - сумма, среднее, максимальное значение - или вновь многомерные данные (кубы). Пример такого куба, содержащего информацию об отпусках товара по конкретной фирме из конкретного города для конкретного временного отрезка, представлен на рис. 15.2.
Динамическая фиксация числа измерений
При помощи компонентов Delphi, которые будут обсуждаться ниже, в источнике данных фиксируются:
• поля - измерения метакуба;
• поля, по которым должно производиться агрегирование данных (суммирование, подсчет среднего и т.д.).
Затем определяется, какие измерения показываются как столбцы, какие -как строки. После этого пользователю предоставляется таблица многомерных данных. В форме приложения может быть расположен план метакуба, то есть список измерений, где каждому измерению соответствует кнопка. Нажимая кнопку, пользователь активизирует или деактивизирует показ данных по тому или иному измерению куба.
На рис. 15.3. показана информация из пятимерного куба. Пользователь зафиксировал текущие измерения - "Покупатель", "Месяц", "Тип товара". В ячейках показывается суммарный отпуск товара.
На рис. 15.4. показан тот же куб, в котором зафиксированы три другие измерения - "Город", "Товар", "Месяц". В ячейках показывается суммарный отпуск товара.
На рис. 15.5 показан тот же куб, в котором выбраны 4 измерения - "Город". "Покупатель", "Товар", "Месяц". В ячейках показывается суммарный отпуск товара
Обзор компонентов Delphi для разработки систем принятия решений
Компоненты для разработки систем принятия решений расположены в Delphi на странице Decision Cube.
Компонент TDecisionQuery служит для определения НД, на основании которого затем будет создаваться многомерный куб. Компонент TDecisionQuery разработан специально для указанных целей, и поэтому его использование при разработке систем принятия решений является более предпочтительным, чем использование "обычных" компонентов НД - TTable и TQuery Однако и эти компоненты могут применяться для хранения данных, на которых строится метакуб.
Компонент TDecisionCube, собственно, и реализует многомерный куб данных. Он соединяется с НД (как правило, это компонент TDecisionQuery) при помощи свойства DataSet.
Компонент TDecisionSource является аналогом "источника данных", компонента TDataSource, но адаптирован для целей работы с многомерными данными. Многомерный "источник данных" соединяется с компонентом TDecisionCube при помощи свойства DecisionCube.
Компонент TDecisionPivot позволяет открывать и закрывать измерения метакуба. Для этого пользователю следует нажать (или отжать нажатую) кнопку, соответствующую конкретному измерению. Данный компонент связан с компонентом TDecisionSource при помощи свойства DecisionSource. Применение этого компонента необязательно, поскольку компонент TDecisionGrid также содержит средства для открытия и закрытия данных по измерениям куба. Это - значки "+" и "-" в заголовках столбцов и строк, соответствующих измерениям куба.
Компонент TDecisionGrid имеет в системах многомерных данных то же функциональное предназначение, что и компонент TDBGrid при работе с обычными НД. В TDecisionGrid показываются непосредственно данные из многомерного куба. Данные по одним измерениям куба выводятся построчно (горизонтально), по другим - вертикально, в виде столбцов. Компонент связан с компонентом TDecisionSource через свойство DecisionSource.
Компонент TDecisionGraph предназначен для показа графиков, источником которых служат многомерные данные. Он связан с компонентом TDecisionSource через свойство DecisionSource.
Ниже на примере будут показаны особенности использования названных компонентов.
Пример многомерных данных
Пусть имеем три таблицы БД.
Первая таблица Tovary.DB содержит сведения о товарах. В состав таблицы входят поля:
• Tovar-наименование товара; • Typ_Tovara - тип товара; • Ed_Izm - единица измерения товара;
• Zena - цена за единицу измерения товара.
Первичный ключ построен по полю Tovar.
Вторая таблица Pokup.DB содержит сведения о покупателях товара. В состав таблицы входят поля:
• Pokup - наименование покупателя; • Gorod - город, в котором расположена организация-покупатель.
Первичный ключ построен по полю Pokup.
Третья таблица Rashod.DB содержит сведения о расходе товара со склада. В состав таблицы входят поля:
• N_Rash - уникальный номер расхода; • Den - день; • Mes - месяц; • God - год даты расхода; • Tovar - наименование товара; • Pokup - наименование покупателя; • Kolvo - количество единиц отпущенного товара.
Первичный ключ построен по полю N_Rash. Таблица находится в отношении "многие-к-одному" с таблицами Tovary.DB и Pokup.DB. Для реализации целостности построены внешние индексы по полям Tovary и Pokup.
Необходимо построить многомерный куб для представления:
• общей суммы расхода; • средней суммы расхода;
по измерениям:
• товар; • город; • покупатель; • тип товара; • месяц из даты отпуска товара.
В процессе построения многомерных данных рассмотрим особенности компонентов, перечисленных в предыдущем разделе.
Использование компонента TDecisionQuery
Разместим в форме компонент TDecisionQuery с именем DecisionQuery1. Установим в свойство Database компонента имя базы данных (в нашем случае это loc_skld). В свойстве SQL определим оператор SELECT, в котором произведем внутреннее соединение таблиц Rashod, Tovary и Pokup:
SELECT P.GOROD, R.РОКUР,Т.TYP_TOVARA, R.TOVAR, R.MES,
SUM( R.KOLVO * T.ZENA) , AVG( R.KOLVO * T.ZENA)
FROM "Rashod.DB" R, "Pokup.DB" P, "Tovary.DB" T
WHERE R.POKUP= P.POKUP AND
T.TOVAR = R.TOVAR
GROUP BY P.GOROD, R.POKUP, TYP_TOVARA, R.TOVAR, R.MES
GROUP BY P.GOROD, R.POKUP, TYP_TOVARA, R.TOVAR, R.MES
Существуют следующие правила объявления измерений в многомерном кубе:
1. поля, по которым должны строиться измерения многомерного куба, перечисляются после ключевого слова SELECT;
2. поля, по которым измерений строить не нужно, в операторе SELECT в качестве возвращаемых полей результирующего набора данных не перечисляются;
3. только после полей-источников для измерений куба перечисляются агрегатные выражения - сумма, среднее, и число повторений;
агрегированные данные затем будут выводиться по указанным ранее измерениям;
4. обязательно использование раздела ORDER BY, где необходимо перечислить все поля, по которым строятся измерения куба, причем в том же порядке, в котором они следуют после ключевого слова SELECT.
Рассмотрим приведенный выше оператор SELECT. Измерения будут построены по полям:
1.GOROD;
2. POKUP;
3. TYP_TOVARA;
4.TOVAR;
5. MES.
В качестве данных по измерениям будет выводиться (по выбору) результат одной из следующих агрегаций:
1. суммарная стоимость отпущенного товара (KOLVO * ZENA);
2. средняя стоимость отпущенного товара (KOLVO * ZENA).
Компонент TDecisionQuery настолько "похож" на компонент TQuery, что не имеет отличных от него, уникальных свойств, методов и событий. Установим значение True в свойство компонента Active. Теперь набор многомерных данных активен в нашем приложении.
Редактор многомерного запроса
Отвлечемся на некоторое время от нашего примера и рассмотрим, как использовать еще одно средство компонента TDecisionQuery - редактор многомерного запроса - для построения SQL-оператора выборки многомерных данных. Отметим при помощи мыши компонент TDecisionQuery и нажмем правую кнопку мыши, а затем во всплывающем меню выберем элемент Decision Query Editor. Появится окно редактора многомерного запроса (рис. 15.6).
В списке List of Available Fields перечислены поля таблицы БД, чье имя выбрано в выпадающем списке Table. Используя кнопки с изображением стрелок, поля, по которым должны строиться измерения в многомерном кубе, перемещают в список Dimensions. Тем же способом в список Summaries перемещаются поля, по которым необходимо производить агрегацию. Тип агрегации запрашивается тут же. Это SUM (сумма), COUNT (счетчик повторений) и AVERAGE (среднее значение).
Закладка SQL позволяет просмотреть и, если необходимо, изменить текст SQL-оператора, построенного на основе наших действий. Кнопка Query Builder позволяет перейти в режим запроса по образцу (Query By Example) для построения оператора SELECT.
Использование компонента TDecisionCube
Вернемся к примеру. Разместим в форме компонент TDecisionCube с именем DecisionCube1. Занесем в свойство DataSet имя набора данных DecisionQuery1. Определим свойства куба. Для этого отыщем в инспекторе объектов свойство DimensionMap и в поле данных этого свойства нажмем кнопку (...). Появится окно редактора многомерного куба {Decision Cube Editor). Оно показано на рис. 15.7.
В списке Available Fields перечислены поля, по которым строятся измерения куба, и формулы для расчета агрегированных значений. Для текущего поля или формулы слева показываются параметры:
Display Name -
метка, которая будет показываться для измерения в компонентах TDecisionGrid, TDecisionPivot, TDecisionGraph;• Type - тип (Dimension, Sum, Average, Count);
• Active Type - определяет показываемую информацию - As Needed (показывается, когда необходимо); Active (показывается всегда); Inactive (не показывается никогда);
•Formal - формат представления значений;
•Grouping - группировка по году, кварталу, месяцу и отдельному значению;
•Initial Value - начальное значение.
Закладка Memory Control позволяет определить установки памяти:
•максимальное число измерений, сумм, ячеек (группа Cube Maximums);
•показывать только заголовки данных во время разработки приложения для экономии времени и ресурсов компьютера (группа Designer Data Options).
Определим названия измерений и агрегированных данных (поле Display Name).
Кратко рассмотрим свойства, методы и события компонента TDecisionCube.:
Свойства
property Capacity: Integer;
определяет максимальный размер кэша в байтах для хранения многомерного массива данных куба. В случае нехватки памяти возбуждается исключение ELowCapacity Error.property DataSet: TDataSet;
содержит имя НД, который указывает данные для представления в кубе. Рекомендуется в качестве НД использовать созданный специально для указанных целей компонент TDecisionQuery.property DimensionCount: Integer;
содержит текущее число измерений в кубе.property DimensionMap: TCubeDims;
определяет параметры компонента TDecisionCube, такие как число и состав измерений куба; формат представления значений по конкретному измерению; метки, которые будут соответствовать каждому измерению в компонентах TDecisionGrid и TDecisionPivot; максимальное число измерений; состав показываемых значений. Установка указанных параметров осуществляется в редакторе куба (Decision Cube Editor), который активизируется при двойном щелчке мышью на компоненте TDecisionCube в форме приложения или при нажатии кнопки в поле данных свойства DimensionMap в инспекторе объектов.property DimensionMapCount: Integer;
содержит число полей НД, которые участвуют в формировании многомерного куба.property MaxDimensions: Integer;
содержит максимальное число измерений куба.property MaxSummaries: Integer;
содержит максимальное число сумм для куба.property ShowProgressDialog: Boolean;
определяет, нужно (True) или не нужно (False) показывать прогресс-индикатор во время формирования куба.property SummaryCount: Integer;
возвращает число полей, использованных для формирования сумм.Метод procedure ShowCubeDialog; во время выполнения осуществляет вызов редактора куба, в котором могут быть изменены или переопределены свойства куба, установленные во время разработки. Более подробно см. свойство DimensionMap.
События
property OnLowCapacity: TCapacityErrorEvent;
TCapacityErrorEvent = procedure (var Action: TErrorAction) of object;
TErrorAction = ( eaFail, eaContinue ) ;
наступает в случае, когда не хватает памяти в кэше для хранения многомерных данных. Максимальный размер кэша устанавливается в свойстве Capacity.
property OnRefresh: TCubeRefreshEvent;
TCubeRefreshEvent = procedure(DataCube: TCustomDataStore; DimMap:
TCubeDims) of object;
наступает перед изменением плана куба. Это происходит после изменения параметров куба во время выполнения (см. метод ShowCubeDialog).
Использование компонента TDecisionSource
Компонент TDecisionSource выполняет в системах представления многомерных данных те же функции, что и компонент TDataSource в системах представления обычных данных, то есть служит "источником данных" для визуальных компонентов (TDecisionGrid, TDecisionGraph). Иными словами, TDecisionSource служит в качестве связующего звена между невизуальными компонентами многомерных данных (TDecosionCube) и визуальными компонентами.
Разместим в форме компонент TDecisionSource с именем DecisionSourcel. Установим в свойство DataSet значение имени компонента TDecisionCube, то есть DecisionCubel.
Свойства и события компонента TDecisionSource описаны ниже. Некоторые из них связаны с компонентами TDecisionPivot и TDecisionGrid, которые будет рассмотрены в следующих разделах.
Свойства
property DecisionCube: TDecisionCube;
содержит имя компонента TDecisionCube.property SparseCols: boolean;
определяет, показывать или нет столбцы с пустыми значениями.property SparseRows: boolean;
определяет, показывать или нет строки с пустыми значениями.События
property OnBeginPivot: TNotifyEvent;
наступает перед изменением данных в кубе при нажатии (отжатии) кнопки, соответствующей измерению куба в компоненте TDecisionPivot, или при раскрытии/свертке данных по измерению в TdecisionGrid TNotifyEvent = procedure(Sender: TObject) of object;property OnEndPivot: TNotifyEvent;
событие вызывается той же причиной, что и событие OnBeginPivot, однако наступает после изменения данных в кубе, но до отображения этих изменений в связанных с TDecisionSource компонентах. TNotifyEvent = procedure(Sender: TObject) of object;property OnLayoutChange: TNotifyEvent;
наступает после изменения схемы данных в кубе. Событие наступает после события OnBeginPivot, но перед событием OnEndPivot и только в том случае, когда данные в кубе остаются теми же. При изменении состава измерений, которое сопровождается изменением данных в кубе, наступает событие OnNewDimensions. TNotifyEvent = procedure(Sender: TObject) of object;property OnNewDimensions: TNotifyEvent;
наступает после изменения данных в кубе.TNotifyEvent = procedure(Sender: TObject) of object;
property OnStateChange: TNotifyEvent;
наступает после изменения свойств куба (компонент TDecisionCube). TNotifyEvent = procedure(Sender: TObject) of object;Создание сетки многомерных данных
Компонент TDecisionGrid служит для представления многомерных данных в виде таблицы, в которой измерениям соответствуют строки и столбцы. Ячейки TDecisionGrid заполняются данными, полученными на основе расчета агрегатных функций.
Разместим в форме компонент TDecisionGrid с именем DecisionGrid1. Установим в его свойство DecisionSource имя компонента DecisionSource1 После этого произойдет визуализация многомерных данных (рис. 15.8).
Вспомним оператор SELECT, в котором определены измерения и агрегация данных в многомерном кубе:
SELECT P.GOROD, R.POKUP,Т.TYP_TOVARA, R.TOVAR, R.MES,
SUM( R.KOLVO * T.ZENA) , AVG ( R.KOLVO * T.ZENA)
FROM "Rashod.DB" R, "Pokup.DB" P, "Tovary.DB" T
WHERE R.POKUP= P.POKUP AND
T.TOVAR = R.TOVAR
GROUP BY P.GOROD, R.POKUP, TYP_TOVARA, R.TOVAR, R.MES
Согласно данному оператору, в кубе имеются измерения по полям
1. GOROD (город покупателя);
2. POKUP (название покупателя);
3. TYP_TOVARA (тип товара);
4. TOVAR (название товара);
5. MES (месяц года, в который произошел отпуск товара).
В качестве данных по измерениям будет выводиться (по выбору) результат одной из следующих агрегаций:
1. суммарная стоимость отпущенного товара (KOLVO * ZENA);
2. средняя стоимость отпущенного товара (KOLVO * ZENA).
Как видно из рис 15.8, одни измерения показываются по горизонтали, построчно ("Покупатель", "Тип товара", "Товар"), другие - вертикально, как столбцы ("Город"). По умолчанию в ячейках показываются данные той агрегатной функции, которая в списке агрегатных функций в операторе SELECT указана первой. В нашем случае - это суммарная стоимость отпущенного товара SUM( R KOLVO * T.ZENA).
Раскрытие и закрытие данных по измерениям средствами компонента TDecisionGrid
Каждому измерению в сетке данных соответствует знак "+" или "-" Он указывается в ячейке, в которой содержится название предыдущего измерения Например, знак для измерения "Тип товара" находится в ячейке, в которой показывается название предыдущего измерения - "Покупатель".
Знак "+" сообщает, что информация по измерению в сетке данных не показывается. Знак "-", наоборот, свидетельствует о том, что данные по измерению показываются Если щелкнуть на значке мышью, значок переходит в противоположное состояние (с "+" на "-" и наоборот). При этом данные по измерению также переходят в противоположное состояние визуализации - если они показывались в сетке, то прячутся, и наоборот.
Важно помнить, что сокрытие данных по какому-либо измерению ведет к сокрытию данных по измерениям, расположенным правее от него. На рис 15 9 показан результат сокрытия данных по измерению "Тип товара" Как видно из рисунка, скрыты не только данные по измерению "Тип товара", но и по измерению"Товар".
Показ промежуточных сумм
Как видно из рис. 15 8, для каждого значения данных по измерению показываются промежуточные суммы. Их показ можно устранить. Для этого нужно щелкнуть по компоненту TDecisionGrid правой кнопкой мыши и во всплывающем меню выбрать Subtotals on/off. Повторный выбор данного элемента меню приведет к включению промежуточных сумм в сетку
Подобного же эффекта, но для конкретного измерения, можно добиться, если выбрать мышью ячейку заголовка конкретного измерения и нажать правую кнопку Выбор элемента Display Data and Subtotals приводит к показу промежуточных сумм Выбор элемента Display Data Only устраняет показ промежуточных сумм для измерения.
Те же действия осуществимы и для конкретного значения по измерению. Если щелкнуть правой кнопкой мыши по ячейке данных, соответствующих нужному измерению, то появится меню, в котором к элементам Display Data and Subtotals и Display Data Only добавится элемент Display Subtotals Only. Выбор этого элемента приводит к показу только промежуточных сумм. Установка одного из названных режимов показа относится не к измерению в целом, а к данным, касающимся того значения измерения, которое показывается в ячейке.
Все возможности, о которых идет речь в данном подразделе, доступны как во время разработки приложения, так и во время его выполнения
Исключение и включение измерения в состав показываемых в сетке данных
То или иное измерение может быть исключено из сетки данных или вновь добавлено в нее - как во время разработки, так и во время выполнения приложения.
Выше нами рассмотрено использование значков "+" и "-" Однако сворачивание и раскрытие данных в случае использования этих значков затрагивает и соседние измерения, расположенные справа. Механизм, о котором пойдет речь в этом подразделе, позволяет исключать или вновь включать в сетку данные, соответствующие конкретному измерению, не затрагивая при этом данных по другим измерениям
Для того чтобы скрыть данные по измерению, можно воспользоваться двумя способами.
Первый способ - щелкнуть правой кнопкой мыши по ячейке данных измерения, которое показывается построчно, и во всплывающем меню выбрать элемент Drill in to this value
Второй способ - нажать правую кнопку мыши:
• над заголовком измерения,
над пустой ячейкой в ряду ячеек сетки, расположенным над рядом, в котором показываются названия измерений;
над крайним левым (пустым) столбцом сетки, в списке измерений снять отметку с необходимого измерения.
Чтобы включить измерение в состав показываемых, нужно воспользоваться вторым способом и установить отметку у необходимого измерения.
Установка свойств показа данных по отдельному измерению
Свойство property Dimensions: TDisplayDims; представляет собой коллекцию компонентов TDisplayDim, каждый из которых позволяет управлять представлением данных, соответствующих конкретному измерению.
Рассмотрим свойства компонента TDisplayDims:
• property Count: Integer; возвращает число измерений в TDecisionGrid, то есть число компонентов TDisplayDim в коллекции TDisplayDims.
• property Items[Index: Integer]: TDisplayDim; возвращает компонент TDisplayDim с индексом Index. TDisplayDim соответствует серии данных по одному измерению. Значение Index лежит в диапазоне 0..Count-1.
Рассмотрим свойства компонента TDisplayDim, то есть серии данных по конкретному измерению в TDecisionGrid:
•• property Alignment: TAlignment;
определяет выравнивание.•• property Color: TColor;
определяет цвет фона в ячейках.•• property DisplayName: String;
определяет текст заголовка.•• property FieldName: String;
определяет имя поля, по которому строится измерение.•• property Format: String;
определяет формат представления значений в ячейках.•• property Subtotals: Boolean;
определяет, следует ли (True) или нет (False) показывать значения промежуточных сумм по измерению.На этапе конструирования программы значения приведенных выше свойств можно установить, выбрав в инспекторе объектов свойство Dimensions. После этого на экране появится список измерений и агрегированных значений (рис. 15.10). Сделав текущим нужное измерение или агрегированное значение, в инспекторе объектов можно переопределить принятые по умолчанию значения его свойств.
Свойства и события, управляющие поведением TDecisionGrid в целом
Свойства
property CaptionColor: TColor;
определяет цвет фона для заголовков столбцов и строк.property CaptionFont: TFont;
определяет шрифт для заголовков столбцов и строк.property CelIs[ACol, ARow: Integer]: String;
возвращает в строковом виде содержимое ячейки на пересечении столбца АСоl и строки ARow.ro property ColCount: Integer;
возвращает текущее число столбцов.property DataCoIor: TColor;
определяет цвет фона во всех ячейках.property DataFont: TFont;
определяет шрифт во всех ячейках.property DataSumColor: TColor;
определяет цвет фона - только в ячейках, где показываются суммы.property DecisionSource: TDecisionSource;
содержит имя компонента TDecisionSource.property DefauItColWidth: Integer;
определяет умалчиваемую ширину (в пикселях) всех ячеек TDecisionGrid. Установка нового значения этого свойства приводит к изменению ширины сразу всех ячеек компонента.property DefaultRowHeight: Integer;
определяет высоту (в пикселях) всех ячеек TDecisionGrid Установка нового значения свойства приводит к изменению высоты сразу всех ячеек компонента.property FixedCols:Integer;
возвращает число столбцов, используемых для показа заголовков измерений и меток данных, то есть для показа служебной информации.rо property FixedRows:Integer;
возвращает число строк, используемых для показа служебной информации.property GridLineColor: TColor;
определяет цвет линий, отделяющих ячейки друг от друга.property GridLineWidth: Integer;
определяет в пикселях ширину линий, отделяющих ячейки друг от друга.property LabelColor: TColor;
определяет цвет фона ячеек, в которых показываются заголовки столбцов и строк.property LabelFont: TFont;
определяет шрифт ячеек, в которых показываются заголовки столбцов и строк.property LabelSumColor: TColor;
определяет цвет фона ячеек, в которых показываются заголовки сумм.property Options: TDecisionGridOptions;
TDecisionGridOptions = set of TDecisionGridOption; представляет собой множество, которое определяет параметры показа в TDecisionGrid. В множество могут входить значения:cgGridLines -
выводятся вертикальные и горизонтальные линии, отделяющие ячейки друг от друга.cgOutliner -
заголовки измерений куба содержат знаки "+" и "-", которые позволяют открывать или закрывать измерения без помощи компонента TDecisionPivot.cgPivotable -
разрешается перетаскивать измерения способом darg and drop.ro property RowCount:Integer;
возвращает число строк в TDecisionGrid.property Totals: Boolean;
определяет, показываются (True) или нет (False) значения подсумм по измерениям.События
TDecisionDrawCeIlEvent = procedure (Sender: TObject; Col, Row: Longint;
var Value: string; var AFont: TFont; var AColor: TColor; AState: TGridDrawState;
ADrawState: TDecisionDrawState) of Object;
наступает при прорисовке ячейки в TDecisionGrid. Параметры:
Соl - номер столбца;
Row - номер строки;
Value - символьное представление значения, показываемого в ячейке;
AFont -
шрифт, которым показывается значение в ячейке;A Color - цвет фона в ячейке;
AState -
состояние ячейки. TGridDrawState = set of (gdSelected, gdFocused, gdFixed);A DrawState - множество, определяющее вид показываемых в ячейке данных. Может включать в себя значения:
• dsGroupStart - ячейка - первая строка или первый столбец для измерения. В этом случае в множество ADrawState входят также значения dsRow Value или dsCol Value.
• dsRowCaption - в ячейке показывается заголовок строки, то есть имя измерения, значения которого показываются горизонтально.
• dsColCaption - в ячейке показывается заголовок столбца, то есть имя измерения, значения которого показываются вертикально.
• dsSum - в ячейке показывается метка или значение промежуточной суммы. В этом случае в множество ADrawState входят также значения dsRow Value, dsCol Value и dsData.
• dsRow Value - в ячейке показывается одно из значений измерения (метка), а не собственно данные. Название измерения в этом случае расположено в ячейке слева.
• dsColValue - в ячейке показывается одно из значений измерения (метка), а не собственно данные. Название измерения в этом случае расположено в ячейке сверху.
• dsData - в ячейке показываются только данные.
• dsOpenAfter - ячейка имеет знак "+" в правой части, использующийся для раскрытия данных по следующему измерению. В этом случае в множество ADrawState входят также значения dsRowCaption, dsColCaption, dsRow Plus и dsColPlus.
• dsCloseAfter - ячейка имеет знак "-" в правой части, использующийся для сокрытия данных по следующему измерению. В этом случае в множество ADrawState входят также значения dsRowCaption, dsColCaption, dsRowPlus и dsColPlus.
• dsRowIndicator - ячейка используется только для показа знаков "+" или "-" и располагается в самом левом столбце TDecisionGrid. dsColIndicalor - ячейка используется только для показа знаков "+" или "-" и располагается в самой верхней строке TDecisionGrid.
• dsRowPlus - ячейка, определяемая dsRowIndicator, содержит значение "+". Появляется только совместно с dsRowIndicator.
• dsColPlus - ячейка, определяемая dsColIndicator, содержит значение "+". Появляется только совместно с dsColIndicator.
• dsNone - у ячейки нет никакого предназначения.
Пример.
Показать на красном фоне те значения в области данных (но не значений измерений!), где значение больше 2000. Сделать это для всех ячеек, кроме текущей (пусть в ней используется стандартный фон выбора - по умолчанию это синий). Для тех полей области данных, которые содержат пустые значения, выводить 0.procedure TForm1.DecisionGrid1DecisionDrawCell (Sender: TObject; Col, Row: Integer; var Value: String; var aFont: TFont; var aColor:
TColor;
AState: TGridDrawState; aDrawState: TDecisionDrawState) ;
begin
IF dsData in aDrawState THEN begin
IF Value = " THEN Value := '0';
IF not(gdFocused in AState)AND (StrToInt(Value) > 2000) THEN
AColor := cIRed;
END;//if
end;
property OnDecisionExamineCell: TDecisionExamineCellEvent;
TDecisionExamineCellEvent = procedure (Sender: TObject; ICol, IRow: Longint; ISum: Integer; const ValueArray: TSmallIntArray) of Object;
наступает, когда пользователь, находясь в ячейке данных, нажимает правую кнопку мыши. Параметры:
ICol - номер столбца ячейки;
IRon - номер строки ячейки;
ISum - номер текущей суммы;
ValueArray -
массив координат в координатной сетке TDecisionSource (а не TDecisionGrid). Для получения имени измерения, соответствующего конкретному элементу массива, следует использовать метод GetDimensionName.property OnTopLeftChanged: TNotifyEvent;
наступает при скроллинге данных в сетке TDecisionGrid.TNotifyEvent = procedure(Sender: TObject) of object;
Создание переключателя активных измерений
Компонент TDecisionPivot позволяет динамически определять текущие измерения куба, а также выбирать содержимое ячеек данных Каждому измерению куба в компоненте TDecisionPivot соответствует кнопка. Когда кнопка нажата, измерение в составе куба является открытым, то есть данные, соответствующие измерению, показываются в кубе Когда кнопка, соответствующая измерению, не нажата (отжата), данные, соответствующие измерению, в кубе не показываются Агрегатным функциям соответствует отдельная кнопка. С ее помощью в ячейках могут быть показаны данные, соответствующие конкретной агрегатной функции
Разместим в форме компонент TDecisionPivot (имя DecosionPivot1) Установим в его свойство DecisionSource имя компонента DecisionSource1.
Общий вид окна приложения показан на рис.15 11
Изменение способа показа данных по измерению
Из рисунка 15.11 видно, что кнопки в компоненте TDecisionPivot размещены в трех областях
В левой области размещена кнопка "Стоимость (сумма)". Это кнопка предназначена для выбора агрегатных функции По умолчанию в качестве текущей агрегатной функции берется первая из функций, определенных в операторе SELECT (компонент TDecisionQuery). Чтобы сменить агрегатную функцию, нужно нажать кнопку и из появившегося списка выбрать нужную функцию
Средняя область компонента TDecisionPivot содержит кнопки измерений, данные по которым показываются построчно. В нашем случае это кнопки, соответствующие измерениям "Тип товара", "Покупатель", "Товар", "Месяц".
Правая часть компонента TDecisionPivot содержит кнопки измерений, данные по которым показываются в виде столбцов. В нашем случае это кнопка, соответствующая измерению "Город".
Для того чтобы перевести кнопку из одной области в другую, следует щелкнуть по ней правой кнопкой мыши и в всплывающем меню выбрать элемент Move to roh Area (при переводе измерения в построчный показ) или Move to Column Area (при переводе измерения в показ по столбцу).
Фиксация значения в измерении
По умолчанию по измерению показываются все его данные. Например, по измерению "Покупатель" показываются все покупатели. Однако часто бывает необходимо показывать данные не по всем, а по конкретному покупателю. Для этого следует переместить указатель мыши на кнопку соответствующего измерения и нажать правую кнопку мыши, а затем выбрать элемент всплывающего меню Drilled in.
Кнопка "Покупатель" изменит свой заголовок на "Покупатель. All Values". Теперь, чтобы зафиксировать конкретного покупателя, следует нажать правую кнопку мыши и в появившемся списке выбрать название конкретного покупателя. При этом его имя будет показываться в кнопке, соответствующей измерению "Покупатель", а данные в кубе будут сечением по координате измерения "Покупатель", соответствующей конкретному покупателю (рис.15.12).
Для того чтобы вновь показывать все значения по измерению (например, всех покупателей по измерению "Покупатель"), следует установить указатель мыши на кнопку, соответствующую измерению, нажать правую кнопку мыши и во всплывающем меню снять отметку с режима Drilled in.
Свойства, управляющие представлением в форме компонента TDecisionPivot
property ButtonAutoSize: Boolean;
указывает, следует ли автоматически согласовывать размеры кнопок, соответствующих измерениям куба в компоненте TDecisionPivot, с размером самого компонента TDecisionPivot (значение True) или, независимо от размеров компонента, всегда поддерживать одни и те же размеры кнопок (значение False).
property ButtonHeight: Integer;
определяет высоту (в пикселях) кнопок в компоненте TDecisionPivot (только для случая, когда свойство ButtonAutoSize = False).property ButtonSpacing: Integer;
определяет расстояние (в пикселах) между кнопками. property ButtonWidth: Integer;определяет ширину (в пикселях) кнопок в компоненте TDecisionPivot (только для случая, когда свойство ButtonAutoSize = False).
property DecisionSource: TDecisionSource;
содержит имя компонента TDecisionSource.property GroupLayout: TDecisionButtonPosition;
TDecisionButtonPosition = (xtHorizontal, xtVertical, xtLeftTop); определяет способ расположения кнопок измерений в компоненте TDecisionPivot:xtHorizontal -
горизонтальное;xtVertical - вертикальное;
xtLeftTop -
кнопки, соответствующие измерениям, которые выводятся в виде строк, помещаются слева внизу; кнопки, соответствующие измерениям-столбцам - вверху и кнопки, соответствующие суммам - вверху слева.property Groups: TDecisionPivotOptions;
TDecisionPivotOptions = set of TDecisionPivotOption;
TDecisionPivotOption = (xtRows, xtColumns, xtSummaries);
множество, которое определяет, какие кнопки входят в TDecisionPivot. В состав множества могут входить значения:
xtRows - кнопки измерений куба, которые выводятся как строки;
xtColumns -
кнопки измерений куба, которые выводятся как столбцы;xtSummaries -
кнопки, соответствующие суммам. property GroupSpacing: Integer;определяет расстояние (в пикселах) между группами кнопок, соответствующих измерениям по строкам, по столбцам и суммам.
Архитектуры "файл-сервер" и "клиент-сервер"
Базы данных на персональных компьютерах развивались по направлению от настольных (desktop), или локальных приложений, когда реально с БД могло работать одно приложение, до систем коллективного доступа к БД.
Локальное приложение устанавливалось на единичном персональном компьютере; там же располагалась и БД, с которой работало данное приложение. Однако необходимость коллективной работы с одной и той же БД повлекла за собой перенос БД на сетевой сервер. Приложение, работающее с БД, располагалось также на сервере. Менее характерным был другой способ, заключавшийся в хранении приложения, обращавшегося к БД, на конкретном компьютере пользователей ("клиентов"). Были выпущены новые версии локальных СУБД, которые позволяли создавать приложения, одновременно работающие с одной БД на файловом сервере. Основной проблемой была явная или неявная обработка транзакций и неизбежно встающая при коллективном доступе проблема обеспечения смысловой и ссылочной целостности БД при одновременном изменении одних и тех же данных.
В ходе эксплуатации таких систем были выявлены общие недостатки файл-серверного подхода при обеспечении многопользовательского доступа к БД. Они состоят в следующем:
• вся тяжесть вычислительной нагрузки при доступе к БД ложится на приложение клиента, что является следствием принципа обработки информации в системах "файл-сервер": при выдаче запроса на выборку информации из таблицы вся таблица БД копируется на клиентское место, и выборка осуществляется на клиентском месте;
• локальные СУБД используют так называемый "навигационный подход", ориентированный на работу с отдельными записями; не оптимально расходуются ресурсы клиентского компьютера и сети;
например, если в результате запроса мы должны получить 2 записи из таблицы объемом 10 000 записей, все 10 000 записей будут скопированы с файл-сервера на клиентский компьютер; в результате возрастает сетевой трафик и увеличиваются требования к аппаратным мощностям пользовательского компьютера. Заметим, что потребности в постоянном увеличении вычислительных мощностей клиентского компьютера обусловливаются не только развитием программного обеспечения как такового, но и возрастанием обрабатываемых объемов информации;
• в БД на файл-сервере гораздо проще вносить изменения в отдельные таблицы, минуя приложения, непосредственно из инструментальных средств (например, из утилиты Database Desktop фирмы Borland для файлов Paradox или dBase); подобная возможность облегчается тем обстоятельством, что, фактически, у локальных СУБД база данных понятие более логическое, чем физическое, поскольку под БД понимается набор отдельных таблиц, сосуществующих в едином каталоге на диске. Все это позволяет говорить о низком уровне безопасности - как с точки зрения хищения и нанесения вреда, так и с точки зрения внесения ошибочных изменений;
• бизнес-правила в системах "файл-сервер" реализуются в приложении, что позволяет в разных приложениях, работающих с одной БД, проектировать взаимоисключающие бизнес-правила; смысловая целостность информации при этом может нарушаться;
• недостаточно развитый аппарат транзакций для локальных СУБД служит потенциальным источником ошибок как с точки зрения одновременного внесения изменений в одну и ту же запись, так и с точки зрения отката результатов серии объединенных по смыслу в единое целое операций над БД, когда некоторые из них завершились успешно, а некоторые - нет; это может нарушать ссылочную и смысловую целостность БД.
Приведенные недостатки решаются при переводе приложений из архитектуры "файл-сервер " в архитектуру "клиент-сервер ", которая знаменует собой следующий этап в развитии СУБД. Характерной особенностью архитектуры "клиент-сервер" является перенос вычислительной нагрузки на сервер БД (SQL-сервер) и максимальная разгрузка приложения клиента от вычислительной работы, а также существенное укрепление безопасности данных - как от злонамеренных, так и просто ошибочных изменений.
БД в этом случае помещается на сетевом сервере, как и в архитектуре "файл-сервер", однако прямого доступа к БД из приложений не происходит. Функции прямого обращения к БД осуществляет специальная управляющая программа - сервер БД (SQL-сервер), поставляемая разработчиком СУБД.
Взаимодействие сервера БД и приложения-клиента происходит следующим образом: клиент формирует SQL-запрос и отсылает его серверу. Сервер, приняв запрос, выполняет его и результат возвращает клиенту. В клиентском приложении в основном осуществляется интерпретация полученных от сервера данных, реализация интерфейса с пользователем и ввод данных, а также реализация части бизнес-правил.
Преимущества архитектуры "клиент-сервер":
• большинство вычислительных процессов происходит на сервере; таким образом снижаются требования к вычислительным мощностям компьютера клиента;
• снижается сетевой график за счет посылки сервером клиенту только тех данных, которые он запрашивал; например, если необходимо сделать из таблицы объемом 10 000 записей выборку, результатом которой будут всего 2 записи, сервер выполнит запрос и перешлет клиенту НД из 2 записей;
• упрощается наращивание вычислительных мощностей в условиях развития программного обеспечения и возрастания объемов обрабатываемых данных: проще и чаще дешевле усилить мощности на сетевом сервере или полностью заменить сервер на более мощный, нежели наращивать мощности или полностью заменять 100-500 клиентских компьютеров;
• БД на сервере представляет собой, как правило, единый файл, в котором содержатся таблицы БД, ограничения целостности и другие компоненты БД. Взломать такую БД, даже при наличии умысла, тяжело; значительно увеличивается защищенность БД от ввода неправильных значений, поскольку сервер БД проводит автоматическую проверку соответствия вводимых значений наложенным ограничениям и автоматически выполняет необходимые бизнес-правила; кроме того, сервер отслеживает уровни доступа для каждого пользователя и блокирует осуществление попыток выполнения неразрешенных для пользователя действий, например, изменения или просмотр таблиц; все это позволяет говорить о значительно более высоком уровне обеспечения безопасности БД и ссылочной и смысловой целостности информации;
• сервер реализует управление транзакциями и предотвращает попытки одновременного изменения одних и тех же данных; различные уровни изоляции транзакций позволяют определить поведение сервера при возникновении ситуаций одновременного изменения данных;
• безопасность системы возрастает за счет переноса большей части бизнес-правил на сервер; падает удельный вес противоречащих друг другу бизнес-правил в клиентских приложениях, выполняющих разные действия над БД; определить такие противоречивые бизнес-правила в приложениях клиента все еще можно, однако намного труднее их выполнить ввиду автоматического отслеживания сервером БД правильности данных.
Для реализации архитектуры применяют так называемые "промышленные" ("удаленные") СУБД, такие как Borland InterBase, Oracle, Informix, Sybase, DB2, MS SQL Server.
В дальнейшем реализация архитектуры "клиент-сервер" будет рассматриваться для сервера Borland InterBase. Объяснить такой выбор нетрудно. Во-первых, InterBase - "родной" сервер для Delphi (поэтому для доступа к нему не нужно устанавливать дополнительных драйверов SQL Links, что необходимо при работе из приложений, написанных на Delphi, с Oracle, Sybase и другими СУБД). Во-вторых, в поставку Delphi входит локальный (однопользовательский, на 2 одновременных подключения) сервер Local Borland InterBase. Доступен также и InterBase для Windows 95 на 4 пользователя.
Локальный InterBase может использоваться для отладочных целей. После того, как приложение отлажено на локальной версии SQL-сервера, происходит масштабирование приложения (upsizing}. БД переносится на сетевой сервер, а изменения в клиентских приложениях при этом минимальны - необходимо изменить псевдоним БД и, может быть, скорректировать некоторые параметры соединения приложения с сервером.
При переносе приложений, ранее разработанных для применения в архитектуре "файл-сервер", требуется не только частично или полностью переписывать приложения клиентов, но и преобразовывать локальную БД в серверную. Для этого под управлением серверной СУБД (например, InterBase) создают БД на сервере, куда затем "перекачивают" данные из локальных СУБД, реализованных, например, с помощью Paradox. Основная проблема, встающая в этом случае - несовместимость некоторых форматов данных или их отсутствие. Например, InterBase не поддерживает поля типа Boolean (Logical), и их необходимо реализовывать при помощи столбцов типа CHAR(l); InterBase не поддерживает автоинкрементные поля Paradox - для обеспечения уникальности значений в числовых полях в БД InterBase используют генераторы и т.д. При возникновении подобных проблем следует изучить вопросы совместимости типов данных локальной СУБД и выбранной серверной СУБД.
SQL-сервер Borland InterBase и его основные компоненты
SQL-сервер Borland InterBase является "промышленной" СУБД, предназначенной для хранения и выдачи больших объемов данных при использовании архитектуры "клиент-сервер" в условиях одновременной работы с БД множества клиентских приложений. Масштаб информационной системы при этом произволен - от системы уровня рабочей группы (под управлением Novell Netware или Windows NT на базе IBM PC) до системы уровня большого предприятия (на базе серверов IBM, Hewlett-Packard, SUN),
Рассмотрим ряд компонентов InterBase, использование которых обеспечивает максимальную вычислительную разгрузку клиентского приложения и гарантирует высокую безопасность и целостность информации.
Для задания ссылочной и смысловой целостности в БД определяются:
• отношения подчиненности между таблицами БД путем определения первичных (PRIMARY) ключей у родительских и внешних (FOREIGN) ключей у дочерних таблиц;
• ограничения на значения отдельных столбцов путем определения ограничений (CONSTRAINT) на значение домена или столбца; при этом условия ограничений могут быть весьма разнообразны - от требования попадания значения в определенный диапазон или соответствия маске до определенного отношения с одной или несколькими записями из другой таблицы (или многих таблиц) БД,
• бизнес-правила при помощи триггеров (TRIGGER) - подпрограмм, автоматически выполняемых сервером до или (и) после события изменения записи в таблице БД;
• уникальные значения нужных полей путем создания и использования генераторов (GENERATOR).
Для ускорения работы клиентских приложений с удаленной БД могут быть определены хранимые процедуры (STORED PROCEDURE), которые представляют собой подпрограммы, принимающие и возвращающие параметры и могущие выполнять запросы к БД, условные ветвления и циклическую обработку. Хранимые процедуры пишутся на специальном алгоритмическом языке. В хранимых процедурах программируются часто повторяемые последовательности запросов к БД. Текст процедур хранится на сервере в откомпилированном виде. Преимущества в использовании хранимых процедур очевидны:
• отпадает необходимость синтаксической проверки каждого запроса и его компиляции перед выполнением, что убыстряет выполнение запросов;
• отпадает необходимость реализации в приложении запросов, определенных в теле хранимых процедур;
• увеличивается скорость обработки транзакций, т.к. вместо подчас длинного SQL-запроса по сети передается относительно короткое обращение к хранимой процедуре.
В составе записи БД могут определяться blob-поля (Binary Large Object, большой двоичный объект), предназначенные для хранения больших объемов данных в виде последовательности байтов. Таким образом могут храниться текстовые и графические документы, файлы мультимедиа, звуковые файлы и т.д. Интерпретация BLOB-поля выполняется в приложении, однако разработчик может определить так называемые BLOB-фильтры для автоматического преобразования содержимого BLOB-поля к другому виду.
InterBase дает возможность использовать определяемые пользователем функции (User Defined Function, UDF), в которых могут реализовываться функциональности, отсутствующие в стандартных встроенных функциях InterBase (вычисление максимума, минимума, среднего значения, преобразование типов и приведение букв к заглавным). Например, в UDF можно реализовать извлечение из значения даты номера дня, года; определение длины символьного значения; усечение пробелов;
разные математические алгоритмы и другое. Функция пишется на любом алгоритмическом языке, позволяющем разрабатывать DLL (библиотеки динамического вызова), например на Object Pascal.
InterBase может посылать уведомления клиентским приложениям о наступлении какого-либо события (EVENT). Одновременно работающие приложения могут обмениваться сообщениями через сервер БД, вызывая хранимые процедуры, в которых реализована инициация нужного события.
Для обеспечения быстроты выполнения запросов и снятия с клиентского приложения необходимости такие запросы выдавать в БД можно определить виртуальные таблицы (или просмотры, VIEW), в которых объединяются записи из одной или более таблиц, соответствующих некоторому условию. Работа с просмотром из клиентского приложения ничем не отличается от работы с обычной таблицей. Поддерживает просмотр сервер, реагируя на изменение данных в БД. Просмотры могут быть изменяемыми и не допускающими внесения в них изменений.
Для доступа к БД используется утилита Windows Interactive SQL (WISQL). Она работает с БД напрямую через InterBase API, минуя BDE. В WISQL можно выдавать любые запросы, будь то создание БД, таблиц, изменение структуры данных, извлечение данных из БД или их изменение, а также назначение прав доступа к информации для отдельных пользователей.
Для управления SQL-сервером в целом и отдельными БД в частности используется утилита InterBase Server Manager. Здесь можно определять параметры SQL-сервера, производить сохранение, восстановление БД, сборку "мусора", определять новых пользователей, их пароли и т д.
Для просмотра БД, работы с таблицами, индексами, доменами, ограничениями и др. могут использоваться утилиты Database Desktop (весьма ограниченно) и SQL Explorer.
Для просмотра и анализа реальных процессов, происходящих на сервере при реализации пользовательского запроса, используется утилита SQL Monitor.
InterBase: некоторые технические характеристики
Приведем некоторые технические характеристики сервера (К -1024 байта)
Характеристика | Значение |
Максимальный размер одной БД | Рекомендуется не выше 10 Гбайт. Однако известны случаи объема одной БД в 10-20 Гбайт |
Максимальное число таблиц в одной БД | 65,536 |
Максимальное число полей (столбцов) в одной таблице | 1000 |
Максимальное число записей в одной таблице | Не ограничено |
Максимальная длина записи | 64 К (не считая полей BLOB) |
Максимальная длина поля | 32 К (кроме полей BLOB) |
Максимальная длина поля BLOB | Не ограничена |
Максимальное число индексов в БД | 65,536 |
Максимальное число полей в индексе | 16 |
Максимальное число вложенностей SQL-запроса | 16 |
Максимальный размер хранимой процедуры или триггера | 48 К |
Максимальное количество UDF в базе данных | Длина имени UDF - не более 31 символов. Каждый UDF должен иметь уникальное имя. Максимальное число UDF ограничивается только требованием уникальности имени |
Историческая справка: InterBase был разработан в начале 80-х годов группой разработчиков из американской корпорации DEC. В дальнейшем разработка данного продукта велась независимыми компаниями InterBase Software и впоследствии слившейся с ней Ashton-Tate. Borland приобрела права на InterBase у Ashton-Tate после слияния с нею.
InterBase активно используется в США в государственном и военном секторах, что, видимо, и стало преградой для движения InterBase в Россию. В России InterBase используется с 1993 г., но интерес к этому SQL-серверу возрос только в последнее время, в связи с включением его локальной (или 4-х пользовательской версии) в состав Delphi Client/Server Suite. Внимание разработчиков БД и приложений InterBase привлек, во-первых, потому, что это "родной" продукт Borland (а средства разработки приложений этой компании давно зарекомендовали себя с положительной стороны), во-вторых, потому, что InterBase весьма прост в установке, настройке и - главное - в администрировании по сравнению с другими SQL-серверами, и в-третьих, потому, что он обладает прекрасными функциональными возможностями.
Физическая организация базы данных InterBase
База данных InterBase состоит из последовательно начиная с 0 пронумерованных страниц. Нулевая страница является служебной и содержит информацию, необходимую для соединения с БД
Размер страницы - 1 (по умолчанию), 2, 4 или 8 Кбайт. Размер страницы устанавливается при создании БД, но может быть изменен при сохранении и восстановлении БД. Одна страница читается сервером за один логический доступ к БД. Поэтому размер страницы рекомендуется делать равным размеру кластера диска, однако в зависимости от того, какие операции чаще выполняются для БД - операции чтения или записи, - рекомендуется соответствующим образом изменять размер страницы, учитывая также длину записи и наличие BLOB.
Объем буфера ввода-вывода для операций чтения-записи определяется в количестве страниц (по умолчанию - 75). Если БД будет чаще читаться, объем буфера следует увеличить. Если в нее будет чаще осуществляться запись, размер буфера следует уменьшить.
В InterBase поддерживается многоверсионная структура записей. При изменении записи какой-либо транзакцией создается новая версия записи, куда помимо данных записывается номер транзакции и указатель на предыдущую версию записи. Старая версия записи помечается как измененная; ее указатель на следующую версию записи указывает на вновь созданную версию данной записи. Каждая стартующая транзакция работает с последней версией записи, изменения для которой подтверждены. Таким образом, параллельно работающие с БД транзакции всегда используют разные версии записей, что позволяет снимать блокировки для клиентских приложений, одновременно работающих с одними и теми же данными в БД. Более подробно об этом рассказано в разделе, посвященном управлению транзакциями. При удалении записи она также физически не удаляется с диска, а помечается как удаленная до тех пор, пока не завершена хотя бы одна активная транзакция, использующая эту запись.
InterBase располагает на одной странице БД версии одной записи таблицы БД. После удаления записей на странице образуются "дырки". При добавлении новой записи анализируется размер максимальной дырки и, если он меньше длины добавляемой записи, происходит компрессия страницы, в процессе которой "дырки" объединяются. Если суммарной "дырки" не хватает для размещения новой записи, та записывается с новой страницы. Загрузка страницы считается нормальной в случае, если "дырки" занимают не более 20% объема страницы.
Выделение страниц никак не оптимизировано. На отдельной служебной странице БД хранятся номера всех свободных страниц. При выделении страниц не предпринимается никаких действий по выделению непрерывных страниц для хранения записей одной таблицы БД, а выделяется первая страница в списке свободных. Если свободной страницы нет, добавляется новая в конец БД. Только в этом случае размер БД возрастает.
Многоверсионная структура записей и неоптимальное выделение страниц ведет к высокой фрагментации БД и как следствие - к замедлению работы с БД. Поэтому необходимо периодически производить дефрагментацию.
Дефрагментированная БД характеризуется расположением записей таблиц БД на непрерывных страницах и отсутствием "мусора". Под мусором понимаются версии записей, с которыми не работает никакая активная транзакция. Известно, что если транзакции, использующие не последнюю версию записи, завершились, никакая другая транзакция из вновь стартующих не будет работать с данной версией записи, поскольку имеются более поздние версии (как минимум одна).
Существует несколько способов проведения дефрагментации.
Первый состоит в сохранении БД на дисковом носителе и последующем ее восстановлении из сделанной резервной копии. Данные действия реализуются в утилите InterBase Server Manager. Этот способ является предпочтительным, поскольку гарантирует сбор всего мусора (в момент сохранения и восстановления БД не должно быть активных подключений к БД со стороны иных пользователей и потому не может быть активных транзакций).
Второй способ состоит в автоматическом сборе мусора. Интервал (sweep interval), через который происходит сборка мусора, измеряется в транзакциях. По умолчанию автоматический сбор мусора производится через каждые 20 000 транзакций. Этот показатель может быть изменен в утилите InterBase Server Manager. Там же может быть предпринята принудительная сборка мусора. Данный способ дефрагментации БД менее предпочтителен, поскольку удаляются только те старые версии записей, для которых нет активных транзакций. В результате могут быть удалены не все старые версии. При большом числе активных транзакций процесс сборки мусора может существенно замедлить их выполнение.
Если на Вашей машине установлен InterBase (локальный или многопользовательский), его старт происходит автоматически при загрузке операционной системы. Об этом сигнализирует значок
справа на нижней панели Windows 95/NT. Щелкнув на этом значке правой кнопкой мыши, можно вызвать
вспомогательное меню. Опция Startup Configuration этого меню позволяет просмотреть и переопределить стартовые установки InterBase. Опция Shutdown завершает работу SQL-сервера. Опция Properties позволяет просматривать свойства InterBase и текущей сессии, например число активных подключений и число используемых БД.Delphi и InterBase
Delphi Client/Server Suite предназначена для написания приложений, работающих как с локальными, так и с удаленными данными. В последнем случае с помощью Delphi создаются клиентские приложения (забегая вперед, заметим, что в многозвенной архитектуре с помощью Delphi создаются также и серверы приложений - промежуточное звено между приложением "тонкого" клиента и SQL-сервером).
Написание приложений, работающих с локальными СУБД в однопользовательском и многопользовательском (в архитектуре "файл-сервер") режимах, несколько отлично от написания клиентских приложений, работающих с удаленными БД в архитектуре "клиент-сервер". Выделим сначала общие черты:
• для доступа как к локальным, так и к удаленным данным могут использоваться компоненты TTable, TQuery;
• в качестве промежуточного компонента между НД и визуальными компонентами для работы с данными, используется компонент TDataSource;
• состав визуальных компонентов для работы с данными одинаков при доступе как к локальным, так и удаленным данным.
Однако необходимо помнить о следующих отличиях:
• компонент TTable для доступа к удаленным данным использовать не рекомендуется, поскольку он требует передачи всех данных результата выполнения запроса к серверу, а не только той их части, которая должна быть визуализирована (что имеет место для компонента TQuery);
изменение записей БД следует производить не методами Insert, Edit, Delete, Post, Cancel, а при помощи SQL-операторов INSERT, UPDATE, DELETE (выполняемых при посредстве компонента TQuery, метод ExecSQL);
подобная рекомендация является следствием того факта, что в отличие от доступа к локальным СУБД, где главенствует навигационный (по одной записи) доступ к данным, при обращении к удаленным БД используется язык SQL, оперирующий сразу множеством записей;
• следует уделять особое внимание управлению транзакциями и в первую очередь - выбору, адекватного потребностям приложения уровня изоляции транзакций;
• для соединения с удаленной БД всех компонентов, работающих с этой БД, следует использовать как можно меньше компонентов TDatabase (в идеале - один), определяемых в приложении явно (поскольку в этом случае можно управлять параметрами соединения с удаленной БД);
• бизнес-правила, где это возможно, следует переносить на сервер, разгружая от них клиентское приложение;
• для обращения к хранимым процедурам на сервере можно использовать компонент TStoredProc;
• следует стремиться как можно меньше загружать сетевой трафик путем явного старта и подтверждения транзакций (методы TDatabase Start Transaction, TDatabase Commit), поскольку изменения БД в рамках одной транзакции выполняются одним пакетом; когда подтверждение единичных изменений БД производится неявно стартуемыми и завершаемыми (в режиме SQLPASSTHRU = SHARED AUTOCOMMIT) транзакциями, это ведет к возрастанию графика и, как следствие, к замедлению работы, часто весьма существенному;
• следует уделять существенное внимание оптимизации запросов к БД, особенно при чтении данных (SELECT), поскольку оптимально построенный запрос может выполняться в несколько раз быстрее, чем неоптимальный, и требовать меньшего количества ресурсов для своего выполнения;
• компонент TIBEventAlerter, служащий для получения от сервера уведомления о наступлении событий, может применяться только при доступе к удаленным данным.
Приведенные выше особенности разработки клиентского приложения, так же как и особенности организации серверных БД и доступа к информации в них, будут рассмотрены в следующих разделах.
Вопросы соединения с удаленным сервером
При работе с локальным сервером достаточно установить псевдоним БД (утилита BDE Administrator) и затем использовать данный псевдоним в приложении Delphi, в компонентах TDatabase и, может быть, TTable и TQuery (когда соединение с БД производится, минуя компонент TDatabase). При доступе к локальному серверу напрямую из InterBase (например, из утилиты WISQL) указывается путь к БД (InterBase работает с БД, используя собственный API и ничего не "зная" о BDE).
При работе с удаленным сервером необходимо "прописать" его на компьютере, с которого происходит обращение к серверу. При этом не важно, будем мы работать с БД на сервере при помощи BDE (т.е. с использованием псевдонимов БД) или напрямую из InterBase, указывая путь к БД непосредственно.
Для удаленного сервера (при использовании протокола TCP/IP):
1) его IP-адрес и имя должны быть описаны в файле HOSTS, например: 10.12.0.41 spv
2) протокол доступа к InterBase должен быть описан в файле SERV ICE:
gds_db 3050/tcp Оба указанных файла находятся в каталоге WINDOWS.
Создание БД "Учет товаров на складе"
Создадим простую БД для учета товаров на складе. Рассмотрим все шаги, необходимые для этого.
На рис. 17.1 приведена модель БД, которая будет далее использоваться в примерах второй части книги.
Структура процессов, подлежащих учету в нашей БД, проста. Каждый день на склад поступают товары. Допустим, процесс прихода товаров и проблема учета такого прихода нас не интересуют. Например, сведения о приходе и количестве товара аккумулируются в другой БД. Нам необходимо лишь вести учет отпуска товаров со склада. При этом подразумевается, что если товар отпускается, он на складе есть и как минимум в том количестве, которое нужно отпустить Пусть контроль за соответствием остатков товара запросам на его продажу ведет кто-нибудь другой - неважно, программа или человек.
Таким образом, предлагаемая в качестве примера простейшая БД предназначена для механического учета отпуска товара со склада.
Каждый товар имеет уникальное название (по нему построен первичный ключ) и обладает двумя атрибутами - единицей измерения и стоимостью за единицу измерения Если один и тот же товар присутствует на складе в разных единицах измерения (например, сахар в мешках, фасованный в пакетах, фасованный в коробках и т.д.; огурцы в банках и огурцы развесные и пр.), для каждого такого случая один и тот же товар должен иметь семантически одинаковое, но синтаксически разное название, например "Огурцы баночные", "Огурцы развесные" Так сделано нами для простоты Несомненно, многие разработчики БД определят в таблице "Товары" составной первичный ключ по столбцам НАЗВАНИЕ ТОВАРА и ЕД ИЗМЕРЕНИЯ, чтобы иметь возможность указывать синтаксически одно название товара для разных случаев единиц измерения Отметим, что практическая предпочтительность того или иного подхода зависит от особенностей конкретной предметной области.
Сразу оговоримся, что цена товара не имеет тенденции изменяться во времени Товар всегда отпускается по фиксированной цене, указанной в столбце ЦЕНА ЕД. ИЗМЕРЕНИЯ для данного товара Такое условие взято нами для простоты БД, иначе мы можем погрязнуть в вопросах организации складской деятельности, вместо того, чтобы акцентировать внимание на технических аспектах разработки БД. Для тех, кто не согласен с данной оговоркой, можно предложить мысленно вернуться во времена развитого социализма, когда покупка и отпуск товаров осуществлялся по установленной свыше цене, или допустить, что цены установлены в условных единицах, не подверженных воздействию инфляции.
Договоримся, что в течение одного и того же дня один и тот же покупатель может многократно получать со склада один и тот же товар. Поэтому в таблице "Расход товара" в качестве первичного ключа не годится последовательность столбцов ДАТА РАСХОДА, НАЗВАНИЕ ТОВАРА, НАЗВАНИЕ ПОКУПАТЕЛЯ. Такие значения могут дублироваться. Поэтому введем столбец НОМЕР РАСХОДА, который должен содержать некоторый уникальный номер, и примем его в качестве первичного ключа. Вопрос, каким образом попадают в данный столбец уникальные значения, оставим открытым (необходимо использование генератора, речь о котором пойдет в этой главе ниже).
Информация собственно о покупателе не требует никаких оговорок, за исключением того, что столбец ГОРОД выделен нами из АДРЕСА для дальнейших упражнений по составлению запросов и использованию оператора SELECT. Первичным ключом в данном случае служит столбец НАЗВАНИЕ ПОКУПАТЕЛЯ.
Как видим, таблица "Расход товара" является дочерней таблицей для таблиц "Товары" и "Покупатели" и находится с ними в связи "многие-к-
одному". Поэтому между дочерней таблицей и каждой из ее родительских таблиц должна быть установлена ссылочная целостность. Для этого в таблице "Товары" определим внешний ключ по столбцу "НАЗВАНИЕ ТОВАРА", ссылающийся на первичный ключ таблицы "Товары", и внешний ключ по столбцу "НАЗВАНИЕ ПОКУПАТЕЛЯ", ссылающийся на первичный ключ таблицы "Покупатели".
Создание псевдонима удаленной БД
Пусть БД будет находиться в каталоге D:\BOOK\IB_SKLAD Заметим, что на этапе создания БД и отладки приложения мы будем использовать локальный вариант InterBase, поставляемый с Delphi Client/Server Suite Пусть создаваемая БД будет иметь имя IB_SKLAD GDB. Назначим ей псевдоним ib_skl, используя утилиту BDE Administrator (рис. 17.2). Данный псевдоним будет нам нужен для написания клиентской части приложения по учету расхода товаров со склада.
Обратим внимание на то, что для БД, которые будут содержать русскоязычную текстовую информацию в строковых столбцах с использованием набора символов WIN 1251 и порядка сортировки PXW_CYRL, необходимо сразу установить для псевдонима драйвер Pdox ANSI Cyrillic в строке LANGDRIVER.
Создание БД
Используя утилиту InterBase Windows ISQL (interactive SQL), далее называемый WISQL, создадим БД. Для этого запустим утилиту WISQL и в главном меню выберем режим File \ Create Database. Далее, в окне диалога укажем имя БД, имя пользователя SYSDBA, пароль masterkey (имя и пароль системного администратора для локального сервера InterBase), а также в окне DataBase Options - параметры БД. В нашем случае в качестве единственного параметра будет введена установка по умолчанию набора символов WIN1251 (рис. 17.3).
Соединение с БД
Перед соединением с БД необходимо установить текущий набор символов в режиме Session | Advanced Settings (рис. 17.4).
Соединение с БД производится в режиме File \ Connect to DataBase. Для соединения необходимо указать имя БД, имя пользователя и пароль, (рис. 17.5)
В случае отсутствия сообщений об ошибках, соединение с БД установлено.
Создание таблиц БД
Перед созданием таблиц должны быть реализованы SQL-операторы CREATE TABLE для каждой создаваемой таблицы. Существует два способа выполнения SQL-операторов в WISQL. Первый заключается в наборе текста оператора в окне SQL Statement. В дальнейшем набранный оператор выполняется после нажатия кнопки Run. Второй способ состоит в формировании текстового файла, который содержит от одного до нескольких SQL-операторов. Такой файл выполняется в режиме File \ Run an SQL script.
Для описываемого примера воспользуемся первым способом. Поэтапно введем и выполним в окне SQL Statement (рис. 17.6) следующие операторы:
CREATE TABLE POKUPATELI(
POKUP VARCHAR(20) NOT NULL COLLATE PXW_CYRL,
GOROD VARCHAR(12) COLLATE PXW_CYRL,
ADRES VARCHAR(20) COLLATE PXW_CYRL,
PRIMARY KEY(POKUP)
) ;
CREATE TABLE TOVARY(
TOVAR VARCHAR(20) NOT NULL COLLATE PXW_CYRL,
ED_IZM VARCHAR(lO) NOT NULL COLLATE PXW_CYRL,
ZENA INTEGER NOT NULL,
PRIMARY KEY(TOVAR)
) ;
CREATE TABLE RASHOD(
N_RASH INTEGER NOT NULL,
DAT_RASH DATE NOT NULL,
KOLVO INTEGER NOT NULL,
TOVAR VARCHAR(20) NOT NULL COLLATE PXW_CYRL,
POKUP VARCHAR(20) COLLATE PXW_CYRL,
PRIMARY KEY(N_RASH),
FOREIGN KEY(POKUP) REFERENCES POKUPATELI,
FOREIGN KEY(TOVAR) REFERENCES TOVARY
Для подтверждения выполненных действий выберите в главном меню режим File | Commit Work или введите в окне SQL Statement и выполните оператор COMMIT Если Вы по каким-либо причинам решили не запоминать изменений, выберите режим File | Rollback Work или выполните оператор ROLLBACK.
Выполните разъединение с БД, выбрав в главном меню опцию File | Disconnect from DataBase и затем выберите Еxit для выхода из W1SQL.
Соединение с БД из приложения
Соединение с БД, расположенной на сервере, осуществляется при помощи компонента TDataBase
Разместим в форме такой компонент с именем DataBase 1 и установим в его свойство AliasName значение ранее созданного псевдонима 'ib_skl' В свойство DaiaBaseName поместим значение 'm_sklad' Это имя будет в дальнейшем служить именем нашей БД в компонентах, реализующих запросы к базе данных
Для установки параметров соединения с БД вызовем в редактор свойств БД Для этого щелкнем по компоненту DataBasel правой кнопкой мыши и выберем в появившемся меню опцию Datable Editor Нажмем кнопку Defaults для получения параметров БД, принятых по умолчанию Введем пароль
PASSWORD=masterkey
затем укажем имя пользователя
USER NAME=SYSDBA
после чего снимем отметку с флажка Login Prompt, указывающего на необходимость выдачи окна ввода имени пользователя и пароля (рис. 17.7)
Нажмем кнопку Ok. Теперь, поскольку в параметрах БД указаны имя пользователя и пароль, приложение будет соединяться с БД автоматически. Для соединения с БД установим свойство Connected в True
Создание формы ввода в ТБД
Для каждой из таблиц создадим НД Для этого разместим в форме три компонента TTable с именами Tovary Table, Роkир Table и RashodTable. В свойство DalabaseName каждого из них занесем 'm_sklad', выбрав его из выпадающего списка псевдонимов БД. Как можно заметить, имя 'm_sklad' установлено в свойстве DataBaseName компонента Databasel, в то время как имя зарегистрированного в BDE Configuration Utility псевдонима на самом деле есть 'ib_skl'.
Для каждого TTable выберем имя соответствующей ТБД в выпадающем списке свойства TableName Затем откроем НД, установив True в свойство Active каждого TTable .
После этого разместим в форме три компонента TDataSource и три компонента TDBGrid и соединим каждый компонент TDataSource с одним из компонентов TTable (свойство DataSet) Затем соединим каждый из компонентов TDBGrid с одним из компонентов TDataSource (свойство DataSource). Затем настроим заголовки столбцов каждого компонента TDBGrid, используя редактор столбцов TDBGrid Для этого нужно сделать компонент TDBGrid текущим элементом разрабатываемой формы, нажать правую кнопку мыши, во всплывающем меню выбрать Columns Editor, в появившемся списке перемещаться по полям и для каждого поля в инспекторе объектов установить заголовок столбца TDBGrid в свойстве TitleCaption.
Занесем в таблицы информацию, подобранную таким образом, чтобы в дальнейшем рассмотреть широкий спектр возможностей, предоставляемый оператором SELECT (рис 17 8). Самая объемная глава второй части книги, посвященная часто используемому SQL-оператору SELECT, будет построена на использовании запросов к созданной нами учебной БД.
Создание триггеров для поддержания каскадных воздействий
В рассматриваемом варианте БД соединения дочерних и родительских таблиц (соответственно RASHOD и TOVARY, POKUPATELI) являются "жесткими". Напомним, что при реализации ссылочной целостности на уровне PRIMARY KEY - FOREIGN KEY нельзя изменять значение поля связи как в дочерней таблице, так и в родительской таблице, если для данного значения первичного ключа существуют дочерние записи.
Дпя избранной структуры данных
CREATE TABLE POKUPATELI(
POKUP VARCHAR(20) NOT NULL COLLATE PXW_CYRL,
GOROD VARCHAR(12) COLLATE PXW_CYRL,
ADRES VARCHAR(20) COLLATE PXW_CYRL,
PRIMARY KEY(POKUP)
) ;
CREATE TABLE TOVARY(
TOVAR VARCHAR(20) NOT NULL COLLATE PXW_CYRL,
ED_IZM VARCHAR(IO) NOT NULL COLLATE PXW_CYRL,
ZENA INTEGER NOT NULL,
PRIMARY KEY(TOVAR)
) ;
CREATE TABLE RASHOD(
N_RASH INTEGER NOT NULL,
DAT_RASH DATE NOT NULL,
KOLVO INTEGER NOT NULL,
TOVAR VARCHAR(20) NOT NULL COLLATE PXW_CYRL,
POKUP VARCHAR(20) COLLATE PXW_CYRL,
PRIMARY KEY(N_RASH),
FOREIGN KEY(POKUP) REFERENCES POKUPATELI,
FOREIGN KEY(TOVAR) REFERENCES TOVARY
) ;
жирным отмечены ограничения по внешнему ключу. Таким образом, если в таблице TOVARY для какого-либо товара существует расход в таблице RASHOD, изменение названия товара в таблице TOVARY будет блокировано. При попытке изменить в таблице TOVAR значение наименования товара с "Сахар" на "Сахар кусковой" (рис. 17 9) будет возбуждено исключение из-за нарушения жестких ограничений целостности (рис. 17.10)
Можно увидеть, что жесткие ограничения целостности будут нам мешать в дальнейшем, когда возникнет необходимость изменять название товара или покупателя Если мы не будем менять названия товаров и покупателей, жесткие ограничения целостности можно оставить, однако будем считать, что потребность в подобных изменениях все-таки может возникнуть
Поэтому вместо жестких ограничений целостности нам необходимо реализовать возможность каскадных воздействий - таких, что:
• при изменении значения столбца TOVAR в таблице TOVARY эти изменения должны быть отражены в дочерних записях в таблице RASHOD (если такие есть для изменяемой записи в таблице TOVARY),
• при удалении записи в таблице TOVARY должны быть удалены дочерние записи в таблице RASHOD, если они есть
Для достижения этой цели нужно удалить не устраивающие нас ограничения целостности между таблицами TOVARY и RASHOD и затем определить в БД триггеры, реализующие каскадные воздействия
Для удаления внешнего ключа FOREIGN KEY(TOVAR) REFERENCES TOVARY необходимо удалить ограничение ссылочной целостности, определяемое этим ключом Однако в явном виде ограничение не было определено, например
CONSTRAINTS TOV_RASH FOREIGN KEY(TOVAR) REFERENCES TOVARY
где TOV_RASH есть имя ограничения Поэтому необходимо установить системное имя, присвоенное InterBase данному ограничению внешнего ключа Это системное имя, INTEG_32, можно увидеть в сообщении, выдаваемом при возбуждении исключения по нарушению жесткой ссылочной целостности (см выше) Следует убедиться, что INTEG_32 действительно есть ограничение целостности, которое следует удалить Для этой цели необходимо воспользоваться утилитой SQL Ex plorer, найдя для таблицы RASHOD узел Refrential Constraint's (рис 17.11)
Искомое нами ограничение имеет имя INTEG.32 Удалим его Для этого запустим WISQL, соединимся с БД и выполним оператор
ALTER TABLE RASHOD
DROP CONSTRAINT INTEG_32;
Затем создадим триггеры, реализующие каскадное изменение в таблице RASHOD при изменении названия товара в таблице TOVARY
CREATE TRIGGER BU_TOVARY FOR TOVARY
ACTIVE
BEFORE UPDATE
AS
BEGIN
IF (OLD.TOVAR 0 NEW.TOVAR) THEN
UPDATE RASHOD
SET TOVAR = NEW.TOVAR
WHERE TOVAR = OLD.TOVAR;
END
и каскадное удаление дочерних записей в таблице RASHOD при удалении родительской записи в таблице TOVARY:
CREATE TRIGGER AD_TOVARY FOR TOVARY
ACTIVE
AFTER DELETE
AS
BEGIN
DELETE FROM RASHOD
WHERE RASHOD.TOVAR =TOVARY.TOVAR;
END
Теперь, при изменении наименования товара в таблице TOVARY произойдут каскадные изменения наименования товара в дочерних записях таблицы RASHOD (рис 17 12 и 17 13)
Использование генератора
В таблице RASHOD первичный индекс построен по столбцу N_RASH (номер события расхода товара со склада) По этому столбцу построен первичный ключ, поскольку никакие другие столбцы или их комбинации не могут уникально идентифицировать запись, т к в принципе допускается, что один и тот же покупатель может в течение одной даты не только произвести несколько закупок одного и того же товара, но в двух или более таких закупках приобрести одинаковое количество одного и того же товара
Поэтому в столбце N_RASH должны содержаться уникальные значения Известно, что у таблиц InterBase нет столбца типа "автоинкремент", чье значение автоматически увеличивается при добавлении новой записи (как, например, у таблиц Paradox). Однако в InterBase есть генераторы, всякий раз возвращающие уникальные значения при обращении к ним при помощи функции GEN_ID Для начала определим сам генератор и установим его стартовое значение в 1, выполнив в WISQL операторы
CREATE GENERATOR RASHOD_N_RASH;
SET GENERATOR RASHOD_N_RASH TO 1;
Затем определим для БД хранимую процедуру
CREATE PROCEDURE GET_N_RASH
RETURNS (NR INTEGER)
AS
BEGIN
NR = GEN_ID(RASHOD_N_RASH,1) ;
END
В приложении, используемом для ввода информации в таблицы TOVAR, POKUPATELI, RASHOD, определим компонент StoredProc1 для вызова хранимой процедуры GET_N_RASH Определим свойства этого компонента свойству DatabaseName присвоим значение 'in_skl' (значение свойства DatabaseName компонента Database 1), свойству StoredProcName присвоим значение 'GET_N_RASH' (имя хранимой процедуры)
Далее, вызвав редактор параметров компонента StoredProcI (щелкнув по нему правой кнопкой мыши и выбрав из всплывающего меню опцию Define Parameters), убедимся, что имя и тип выходного параметра процедуры установлены правильно (рис 17.14)
Для компонента RashodTable (тип TTable), ассоциированного с таблицей RASHOD, определим обработчик события AfterInsert, наступающего немедленно после перевода RashodTable в состояние dslnsert:
procedure TForm!.RashodTableAfterInsert(DataSet: TDataSet) ;
begin
StoredProc!.ExecProc;
RashodTable.FieldByName('N_RASH').Value := StoredProcI.ParamByName('NR').Valued-end;
ЗАМЕЧАНИЕ.
Тот же код вместо обработчика события AfterInsert можно поместить в обработчик события OnNewRecord, наступающего также после перевода RashodTable в состояние dslnsert; обработчик этого события обычно используется для занесения умалчиваемого значения в столбцы добавляемой записи, что имеет место до того, как значения столбцов вновь добавляемой записи будут визуализированы и доступны для изменения пользователем.Теперь при переводе RashodTable в состояние dslnsert будет открываться компонент StoredProcI, который будет вызывать процедуру GET_N_RASH. С помощью этой процедуры определяется уникальное значение, которое затем помещается в столбец N_RASH вновь добавляемой записи. Это будет происходить до того, как вновь добавляемая запись будет показана пользователю для корректировки.
Создание приложения для занесения данных
Создадим приложение Inp dpr для занесения данных в БД "Расход товаров со склада". Для этого создадим приложение.
Установка набора символов текущей сессии
Как известно, в БД могут храниться символьные данные, использующие различные наборы символов для указания той или иной национальной кодировки. Для того, чтобы не возникало никаких проблем для обработки русскоязычных данных, перед соединением с БД (созданной с указанием DEFAULT CHARACTER SET WIN1251) необходимо выбрать элемент меню Session | Advanced Settings и установить в поле Character Set On кодировку WIN1251 (рис. 18.1), после чего нажать кнопку Ok.
Создание БД
Для создания БД необходимо выбрать элемент меню Ei/el Create Database и затем в появившемся окне ввести необходимую информацию (рис. 18.2).
Location Info - информация о расположении создаваемой БД. Local Server -данный компьютер; Remote Server - удаленный компьютер. В последнем случае необходимо указать имя сервера (поле Server) и сетевой протокол (поле Net Work Protocol). В случае использования удаленного сервера:
1) его IP-адрес и имя должны быть описаны в файле HOSTS, например: 10.12.0.41 spv
2) протокол доступа к InterBase должен быть описан в файле SERVICE: gds_db 3050/tcp
Оба указанных файла находятся в каталоге WINDOWS.
Database Name -
определяет имя создаваемой БД и полный путь к ней (поле Database). Далее необходимо ввести имя пользователя (поле User Name) и пароль (поле Password).Default Option -
параметры создаваемой БД. Именно в данном поле вводятся принимаемый по умолчанию набор символов (DEFAULT CHARACTER SET) и другие параметры, которые указываются в SQL-операторе CREATE DATABASE.Соединение с БД
Для соединения с БД необходимо выбрать элемент меню File \ Connect to Database и указать реквизиты базы данных (рис. 18.3).
Location Info -
информация о расположении создаваемой БД. Local Server -данный компьютер; Remote Server - удаленный компьютер. В последнем случае необходимо указать имя сервера (поле Server) и сетевой протокол (поле Net Work Protocol).Database Name -
определяет имя создаваемой БД и полный путь к ней (поле Database). Далее необходимо ввести имя пользователя (поле User Name} и пароль (поле Password).Выполнение SQL-операторов
После соединения с БД к ней можно адресовать запросы с помощью операторов SQL. В верхнем окне WISQL вводится SQL-оператор и для выполнения нажимается кнопка Run. Листать введенные SQL-операторы можно при помощи кнопок Previous и Next. В нижнем окне после выполнения оператора дублируется текст самого оператора и, в случае его правильности, показываются результаты выполнения запроса (рис. 18.4). Операторы могут завершаться точкой с запятой (при вводе операторов из WISQL это требование не является обязательным).
Могут выполняться все разрешенные SQL-операторы - от операторов определения метаданных (CREATE TABLE, CREATE PROCEDURE, DROP VIEW, DECLARE FUNCTION и т.д.) до непосредственно запросов к таблицам БД для чтения (SELECT) и изменения данных (INSERT, UPDATE, DELETE).
Подтверждение и откат изменений
При старте WISQL стартует неявная транзакция. Поэтому все запросы к БД в рамках сессии WISQL не актуализируются до выдачи подтверждения.
Подтверждение может выдаваться путем выполнения оператора COMMIT при выборе элемента меню File \ Commit Work. В последнем случае запрашивается подтверждение (рис. 18.5).
Подтверждение запрашивается также при разрыве соединения с БД (режим меню File \ Disconnect from Database) и при выходе из WISQL (режим меню File \ Exit).
При разрыве соединения с БД или при выходе из WISQL отказаться от изменений, произведенных в рамках текущей транзакции, можно при помощи оператора ROLLBACK путем выбора элемента меню File \ Rollback Work.
После подтверждения или отката транзакции неявно стартует новая транзакция. Явно запустить транзакцию можно, выполнив оператор SET TRANSACTION.