Смекни!
smekni.com

Разработка баз данных в Delphi

Создание базданных в Delphi


Урок1:Настройка BDE

Содержаниеурока 1:


Обзор2

СущностьBDE2

Алиасы2

Системнаяинформацияутилиты настройкиBDE 4

Заключение5


Обзор

На этом урокемы познакомимсяс ядром базданных компанииБорланд - BorlandDatabase Engine (BDE), а такженаучимся создаватьи редактироватьалиасы - механизм,облегчающийсвязь с базамиданных. Крометого, мы изучим,как конфигурироватьODBC драйверы.


СущностьBDE

Мощность игибкость Delphi приработе с базамиданных основанана низкоуровневомядре - процессоребаз данныхBorland Database Engine (BDE). Его интерфейсс прикладнымипрограммаминазываетсяIntegrated Database Application Programming Interface (IDAPI). Впринципе, сейчасне различаютэти два названия(BDE и IDAPI) и считаютих синонимами.BDE позволяетосуществлятьдоступ к даннымкак с использованиемтрадиционногоrecord-ориентированного(навигационного)подхода, таки с использованиемset-ориентированногоподхода, используемогов SQL-серверахбаз данных.Кроме BDE, Delphi позволяетосуществлятьдоступ к базамданных, используятехнологию(и, соответственно,драйверы) OpenDataBase Connectivity (ODBC) фирмыMicrosoft. Но, как показываетпрактика,производительностьсистем с использованиемBDE гораздо выше,чем оных прииспользованииODBC. ODBC драйвераработают черезспециальный“ODBC socket”, которыйпозволяетвстраиватьих в BDE.

Все инструментальныесредства базданных Borland- Paradox, dBase, Database Desktop - используютBDE. Все особенности,имеющиеся вParadox или dBase, “наследуются”BDE, и поэтому этимиже особенностямиобладает иDelphi.


Алиасы

Таблицы сохраняютсяв базе данных.Некоторые СУБДсохраняют базуданных в виденесколькихотдельныхфайлов, представляющихсобой таблицы(в основном,все локальныеСУБД), в то времякак другиесостоят изодного файла,который содержитв себе все таблицыи индексы(InterBase). Например,таблицы dBase иParadox всегда сохраняютсяв отдельныхфайлах на диске.Каталог, содержащийdBase .DBF файлы илиParadox .DB файлы,рассматриваетсякак база данных.Другими словами,любой каталог,содержащийфайлы в форматеParadox или dBase, рассматриваетсяDelphi как единаябаза данных.Для переключенияна другую базуданных нужнопросто переключитьсяна другой каталог.Как уже былоуказано выше,InterBase сохраняетвсе таблицыв одном файле,имеющем расширение.GDB, поэтому этотфайл и естьбаза данныхInterBase.

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

Для созданияалиаса запуститеутилиту конфигурацииBDE (программуbdeadmin.exe), находящуюсяв каталоге, вкотором располагаютсядинамическиебиблиотекиBDE.

Рис. 1: Главноеокно утилитыконфигурацииBDE


Главноеокно утилитынастройки BDEимеет вид,изображенныйна рис.1. Длясоздания алиасавыберите в меню“Object” пункт“New”. В появившемсядиалоговомокне выберитеимя драйверабазы данных.Тип алиасаможет бытьстандартным(STANDARD) для работыс локальнымибазами в форматеdBase или Paradox илисоответствоватьнаименованиюSQL-сервера (InterBase,Sybase, Informix, Oracle и т.д.).

Рис.2: В диалоговомокне добавлениянового алиасаможно указатьтип базы данных

После созданиянового алиасаследует датьему имя. Этоможно сделатьс помощью подпункта“Rename” меню“Object”. Однакопросто создатьалиас не достаточно.Вам нужно указатьдополнительнуюинформацию,содержаниекоторой зависитот типа выбраннойбазы данных.Например, длябаз данныхParadox и dBase (STANDARD) требуетсяуказать лишьпуть доступак данным, имядрайвера и флагENABLE BCD, которыйопределяет,транслируетли BDE числав двоично-десятичномформате (значениядвоично-десятичногокода устраняютошибки округления):

TYPE

STANDARD

DEFAULTDRIVER

PARADOX

ENABLEBCD

FALSE

PATH

c:\users\data


SQL-сервер InterBase идругие типыбаз данныхтребуют заданиябольшого количествапараметров,многие из которыхможно оставитьустановленнымипо умолчанию.


Системнаяинформацияутилиты настройкиBDE

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

Рассмотрим,например, системнуюинформациюдрайвера PARADOX:

  • NET DIR. Параметрсодержитрасположениекаталога сетевогоуправляющегофайла. Он нужендля того, чтобыобратитьсяк таблице PARADOXна сетевомдиске.

  • VERSION. Номерверсии драйвера.

  • TYPE. Тип драйвера.

  • LANGDRIVER. Языковойдрайвер, определяющиймножестводопустимыхсимволов.

  • BLOCK SIZE. Размерблока на диске,используемогодля запоминанияодной записи.

  • FILL FACTOR. Содержитпроцент отблока на текущемдиске. Параметрнужен для созданияиндексныхфайлов.

  • LEVEL. Параметропределяеттип формататаблицы, используемойдля созданиявременныхтаблиц.

  • STRICTINTEGRTY. Параметриспользованияссылочнойцелостности.Если он равенTRUE, то вы не можетеизменить таблицус ссылочнойцелостностью,а если FALSE, то можете,но рискуетенарушить целостностьданных.

Как уже отмечалосьвыше, утилитанастройки BDEсохраняет всюконфигурационнуюинформациюв файле IDAPI.CFG. Этотфайл с предустановленнымиссылками надрайверы инекоторымистандартнымиалиасами создаетсяпри установкеDelphi. Кроме того,он создаетсяпри установкефайлов редистрибуцииBDE (т.е. когда ВыпереноситеBDE и SQL Links на другиекомпьютеры).


Заключение

Итак, на данномуроке мы постаралисьпонять для, чтотакое BDE,изучили оченьважное дляработы с базамиданных понятие- алиас, а такженаучилисьнастраиватьего параметрыдля корректнойработы программына примередрайвера PARADOX.

5

Урок 1:НастройкаBDE



Создание базданных в Delphi


Урок 2:Создание таблицс помощью DatabaseDesktop

Содержаниеурока 2:


Обзор2

УтилитаDatabase Desktop2

Заключение9


Обзор

На данном урокемы изучим, каксоздаватьтаблицы базыданных с помощьюутилиты Database Desktop,входящей впоставку Delphi. Хотядля созданиятаблиц можноиспользоватьразличныесредства (SQL -компонентTQueryи компонентTTable),применениеэтой утилитыпозволяетсоздаватьтаблицы винтерактивномрежиме и сразуже просмотретьих содержимое- и все это длябольшого числаформатов. Этоособенно удобнодля локальныхбаз данных, вчастностиParadox и dBase.

УтилитаDatabase Desktop

Database Desktop - это утилита,во многом похожаяна Paradox, котораяпоставляетсявместе с Delphi дляинтерактивнойработы с таблицамиразличныхформатов локальныхбаз данных -Paradox и dBase, а такжеSQL-серверныхбаз данныхInterBase, Oracle, Informix, Sybase (с использованиемSQL Links). Исполняемыйфайл утилитыназываетсяDBD32.EXE. Для запускаDatabase Desktop просто дваждыщелкните поее иконке.

Рис. 1: Выпадающийсписок в диалоговомокне Table Type позволяетвыбрать типсоздаваемойтаблицы


После стартаDatabase Desktop выберитекоманду менюFile|New|Table для созданияновой таблицы.Перед Вамипоявится диалоговоеокно выборатипа таблицы,как показанона рис.1. Вы можетевыбрать любойформат изпредложенного,включая различныеверсии одногои того же формата.

После выборатипа таблицыDatabase Desktop представитВам диалоговоеокно, специфичноедля каждогоформата, в которомВы сможетеопределитьполя таблицыи их тип, какпоказано нарис.2.

Рис. 2: Database Desktop позволяетзадать именаи типы полейв таблице


Имя поля в таблицеформата Paradoxпредставляетсобой строку,написаниекоторой подчиняетсяследующимправилам:


  • Имя должнобыть не длиннее25 символов.

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

  • Имя не должносодержатьквадратные,круглые илифигурные скобки[], ()или {},тире, а такжекомбинациюсимволов “тире”и “больше”(->).

  • Имя не должнобыть толькосимволом #,хотя этот символможет присутствоватьв имени средидругих символов.Хотя Paradox поддерживаетточку (.)в названииполя, лучше ееизбегать, посколькуточка зарезервированав Delphi для другихцелей.


Имя поля в таблицеформата dBaseпредставляетсобой строку,написаниекоторой подчиняетсяправилам, отличнымот Paradox:


  • Имя должнобыть не длиннее10 символов.

  • Пробелы в именинедопустимы.


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


Укажем ещеправила, которымподчиняетсянаписание именполей в форматеInterBase.


  • Имя должнобыть не длиннее31 символа.

  • Имя должноначинатьсяс букв A-Z,a-z.

  • Имя поля можетсодержатьбуквы (A-Z,a-z),цифры, знак $и символ подчеркивания(_).

  • Пробелы в именинедопустимы.

  • Для имен таблицзапрещаетсяиспользоватьзарезервированныеслова InterBase.


Следующий(после выбораимени поля) шагсостоит в заданиитипа поля. Типыполей оченьсильно различаютсядруг от друга,в зависимостиот формататаблицы. Дляполучениясписка типовполей перейдитек столбцу “Type”,а затем нажмитепробел илищелкните правойкнопкой мышки.Приведем спискитипов полей,характерныедля форматовParadox, dBase и InterBase.

Итак, поля таблицформата Paradox могутиметь следующийтип (для вводатипа поля можнонабрать толькоподчеркнутыебуквы или цифры):

Табл.A: Типы полейформата Paradox

Alpha

строкадлиной 1-255 байт,содержащаялюбые печатаемыесимволы

Number

числовоеполе длиной8 байт, значениекоторого можетбыть положительными отрицательным.Диапазон чисел- от 10-308до 10308с 15 значащимицифрами

$(Money)

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

Short

числовоеполе длиной2 байта, котороеможет содержатьтолько целыечисла в диапазонеот -32768 до 32767

LongInteger

числовоеполе длиной4 байта, котороеможет содержатьцелые числав диапазонеот -2147483648 до 2147483648

#(BCD)

числовоеполе, содержащееданные в форматеBCD (Binary Coded Decimal). Скоростьвычисленийнемного меньше,чем в другихчисловых форматах,однако точность- гораздо выше.Может иметь0-32 цифр последесятичнойточки

Date

поледаты длиной4 байта, котороеможет содержатьдату от 1 января9999 г. до нашейэры - до 31 декабря9999 г. нашей эры.Корректнообрабатываетвисокосныегода и имеетвстроенныймеханизм проверкиправильностидаты

Time

полевремени длиной4 байта, содержитвремя в миллисекундахот полуночии ограничено24 часами

@(Timestamp)

обобщенноеполе даты длиной8 байт - содержити дату и время

Memo

поледля хранениясимволов,суммарнаядлина которыхболее 255 байт.Может иметьлюбую длину.При этом размер,указываемыйпри созданиитаблицы, означаетколичествосимволов,сохраняемыхв таблице (1-240)- остальныесимволы сохраняютсяв отдельномфайле с расширением.MB

FormattedMemo

поле,аналогичноеMemo, с добавлениемвозможностизадавать шрифттекста. Такжеможет иметьлюбую длину.При этом размер,указываемыйпри созданиитаблицы, означаетколичествосимволов,сохраняемыхв таблице (0-240)- остальныесимволы сохраняютсяв отдельномфайле с расширением.MB. Однако, Delphi встандартнойпоставке необладаетвозможностьюработать сполями типаFormatted Memo

Graphic

поле,содержащееграфическуюинформацию.Может иметьлюбую длину.Смысл размера- такой же, каки в Formatted Memo. Database Desktop “умеет”создаватьполя типа Graphic,однако наполнятьих можно тольков приложении

OLE

поле,содержащееOLE-данные (Object Linking andEmbedding) - образы, звук,видео, документы- которые длясвоей обработкивызывают создавшееих приложение.Может иметьлюбую длину.Смысл размера- такой же, каки в Formatted Memo. Database Desktop “умеет”создаватьполя типа OLE,однако наполнятьих можно тольков приложении.Delphi “напрямую”не умеет работатьс OLE-полями, ноэто легкообходитсяпутем использованияпотоков

Logical

поледлиной 1 байт,которое можетсодержатьтолько двазначения - T(true, истина) илиF(false, ложь). Допускаютсястрочные ипрописныебуквы

+(Autoincrement)

поледлиной 4 байта,содержащеенередактируемое(read-only) значениетипа longinteger.Значение этогополя автоматическиувеличивается(начиная с 1) сшагом 1 - этоочень удобнодля созданияуникальногоидентификаторазаписи (физическийномер записине может служитьее идентификатором,поскольку вПарадоксетаковой отсутствует.В InterBase также отсутствуютфизическиеномера записей,но отсутствуети поле Autoincrement.Его с успехомзаменяетвстроеннаяфункция Gen_id,которую удобнейвсего применятьв триггерах)

Binary

поле,содержащеелюбую двоичнуюинформацию.Может иметьлюбую длину.При этом размер,указываемыйпри созданиитаблицы, означаетколичествосимволов,сохраняемыхв таблице (0-240)- остальныесимволы сохраняютсяв отдельномфайле с расширением.MB. Это полнейшийаналог поляBLOb в InterBase

Bytes

строкацифр длиной1-255 байт, содержащаялюбые данные


Поля таблицформата dBase могутиметь следующийтип (для вводатипа поля можнонабрать толькоподчеркнутыебуквы или цифры):

Табл.B: Типы полейформата dBase

Character(alpha)

строкадлиной 1-254 байт,содержащаялюбые печатаемыесимволы

Float(numeric)

числовоеполе размером1-20 байт в форматес плавающейточкой, значениекоторого можетбыть положительными отрицательным.Может содержатьочень большиевеличины, однакоследует иметьв виду постоянныеошибки округленияпри работе сполем такоготипа. Числоцифр последесятичнойточки (параметрDec в DBD) должно бытьпо крайнеймере на 2 меньше,чем размервсего поля,поскольку вобщий размервключаютсясама десятичнаяточка и знак

Number(BCD)

числовоеполе размером1-20 байт, содержащееданные в форматеBCD (Binary Coded Decimal). Скоростьвычисленийнемного меньше,чем в другихчисловых форматах,однако точность- гораздо выше.Число цифрпосле десятичнойточки (параметрDec в DBD) также должнобыть по крайнеймере на 2 меньше,чем размервсего поля,поскольку вобщий размервключаютсясама десятичнаяточка и знак

Date

поледаты длиной8 байт. По умолчанию,используетсяформат короткойдаты (ShortDateFormat)

Logical

поледлиной 1 байт,которое можетсодержатьтолько значения“истина” или“ложь” - T,t,Y,y(true, истина) илиF,f,N,n(false, ложь). Допускаютсястрочные ипрописныебуквы. Такимобразом, в отличиеот Парадокса,допускаютсябуквы “Y” и “N”(сокращениеот Yes и No)

Memo

поледля хранениясимволов,суммарнаядлина которыхболее 255 байт.Может иметьлюбую длину.Это поле хранитсяв отдельномфайле. Database Desktop неимеет возможностивставлятьданные в полетипа Memo

OLE

поле,содержащееOLE-данные (Object Linking andEmbedding) - образы, звук,видео, документы- которые длясвоей обработкивызывают создавшееих приложение.Может иметьлюбую длину.Это поле такжесохраняетсяв отдельномфайле. Database Desktop “умеет”создаватьполя типа OLE,однако наполнятьих можно тольков приложении.Delphi “напрямую”не умеет работатьс OLE-полями, ноэто легкообходитсяпутем использованияпотоков

Binary

поле,содержащеелюбую двоичнуюинформацию.Может иметьлюбую длину.Данное полесохраняетсяв отдельномфайле с расширением.DBT. Это полнейшийаналог поляBLOb в InterBase


Поля таблицформата InterBase могутиметь следующийтип:

Табл.C: Типы полейформата InterBase

SHORT

числовоеполе длиной2 байта, котороеможет содержатьтолько целыечисла в диапазонеот -32768 до 32767

LONG

числовоеполе длиной4 байта, котороеможет содержатьцелые числав диапазонеот -2147483648 до 2147483648

FLOAT

числовоеполе длиной4 байта, значениекоторого можетбыть положительными отрицательным.Диапазон чисел- от 3.4*10-38до 3.4*1038с 7 значащимицифрами

DOUBLE

числовоеполе длиной8 байт (длиназависит отплатформы),значение которогоможет бытьположительными отрицательным.Диапазон чисел- от 1.7*10-308до 1.7*10308с 15 значащимицифрами

CHAR

строкасимволовфиксированнойдлины (0-32767 байт),содержащаялюбые печатаемыесимволы. Числосимволов зависитот Character Set, установленногов InterBase для данногополя или длявсей базы данных(например, длясимволов вкодировкеUnicode число символовбудет в двараза меньшедлины строки)

VARCHAR

строкасимволовпеременнойдлины (0-32767 байт),содержащаялюбые печатаемыесимволы. Числосимволов такжезависит отCharacter Set, установленногов InterBase для данногополя или длявсей базы данных

DATE

поледаты длиной8 байт, значениекоторого можетбыть от 1 января100 года до 11 декабря5941 года (времятакже содержится)

BLOB

поле,содержащеелюбую двоичнуюинформацию.Может иметьлюбую длину.Database Desktop не имеетвозможностиредактироватьполя типа BLOB

ARRAY

поле,содержащеемассивы данных.InterBase позволяетопределятьмассивы, имеющиеразмерность16. Поле можетиметь любуюдлину. Однако,Database Desktop не имеетвозможностине толькоредактироватьполя типа ARRAY,но и создаватьих

TEXTBLOB

подтипBLOB-поля, содержащеетолько текстовуюинформацию.Может иметьлюбую длину.Database Desktop не имеетвозможностиредактироватьполя типа TEXTBLOB

Типы полеймогут отличатьсяот приведенныхвыше. Это зависитот версии драйверабазы данных.


Итак, мы изучиливсе типы полей,являющиеся“родными” дляDelphi.


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

После созданиятаблицы, с нейможно связатьнекоторыесвойства, переченькоторых зависитот формататаблицы. Так,для таблицформата Paradox можнозадать:

  • Validity Checks (проверкаправильности)- относится кполю записии определяетминимальноеи максимальноезначение, атакже значениепо умолчанию.Кроме того,позволяетзадать маскуввода

  • Table Lookup (таблицадля “подсматривания”)- позволяетвводить значениев таблицу, используяуже существующеезначение вдругой таблице

  • Secondary Indexes (вторичныеиндексы) - позволяютдоступатьсяк данным в порядке,отличном отпорядка, задаваемогопервичнымключом

  • Referential Integrity (ссылочнаяцелостность)- позволяетзадать связимежду таблицамии поддерживатьэти связи науровне ядра.Обычно задаетсяпосле созданиявсех таблицв базе данных

  • Password Security (парольнаязащита) - позволяетзакрыть таблицупаролем

  • Table Language (язык таблицы)- позволяетзадать длятаблицы языковыйдрайвер.


В таблицахdBase не существуетпервичныхключей. Однако,это обстоятельствоможно преодолетьпутем определенияуникальных(Unique) и поддерживаемых(Maintained) индексов(Indexes). Кроме того,для таблицdBase можно определитьи язык таблицы(Table Language) - языковыйдрайвер, управляющийсортировкойи отображениемсимвольныхданных.


Определениядополнительныхсвойств таблицвсех форматовдоступны черезкнопку “Define” (длятаблиц InterBase даннаякнопка называется“Define Index...” и позволяетопределятьлишь толькоиндекс, но непервичный ключ)в правой верхнейчасти окна(группа Table Properties).Причем, все этидействия можнопроделыватьне только присоздании таблицы,но и тогда, когдаона уже существует.Для этогоиспользуетсякоманда Table|RestructureTable (для открытойв данный моменттаблицы) илиUtilities|Restructure (с возможностьювыбора таблицы).Однако, еслиВы желаетеизменить структуруили добавитьновые свойствадля таблицы,которая в данныймомент ужеиспользуетсядругим приложением,Database Desktop откажетВам в этом, посколькуданная операциятребует монопольногодоступа к таблице.Но зато всепроизведенныев структуреизменения сразуже начинают“работать”- например, еслиВы определитессылочнуюцелостностьдля пары таблиц,то при попыткевставить вдочернюю таблицуданные, отсутствующиев родительскойтаблице, в Delphiвозникнетисключительноесостояние.

В заключениеотметим ещечасто используемуюочень полезнуювозможностьDatabase Desktop. Создаватьтаблицу любогоформата можноне только “счистого листа”,но и путемкопированияструктуры ужесуществующейтаблицы. Дляэтого достаточновоспользоватьсякнопкой “Borrow”,имеющейся влевом нижнемуглу окна.Появляющеесядиалоговоеокно позволитВам выбратьсуществующуютаблицу ивключить/выключитьдополнительныеопции, совпадающиес уже перечисленнымисвойствамитаблиц. Этонаиболее легкийспособ созданиятаблиц.


Заключение

Итак, на данномуроке мы познакомилисьсо штатнойутилитой,используемойдля интерактивногосоздания имодификациитаблиц различнойструктуры. Ихотя управлениетаблицами можноосуществлятьс помощью различныхсредств (компонентTTable,компонентTQuery),данная утилитапозволяетделать это винтерактивномрежиме наиболеепростым способом.

9

Урок 2:Создание таблицс помощью DatabaseDesktop



Созданиебаз данных вDelphi


Урок 3:Создание таблицс помощьюSQL-запросов


Содержаниеурока 3:


Обзор2

Созданиетаблиц с помощьюSQL2

Заключение6


Обзор

На данномуроке мы познакомимсяеще с однойвозможностьюсоздания таблиц- через посылкуSQL-запросов. КакВы, наверное,могли заметитьна предыдущемуроке, Database Desktop необладает всемивозможностямипо управлениюSQL-сервернымибазами данных.Поэтому с помощьюDatabase Desktop удобно создаватьили локальныебазы данныхили толькопростейшиеSQL-серверныебазы данных,состоящие изнебольшогочисла таблиц,не очень сильносвязанных другс другом. Еслиже Вам необходимосоздать базуданных, состоящуюиз большогочисла таблиц,имеющих сложныевзаимосвязи,можно воспользоватьсяязыком SQL. Приэтом можновоспользоватьсякомпонентомQueryв Delphi, каждый разпосылая поодному SQL-запросу,а можно записатьвсю последовательностьSQL-предложенийв один так называемыйскрипти послать егона выполнение.Конечно, дляэтого нужнохорошо знатьязык SQL, но, уверяюВас, сложногов этом ничегонет! Конкретныереализацииязыка SQL незначительноотличаютсяв различныхSQL-серверах, однакобазовые предложенияостаются одинаковымидля всех реализаций.

Созданиетаблиц с помощьюSQL

Если Вы хотитевоспользоватьсякомпонентомTQuery,сначала поместитеего на форму.После этогонастройтесвойство DatabaseNameна нужный Вамалиас. Послеэтого можноввести SQL-предложениев свойство SQL.Для выполнениязапроса, изменяющегоструктуру,вставляющегоили обновляющегоданные на сервере,нужно вызватьметод ExecSQLкомпонентаTQuery.Для выполнениязапроса, получающегоданные с сервера(т.е. запроса,в котором основнымявляется операторSELECT), нужно вызватьметод OpenкомпонентаTQuery.Это связанос тем, что BDE припосылке запросатипа SELECT открываеттак называемыйкурсор,с помощью которогоосуществляетсянавигация повыборке данных(подробней обэтом см. в уроке,посвященномTQuery).

ПриведемупрощенныйсинтаксисSQL-предложениядля созданиятаблицы наSQL-сервере InterBase(более полныйсинтаксис можнопосмотретьв online-справочникепо SQL, поставляемомс локальнымInterBase):


CREATE TABLE table

( [, | ...]);


где

table- имя создаваемойтаблицы,

- описание поля,

- описаниеограниченийи/или ключей(квадратныескобки []означаютнеобязательность,вертикальнаячерта |означает “или”).


Описаниеполя состоитиз наименованияполя и типаполя (или домена- см. урок 9), а такжедополнительныхограничений,накладываемыхна поле:


=col {datatype | COMPUTED BY () | domain}

[DEFAULT{literal | NULL | USER}]

[NOT NULL][]

[COLLATEcollation]


Здесь

col- имя поля;

datatype- любой правильныйтип SQL-сервера(для InterBase такимитипами являютсяSMALLINT,INTEGER,FLOAT,DOUBLEPRECISION, DECIMAL,NUMERIC,DATE,CHAR,VARCHAR,NCHAR,BLOB),символьныетипы могутиметь CHARACTER SET - наборсимволов,определяющийязык страны.Для русскогоязыка следуетзадать наборсимволов WIN1251;

COMPUTEDBY () - определениевычисляемогона уровне сервераполя, где - правильноеSQL-выражение,возвращающееединственноезначение;

domain- имя домена(обобщенноготипа), определенногов базе данных;

DEFAULT- конструкция,определяющаязначение поляпо умолчанию;

NOTNULL - конструкция,указывающаяна то, что полене может бытьпустым;

COLLATE- предложение,определяющеепорядок сортировкидля выбранногонабора символов(для поля типаBLOB не применяется).Русский наборсимволов WIN1251имеет 2 порядкасортировки- WIN1251 и PXW_CYRL. Для правильнойсортировки,включающейбольшие буквы,следует выбратьпорядок PXW_CYRL.


Описаниеограниченийи/или ключейвключает в себяпредложенияCONSTRAINTили предложения,описывающиеуникальныеполя, первичные,внешние ключи,а также ограниченияCHECK(такие конструкциимогут определятьсякак на уровнеполя, так и науровне таблицыв целом, еслиони затрагиваютнесколькополей):


= [CONSTRAINT constraint ]

Здесь


= {{PRIMARY KEY | UNIQUE} (col[,col...]) | FOREIGN KEY (col [, col...]) REFERENCES other_table

| CHECK()}

=

{ { | ()}

| [NOT]BETWEEN AND

| [NOT]LIKE [ESCAPE ]

| [NOT]IN ( [, ...] |


= {

col []| | |

| NULL | USER |RDB$DB_KEY } [COLLATE collation]


=num | "string" | charsetname "string"


= {

COUNT (* | [ALL] | DISTINCT )

| SUM ([ALL] | DISTINCT )

|AVG ([ALL] | DISTINCT )

| MAX ([ALL] | DISTINCT )

| MIN ([ALL] | DISTINCT )

| CAST (AS )

| UPPER ()

| GEN_ID(generator, )

}


={= | | = | ! | | !=}


=выражениеSELECT по одномуполю, котороевозвращаетв точности однозначение.

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


ПримерA: Простая таблицас конструкциейPRIMARY KEYна уровне поля

CREATE TABLE REGION(

REGIONREGION_NAME NOT NULL PRIMARY KEY,

POPULATIONINTEGERNOT NULL);


Предполагается,что в базе данныхопределен доменREGION_NAME,например, следующимобразом:


CREATE DOMAINREGION_NAME

AS VARCHAR(40)CHARACTER SET WIN1251 COLLATE PXW_CYRL;


ПримерB: Таблица спредложениемUNIQUEкак на уровнеполя, так и науровне таблицы

CREATE TABLE GOODS (

MODEL SMALLINT NOTNULL UNIQUE,

NAMECHAR(10) NOTNULL,

ITEMID INTEGER NOTNULL, CONSTRAINT MOD_UNIQUE

UNIQUE (NAME,ITEMID));


ПримерC: Таблица сопределениемпервичногоключа, внешнегоключа и конструкцииCHECK,а также символьныхмассивов

CREATE TABLE JOB(

JOB_CODE JOBCODENOT NULL,

JOB_GRADEJOBGRADENOT NULL,

JOB_REGIONREGION_NAME NOT NULL,

JOB_TITLEVARCHAR(25) CHARACTER SET WIN1251 COLLATE PXW_CYRL NOT NULL,

MIN_SALARY SALARYNOT NULL,

MAX_SALARY SALARYNOT NULL,

JOB_REQ BLOB(400,1)CHARACTER SET WIN1251,

LANGUAGE_REQVARCHAR(15) [5],

PRIMARY KEY(JOB_CODE, JOB_GRADE, JOB_REGION),

FOREIGN KEY(JOB_REGION) REFERENCES REGION (REGION),

CHECK (MIN_SALARY

Данный примерсоздает таблицу,содержащуюинформациюо работах(профессиях).Типы полейоснованы надоменах JOBCODE,JOBGRADE,REGION_NAMEи SALARY.Определенмассив LANGUAGE_REQ,состоящий из5 элементовтипа VARCHAR(15).Кроме того,введено полеJOB_REQ,имеющее типBLOBс подтипом 1(текстовыйблоб) и размеромсегмента 400.Для таблицыопределенпервичный ключ,состоящий изтрех полейJOB_CODE,JOB_GRADEи JOB_REGION.Далее, определенвнешний ключ(JOB_REGION),ссылающийсяна поле REGIONтаблицы REGION.И, наконец, включенопредложениеCHECK,позволяющеепроизводитьпроверку соотношениядля двух полейи вызыватьисключительноесостояние принарушениитакого соотношения.


ПримерD: Таблица свычисляемымполем

CREATETABLE SALARY_HISTORY (

EMP_NO EMPNO NOTNULL,

CHANGE_DATE DATEDEFAULT "NOW" NOT NULL,

UPDATER_IDVARCHAR(20) NOT NULL,

OLD_SALARY SALARYNOT NULL,

PERC_CHANGEDOUBLEPRECISION DEFAULT 0 NOT NULL

CHECK (PERC_CHANGEBETWEEN -50 AND 50),

NEW_SALARY COMPUTEDBY

(OLD_SALARY +OLD_SALARY * PERC_CHANGE / 100),

PRIMARY KEY (EMP_NO,CHANGE_DATE, UPDATER_ID),

FOREIGN KEY (EMP_NO)REFERENCES EMPLOYEE (EMP_NO));


Данный примерсоздает таблицу,где среди другихполей имеетсявычисляемое(физически несуществующее)поле NEW_SALARY,значение котороговычисляетсяпо значениямдвух другихполей (OLD_SALARYи PERC_CHANGE).


Заключение

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

6

Урок 3: Созданиетаблиц с помощьюSQL запросов



Создание базданных в Delphi


Урок4: ОбъектTTable

Содержаниеурока 4:

Содержаниеурока 4:1

КлассTDataSet2

Открытиеи закрытиеDataSet4

Поля10

Работас Данными14

ИспользованиеSetKey для поискав таблице17

Использованиефильтров дляограничениячисла записейв DataSet20

Обновление(Refresh)22

Закладки(Bookmarks)23

СозданиеСвязанныхКурсоров (Linkedcursors)23

Основныепонятия оTDataSource26

ИспользованиеTDataSource для проверкисостоянияБД:27

ОтслеживаниесостоянияDataSet 31

Обзор

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

Болееподробно здесьрассказываетсяо TTable и TDataSource.


Имеются несколькоосновныхкомпонент(объектов),которые Выбудете использоватьпостоянно длядоступа к БД.Эти объектымогут бытьразделены натри группы:


  • невизуальные:TTable, TQuery, TDataSet, TField

  • визуальные:TDBGrid, TDBEdit

  • связующие:TDataSource


Первая группавключает невизуальныеклассы, которыеиспользуютсядля управлениятаблицами изапросами. Этагруппа сосредотачиваетсявокруг компоненттипа TTable, TQuery, TDataSet и TField.В Палитре Компонентэти объектырасположенына страницеData Access.

Вторая важнаягруппа классов- визуальные,которые показываютданные пользователю,и позволяютему просматриватьи модифицироватьих. Эта группаклассов включаеткомпонентытипа TDBGrid, TDBEdit, TDBImage иTDBComboBox. В ПалитреКомпонент эти объекты расположенына страницеData Controls.

Имеется и третийтип, которыйиспользуетсядля того, чтобы связать предыдущиедва типа объектов.К третьему типуотноситсятолько невизуальныйкомпонентTDataSource.


КлассTDataSet

TDataSet класс - одиниз наиболееважных объектовБД. Чтобы начатьработать с ним,Вы должны взглянутьна следующуюиерархию:


TDataSet

|

TDBDataSet

|

|-- TTable

|-- TQuery

|-- TStoredProc


TDataSet содержитабстрактныеметоды там, гдедолжно бытьнепосредственноеуправлениеданными. TDBDataSet знает,как обращатьсяс паролями ито, что нужносделать, чтобыприсоединитьВас к определеннойтаблице. TTable знает(т.е. уже всеабстрактныеметоды переписаны),как обращатьсяс таблицей, ееиндексами ит.д.

КакВы увидите вдалее, TQuery имеетопределенныеметоды дляобработки SQLзапросов.

TDataSet - инструмент,который Выбудете использоватьчтобы открытьтаблицу, иперемещатьсяпо ней. Конечно,Вы никогда небудете непосредственносоздаватьобъект типаTDataSet. Вместо этого,Вы будетеиспользоватьTTable, TQuery или другихпотомков TDataSet(например, TQBE).Полное пониманиеработы системы,и точное значениеTDataSet, будут становитьсявсе более яснымипо мере прочтенияэтой главы.


Нанаиболеефундаментальномуровне, Dataset этопросто наборзаписей, какизображенона рис.1


Рис.1: Любойdataset состоит изряда записей(каждая содержитN полей) и указательна текущуюзапись.


В большинствеслучаев dataset будетиметь a прямое,один к одному,соответствиес физическойтаблицей, котораясуществуетна диске. Однако,в других случаяхВы можете исполнятьзапрос илидругое действие,возвращающиеdataset, который содержитлибо любоеподмножествозаписей однойтаблицы, либообъединение(join) между несколькимитаблицами. Втексте будутиногда использоватьсятермины DataSet иTTable как синонимы.

Обычно в программеиспользуютсяобъекты типаTTable или TQuery, поэтомув следующихнесколькихглавах будетпредполагатьсясуществованиеобъекта типаTTable называемогоTable1.

Итак, самоевремя начатьисследованиеTDataSet. Как толькоВы познакомитесьс его возможностями,Вы начнетепонимать, какиеметоды используетDelphi для доступак данным, хранящимсяна диске в видеБД. Ключевоймомент здесь- не забывать,что почти всякийраз, когдапрограммистна Delphi открываеттаблицу, онбудет использоватьTTable или TQuery, которыеявляются простонекоторойнадстройкойнад TDataSet.


Открытиеи закрытиеDataSet

В этой главеВы узнаетенекоторые фактыоб открытиии закрытииDataSet.

Если Вы используетеTTable для доступак таблице, топри открытииданной таблицызаполняютсянекоторыесвойства TTable(количествозаписей RecordCount,описание структурытаблицы и т.д.).

Прежде всего,Вы должны поместитьво время дизайнана форму объектTTable и указать, скакой таблицейхотите работать.Для этого нужнозаполнить вИнспектореобъектов свойстваDatabaseName и TableName. В DatabaseName можнолибо указатьдиректорию,в которой лежаттаблицы в форматеdBase или Paradox (например,C:\DELPHI\DEMOS\DATA), либо выбратьиз списка псевдонимбазы данных(DBDEMOS). Теперь, еслисвойство Activeустановитьв True, то при запускеприложениятаблица будетоткрыватьсяавтоматически.

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


Table1.Open;


Или, если Выпредпочитаете,то можете установитьсвойство Activeравное True:


Table1.Active:= True;


Нет никакогоразличия междурезультатомпроизводимымэтими двумяоперациями.Метод Open, однако,сам заканчиваетсяустановкойсвойства Active вTrue, так что можетбыть даже чутьболее эффективноиспользоватьсвойство Activeнапрямую.

Также, какимеются дваспособа открытьa таблицу, таки есть два способазакрыть ее.Самый простойспособ простовызывать Close:


Table1.Close;


Или,если Вы желаете,Вы можете написать:


Table1.Active:= False;


Еще раз повторим,что нет никакойсущественнойразницы междудвумя этимиспособами. Выдолжны толькопомнить, чтоOpen и Close это методы(процедуры), аActive - свойство.


Навигация(Перемещениепо записям)

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

Следующийобширный наборметодов и свойстваTDataSet обеспечиваетвсе , что Вамнужно для доступак любой конкретнойзаписи внутритаблицы:


procedureFirst;

procedureLast;

procedureNext;

procedurePrior;

propertyBOF: Boolean read FBOF;

propertyEOF: Boolean read FEOF;

procedureMoveBy(Distance: Integer);


Дадимкраткий обзорих функциональныхвозможностей:


  • ВызовTable1.First перемещаетВас к первойзаписи в таблице.

  • Table1.LastперемещаетВас к последнейзаписи.

  • Table1.NextперемещаетВас на однузапись вперед.

  • Table1.PriorперемещаетВас на однузапись Назад.

  • Выможете проверятьсвойства BOF илиEOF, чтобы понять,находитесьли Вы в началеили в концетаблицы.

  • ПроцедураMoveBy перемещаетВас на N записейвперед илиназад в таблице.Нет никакогофункциональногоразличия междузапросом Table1.Nextи вызовомTable1.MoveBy(1). Аналогично,вызов Table1.Prior имееттот же самыйрезультат, чтои вызов Table1.MoveBy(-1).


Чтобы начатьиспользоватьэти навигационныеметоды, Вы должныпоместитьTTable, TDataSource и TDBGrid на форму,также, как Выделали это впредыдущемуроке. ПрисоединитеDBGrid1 к DataSource1, и DataSource1 к Table1.Затем установитесвойства таблицы:


  • вDatabaseName имя подкаталога,где находятсядемонстрационныетаблицы (илипсевдонимDBDEMOS);

  • вTableName установитеимя таблицыCUSTOMER.


Если Вы запустилипрограмму,которая содержитвидимый элементTDBGrid, то увидите,что можноперемещатьсяпо записямтаблицы с помощьюполос прокрутки(scrollbar) на нижнейи правой сторонахDBGrid.

Однако, иногданужно перемещатьсяпо таблице“программнымпутем”, безиспользованиявозможностей,встроенныхв визуальныекомпоненты.В следующихнесколькихабзацах объясняетсякак можно этосделать.

Поместите двекнопки на формуи назовите ихNext и Prior, как показанона рис.2.


Рис.2 : Next и Prior кнопкипозволяют Вамперемещатьсяпо БД.


Дваждыщелкните накнопке Next - появитсязаготовкаобработчикасобытия:


procedureTForm1.NextClick(Sender: TObject);

begin

end;


Теперьдобавьте однустрочку кодатак, чтобы процедуравыглядела так:


procedureTForm1.NextClick(Sender: TObject);

begin

Table1.Next;

end;


Повторитете же самыедействия скнопкой Prior, так,чтобы функциясвязанная сней выгляделатак:


procedureTForm1.PriorClick(Sender: TObject);

begin

Table1.Prior;

end;


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

Теперьдобавьте ещедве кнопки иназовите ихFirst и Last, как показанона рис.3


Рис.3: Программасо всеми четырьмякнопками.


Сделайтето же самое дляновых кнопок.


procedureTForm1.FirstClick(Sender: TObject);

begin

Table1.First;

end;


procedureTForm1.LastClick(Sender: TObject);

begin

Table1.Last;

end;


Нет ничегоболее простогочем эти навигационныефункции. First перемещаетВас в началотаблицы, Last перемещаетВас в конецтаблицы, а Next иPrior перемещаютВас на однузапись впередили назад.

TDataSet.BOF - read-only Boolean свойство,используетсядля проверки,находитесьли Вы в началетаблицы. СвойстваBOF возвращаетtrue в трех случаях:


  • Послетого, как Выоткрыли файл;

  • Послетого, как ВывызывалиTDataSet.First;

  • Послетого, как вызовTDataSet.Prior не выполняется.


Первые двапункта - очевидны.Когда Вы открываететаблицу, Delphi помещаетВас на первуюзапись; когдаВы вызываетеметод First, Delphi такжеперемещаетВас в началотаблицы. Третийпункт, однако,требует небольшогопояснения:после того, какВы вызывалиметод Prior многораз, Вы могли добраться доначала таблицы,и следующийвызов Prior будетнеудачным -после этогоBOF и будет возвращатьTrue.

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


whilenot Table.Bof do begin

DoSomething;

Table1.Prior;

end;


В коде, показанномздесь, гипотетическаяфункция DoSomething будетвызвана спервана текущейзаписи и затемна каждой следующейзаписи (от текущейи до началатаблицы). Циклбудет продолжатьсядо тех пор, покавызов Table1.Prior несможет большепереместитьВас на предыдущуюзапись в таблице.В этот моментBOF вернет True и программавыйдет из цикла.(Чтобы оптимизироватьвышеприведенныйкод, установитеDataSource1.Enabled в False передначалом цикла,и верните егов True после окончанияцикла.)

ВсесказанноеотносительноBOF также применимои к EOF. Другимисловами, код,приведенныйниже показываетпростой способпробежать повсем записямв a dataset:


Table1.First;

whilenot Table1.EOF do begin

DoSomething;

Table1.Next;

end;


Классическаяошибка в случаях,подобных этому:Вы входите вцикл while или repeat, нозабываетевызывать Table1.Next:


Table1.First;

repeat

DoSomething;

untilTable1.EOF;


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


EOFвозвращаетTrue в следующихтрех случаях:


  • Послетого, как Выоткрыли пустойфайл;

  • Послетого, как ВывызывалиTDataSet.Last;

  • Послетого, как вызовTDataSet.Next не выполняется.


Единственнаянавигационнаяпроцедура,которая ещене упоминалась- MoveBy, которая позволяетВам переместитьсяна N записей вперед илиназад в таблице.Если Вы хотитепереместитьсяна две записивперед, то напишите:


MoveBy(2);


Иесли Вы хотите переместитьсяна две записиназад, то:


MoveBy(-2);


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

Prior и Next - это простыефункции, которыевызывают MoveBy.

Поля

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


propertyFields[Index: Integer];

functionFieldByName(const FieldName: string): TField;

propertyFieldCount;


СвойствоFieldCount возвращаетчисло полейв текущей структурезаписи. ЕслиВы хотите программнымпутем прочитатьимена полей,то используйтесвойство Fieldsдля доступак ним:


var

S:String;

begin

S:= Fields[0].FieldName;

end;


Если Вы работалис записью вкоторой первоеполе называетсяCustNo, тогда кодпоказанныйвыше поместитстроку “CustNo” впеременнуюS. Если Вы хотитеполучить доступк имени второгополя в вышеупомянутомпримере, тогдаВы могли бынаписать:


S:= Fields[1].FieldName;


Корочеговоря, индекспередаваемыйв Fields(начинающийсяс нуля), и определяетномер поля ккоторому Выполучите доступ,т.е. первое поле- ноль, второеодин, и так далее.

ЕслиВы хотите прочитатьтекущее содержаниеконкретногополя конкретнойзаписи, то Выможете использоватьсвойство Fieldsили методFieldsByName.Для того, чтобынайти значениепервого полязаписи, прочитайтепервый элементмассива Fields:


S:= Fields[0].AsString;


Предположим,что первое полев записи содержитномер заказчика,тогда код, показанныйвыше, возвратилбы строку типа“1021”, “1031” или “2058”.Если Вы хотелиполучить доступк этот переменный,как к числовойвеличине, тогдаВы могли быиспользоватьAsInteger вместо AsString.Аналогично,свойство Fieldsвключают AsBoolean,AsFloat и AsDate.

Еслихотите, Вы можетеиспользоватьфункцию FieldsByNameвместо свойстваFields:


S:= FieldsByName(‘CustNo’).AsString;


Как показанов примерахвыше, и FieldsByName,и Fieldsвозвращаютте же самыеданные. Дваразличныхсинтаксисаиспользуютсяисключительнодля того, чтобыобеспечитьпрограммистовгибким и удобнымнабором инструментовдля программногодоступа к содержимомуDataSet.

Давайте посмотримна простомпримере, какможно использоватьдоступ к полямтаблицы вовремя выполненияпрограммы.Создайте новыйпроект, положитена форму объектTTable, два объектаListBox и две кнопки- “Fields” и “Values” (смрис.4).

Соединитеобъект TTable с таблицейCUSTOMER, котораяпоставляетсявместе с Delphi (DBDEMOS),не забудьтеоткрыть таблицу(Active = True).


Рис.4: ПрограммаFLDS показывает,как использоватьсвойство Fields.


СделайтеDouble click на кнопкеFields и создайтеa метод которыйвыглядит так:


procedureTForm1.FieldsClick(Sender: TObject);

var

i:Integer;

begin

ListBox1.Clear;

fori := 0 to Table1.FieldCount - 1 do

ListBox1.Items.Add(Table1.Fields[i].FieldName);

end;


Обработчиксобытия начинаетсяс очистки первогоListBox1, затем он проходитчерез все поля,добавляя ихимена один задругим в ListBox1.Заметьте, чтоцикл показанныйздесь пробегаетот 0 до FieldCount - 1. ЕслиВы забудетевычесть единицуиз FieldCount, то Вы получитеошибку “List Index Out ofBounds”, так как Выбудете пытатьсяпрочесть имяполя котороене существует.

Предположим,что Вы ввеликод правильно,и заполнилиListBox1 именами всехполей в текущейструктурезаписи.

В Delphi существуюти другие средствакоторые позволяютВам получитьту же самуюинформацию,но это самыйпростой способдоступа к именамполей в Run Time.

Свойство Fieldsпозволяет Вамполучить доступне только именамполей записи,но также и ксодержимомуполей. В нашемпримере, длявторой кнопкинапишем:


procedureTForm1.ValuesClick(Sender: TObject);

var

i:Integer;

begin

ListBox2.Clear;

fori := 0 to Table1.FieldCount - 1 do

ListBox2.Items.Add(Table1.Fields[i].AsString);

end;


Этот код добавляетсодержимоекаждого изполей во второйlistbox. Обратитевнимание, чтовновь счетчикизменяетсяот нуля до FieldCount- 1.

Свойство Fieldsпозволяет Вамвыбрать типрезультатанаписав Fields[N].AsString.Этот и несколькосвязанныхметодов обеспечиваютa простой и гибкийспособ доступак данным, связаннымис конкретнымполем. Вот списокдоступныхметодов которыйВы можете найтив описаниикласса TField:


propertyAsBoolean

propertyAsFloat

propertyAsInteger

propertyAsString

propertyAsDateTime


Всякий раз(когда это имеетсмысл), Delphi сможетсделать преобразования.Например, Delphiможет преобразовыватьполе Boolean к Integer илиFloat, или поле Integer кString. Но не будетпреобразовыватьString к Integer, хотя и можетпреобразовыватьFloat к Integer. BLOB и Memo поля- специальныеслучаи, и мы ихрассмотримпозже. Если Выхотите работатьс полями Date илиDateTime, то можетеиспользовать AsString и AsFloat для доступак ним.

Как было объясненовыше, свойствоFieldByName позволяетВам получитьдоступ к содержимомуопределенногополя простоуказав имяэтого поля:


S:= Table1.FieldByName(‘CustNo’).AsString;


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

Работас Данными

Следующиеметоды позволяютВам изменитьданные, связанныес TTable:


procedureAppend;

procedureInsert;

procedureCancel;

procedureDelete;

procedureEdit;

procedurePost;


Все эти методы- часть TDataSet, ониунаследованыи используютсяTTable и TQuery.

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

Имеется a типичнаяпоследовательность,которую Вымогли бы использоватьпри измененииполя текущейзаписи:


Table1.Edit;

Table1.FieldByName(‘CustName’).AsString:= ‘Fred’;

Table1.Post;


Первая строкапереводит БДв режим редактирования.Следующаястрока присваиваетзначение ‘Fred’полю ‘CustName’. Наконец,данные записываютсяна диск, когдаВы вызываетеPost.

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


Table1.Edit;

Table1.FieldByName(‘CustNo’).AsInteger:= 1234;

Table1.Next;


Общее правило,которому нужноследовать -всякий раз,когда Вы сдвигаетесьс текущей записи,введенные Вамиданные будутзаписаныавтоматически.Это означает,что вызовыFirst,Next,Prior иLastвсегда выполняютPost, еслиВы находилисьв режиме редактирования.Если Вы работаетес данными насервере итранзакциями,тогда правила,приведенныездесь, не применяются.Однако, транзакции- это отдельныйвопрос с ихсобственнымиспециальнымиправилами, Выувидите это,когда прочитаетео них в следующихуроках.

Темне менее, дажеесли Вы не работаетесо транзакциями,Вы можете всеже отменитьрезультатывашего редактированияв любое время,до тех пор, покане вызвалинапрямую иликосвенно методPost.Например, еслиВы перевелитаблицу в режимредактирования,и изменилиданные в одномили более полей,Вы можете всегдавернуть записьв исходноесостояниевызовом методаCancel.

Существуютдва метода,названныеAppend иInsert,который Выможете использоватьвсякий раз,когда Вы хотитедобавить новуюзапись в DataSet. Очевидноимеет большесмысла использоватьAppend дляDataSets которые неиндексированы,но Delphi не будетгенерироватьexception если Вы используетеAppend наиндексированнойтаблице. Фактически,всегда можноиспользоватьи AppendInsert.


Продемонстрируемработу методовна простомпримере. Чтобысоздать программу,используйтеTTable, TDataSource и TdbGrid. Открытьтаблицу COUNTRY. Затемразместитедве кнопки наформе и назовитеих ‘Insert’ и ‘Delete’. Когда Вы всесделаете, то должна получитьсяпрограмма,показаннаяна рис.5


Рис.5: Программаможет вставлятьи удалять записьиз таблицыCOUNTRY.


Следующимшагом Вы долженсвязать кодс кнопкамиInsert и Delete:


procedureTForm1.InsertClick(Sender: TObject);

begin

Table1.Insert;

Table1.FieldByName('Name').AsString:= 'Russia';

Table1.FieldByName('Capital').AsString:= 'Moscow';

Table1.Post;

end;


procedureTForm1.DeleteClick(Sender: TObject);

begin

Table1.Delete;

end;


Процедурапоказаннаяздесь сначалапереводиттаблицу в режимвставки (новаязапись с незаполненнымиполями вставляетсяв текущую позициюdataset). После вставкипустой записи,следующимэтапом нужноназначитьзначения одномуили большемуколичествуполей. Существует,конечно, несколькоразличных путейприсвоить этизначения. Внашей программеВы могли быпросто ввестиинформациюв новую записьчерез DBGrid. Или Вымогли бы разместитьна форме стандартнуюстроку ввода(TEdit) и затем установитькаждое полеравным значению,которое пользовательнапечатал вэтой строке:


Table1.FieldByName(‘Name’).AsString:= Edit1.Text;


Можно было быиспользоватькомпоненты,специальнопредназначенныедля работы сданными в DataSet.

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


Table1.FieldByName('Name').AsString:= 'Russia';


Один из интересныхмоментов в этомпримере этото, что нажатиекнопки Insert дваждыподряд автоматическивызывает exception‘Key Violation’. Чтобыисправить этуситуацию, Выдолжны либоудалить текущуюзапись, илиизменять поляName и Capital вновь созданнойзаписи.

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

Если послевызова Insert, Вырешаете отказатьсяот вставкиновой записи,то Вы можетевызвать Cancel. ЕслиВы сделаетеэто прежде, чемВы вызоветеPost, то все что Выввели послевызова Insert будетотменено, иdataset будет находитьсяв состоянии,которое былодо вызова Insert.

Однодополнительноесвойство, котороеВы должны иметьв виду называетсяCanModify.Если CanModify возвращаетFalse, то TTable находитьсяв состоянииReadOnly. В противномслучае CanModify возвращаетTrue и Вы можете редактироватьили добавлятьзаписи в неепо желанию.CanModify - само по себе‘read only’ свойство.Если Вы хотитеустановитьDataSet в состояниетолько на чтение(Read Only), то Вы должныиспользоватьсвойство ReadOnly,не CanModify.


ИспользованиеSetKey для поискав таблице

Для того,чтобы найтинекоторуювеличину втаблице, программистна Delphi можетиспользоватьдве процедурыSetKey иGotoKey.Обе эти процедурыпредполагают,что поле покоторому Выищете индексировано.Delphi поставляетсяс демонстрационнойпрограммойSEARCH, которая показывает,как использоватьэти запросы.

Чтобы создатьпрограммуSEARCH, поместитеTTable, TDataSource, TDBGrid, TButton, TLabel и TEdit наформу, и расположитеих как показанона рис.6. Назовитекнопку Search, и затемсоединитекомпонентыБД так, чтобыВы видели вDBGrid1 таблицу Customer.


Рис.6: ПрограммаSEARCH позволяетВам ввестиномер заказчикаи затем найтиего по нажатиюкнопки.


Вся функциональностьпрограммыSEARCH скрыта вединственномметоде, которыйприсоединенк кнопке Search. Этафункция считываетстроку, введеннуюв окно редактора,и ищет ее в колонкеCustNo, и наконецпомещает фокусна найденнойзаписи. В простейшемварианте, кодприсоединенныйк кнопке Search выглядиттак:


procedureTSearchDemo.SearchClick(Sender: TObject);

begin

Table1.SetKey;

Table1.FieldByName(’CustNo’).AsString:= Edit1.Text;

Table1.GotoKey;

end;


Первый вызовв этой процедуреустановитTable1 в режим поиска.Delphi должен знать,что Вы переключилисьв режим поискапросто потому,что свойствоFields используетсяпо другому вэтом режиме.Далее, нужноприсвоитьсвойству Fieldsзначение, котороеВы хотите найти.Для фактическоговыполненияпоиска нужнопросто вызыватьTable1.GotoKey.

Если Вы ищетене по первичномуиндексу файла,тогда Вы должныопределитьимя индекса,который Выиспользуетев свойствеIndexName. Например,если таблицаCustomer имеет вторичныйиндекс по полюCity, тогда Вы должныустановитьсвойство IndexNameравным именииндекса. КогдаВы будете искатьпо этому полю,Вы должны написать:


Table1.IndexName:= ’CityIndex’;

Table1.Active:= True;

Table1.SetKey;

Table1.FieldByName(’City’).AsString:= Edit1.Text;

Table1.GotoKey;


Запомните:поиск не будетвыполняться,если Вы не назначитеправильноиндекс (св-воIndexName). Кроме того,Вы должны обратитьвнимание, чтоIndexName - это свойствоTTable, и не присутствуетв других прямыхпотомках TDataSet илиTDBDataSet.

Когда Вы ищетенекотороезначение в БД, всегда существуетвероятностьтого, что поискокажется неудачным.В таком случаеDelphi будет автоматическивызывать exception,но если Вы хотитеобработать ошибку сами,то могли бынаписать примернотакой код:


procedureTSearchDemo.SearchClick(Sender: TObject);

begin

Cust.SetKey;

Cust.FieldByName('CustNo').AsString:=CustNoEdit.Text;

ifnot Cust.GotoKey then

raiseException.CreateFmt('Cannot find CustNo %g',

[CustNo]);

end;

Вкоде, показанномвыше, либо неверноеприсвоениеномера, либонеудача поискаавтоматическиприведут ксообщению обошибке ‘Cannotfind CustNo %g’.

Иногдатребуется найтине точно совпадающеезначение, аблизкое к нему,для этого следуетвместо GotoKeyпользоватьсяметодом GotoNearest.

Использованиефильтров дляограничениячисла записейв DataSet

ПроцедураApplyRangeпозволяет Вамустановитьфильтр, которыйограничиваетдиапазонпросматриваемыхзаписей. Например,в БД Customers, поле CustNoимеет диапазонот 1,000 до 10,000. ЕслиВы хотите видетьтолько те записи,которые имеютномер заказчикамежду 2000 и 3000, тоВы должныиспользоватьметод ApplyRange,и еще два связанныхс ним метода.Данные методыработают толькос индексированнымполем.


Вотпроцедуры,которые Выбудете чащевсего использоватьпри установкефильтров:


procedureSetRangeStart;

procedureSetRangeEnd;

procedureApplyRange;

procedureCancelRange;


Крометого, у TTable естьдополнительныеметоды дляуправленияфильтрами:


procedureEditRangeStart;

procedureEditRangeEnd;

procedureSetRange;


Дляиспользованияэтих процедурнеобходимо:


  1. Сначалавызвать SetRangeStartи использоватьсвойство Fields дляопределенияначала диапазона.

  2. Затемвызвать SetRangeEndи вновь использоватьсвойство Fields дляопределенияконца диапазона.

  3. Первыедва шага подготавливаютфильтр, и теперьвсе что Вамнеобходимо,это вызватьApplyRange,и новый фильтрвступит в силу.

  4. Когданужно прекратитьдействие фильтра- вызовитеCancelRange.


ПрограммаRANGE, которая естьсреди примеровDelphi, показывает,как использоватьэти процедуры.Чтобы создатьпрограмму,поместитеTTable, TDataSource и TdbGrid на форму.Соедините ихтак, чтобы Вывидеть таблицуCUSTOMERS из подкаталогаDEMOS. Затем поместитедва объектаTLabel на форму иназовите их‘Start Range’ и ‘End Range’. Затемположите наформу два объектаTEdit. Наконец, добавьтекнопки ‘ApplyRange’ и‘CancelRange’. Когда Вывсе выполните,форма имеетвид, как на рис.7


Рис.7: ПрограммаRANGE показываеткак ограничиватьчисло записейтаблицы дляпросмотра.


ПроцедурыSetRangeStartи SetRangeEndпозволяют Вамуказать первоеи последнеезначения вдиапазонезаписей, которыеВы хотите видеть.Чтобы начатьиспользоватьэти процедуры,сначала выполнитеdouble-click на кнопкеApplyRange,и создайтепроцедуру,которая выглядиттак:


procedureTForm1.ApplyRangeBtnClick(Sender: TObject);

begin

Table1.SetRangeStart;

ifRangeStart.Text '' then

Table1.Fields[0].AsString := RangeStart.Text;

Table1.SetRangeEnd;

ifRangeEnd.Text '' then

Table1.Fields[0].AsString:= RangeEnd.Text;

Table1.ApplyRange;

end;


СначалавызываетсяпроцедураSetRangeStart,которая переводиттаблицу в режимдиапазона(range mode). Затем Выдолжны определитьначало и конецдиапазона.Обратите внимание,что Вы используетесвойство Fields дляопределениядиапазона:


Table1.Fields[0].AsString:= RangeStart.Text;


Такое использованиесвойства Fields - этоспециальныйслучай, так каксинтаксис,показанныйздесь, обычноиспользуетсядля установкизначения поля.Этот специальныйслучай имеетместо толькопосле того, какВы перевелитаблицу в режимдиапазона,вызвав SetRangeStart.

Заключительныйшаг в процедурепоказаннойвыше - вызовApplyRange.Этот вызовфактическиприводит вашзапрос в действие.После вызоваApplyRange, TTable больше нев находитсяв режиме диапазона,и свойстваFields функционируеткак обычно.

Обработчиксобытия нажатиякнопки ‘CancelRange’:


procedureTForm1.CancelRangeBtnClick(Sender: TObject);

begin

Table1.CancelRange;

end;

Обновление(Refresh)

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

ФункцияRefreshсвязана с функциейOpen, втом смысле чтоона считываетданные, илинекоторую частьданных, связанныхс данной таблицей.Например, когдаВы открываететаблицу, Delphi считываетданные непосредственноиз файла БД.Аналогично,когда Вы Регенерируететаблицу, Delphi считываетданные напрямуюиз таблицы.Поэтому Выможете использоватьэту функцию,чтобы перепрочитатьтаблицу, еслиВы думаете чтоона могла измениться.Быстрее иэффективнее,вызывать Refresh,чем вызыватьClose изатем Open.

Имейте ввиду,однако, чтообновлениеTTable может иногдапривести кнеожиданнымрезультатам.Например, еслиa пользовательрассматриваетзапись, котораяуже была удалена,то она исчезнетс экрана в тотмомент, когдабудет вызванRefresh. Аналогично,если некийдругой пользовательредактировалданные, то вызовRefresh приведет кдинамическомуизменениюданных. Конечномаловероятно,что один пользовательбудет изменятьили удалятьзапись в товремя, как другойпросматриваетее, но это возможно.

Закладки(Bookmarks)

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


functionGetBookmark: TBookmark;

(устанавливаетзакладку втаблице)


procedureGotoBookmark(Bookmark: TBookmark);

(переходитна закладку)


procedureFreeBookmark(Bookmark: TBookmark);

(освобождаетпамять)


КакВы можете видеть,вызов GetBookmarkвозвращаетпеременную типа TBookmark. TBookmark содержитдостаточноеколичествоинформации,чтобы Delphi могнайти местоположениек которомуотносится этотTBookmark. Поэтому Выможете простопередаватьэтот TBookmark функцииGotoBookmark, и будетенемедленновозвращенык местоположению,связанномус этой закладкой.

Обратитевнимание, чтовызов GetBookmarkраспределяетпамять дляTBookmark, так что Выдолжны вызыватьFreeBookmarkдо окончаниявашей программы,и перед каждойпопыткой повторногоиспользованияTbookmark (в GetBookMark).

СозданиеСвязанныхКурсоров (Linkedcursors)

Связанныекурсоры позволяютпрограммистамопределитьотношение одинко многим(one-to-many relationship). Например,иногда полезносвязать таблицыCUSTOMER и ORDERS так, чтобыкаждый раз,когда пользовательвыбирает имязаказчика, тоон видит списокзаказов связанныхс этим заказчиком.Иначе говоря,когда пользовательвыбирает записьо заказчике,то он можетпросматриватьтолько заказы,сделанные этимзаказчиком.

ПрограммаLINKTBL демонстрирует,как создатьпрограммукоторая используетсвязанныекурсоры. Чтобысоздать программузаново, поместитедва TTable, два TDataSources идва TDBGrid на форму.Присоединитепервый набортаблице CUSTOMER, авторой к таблицеORDERS. Программав этой стадииимеет вид, показанныйна рис.8


Рис.8:ПрограммаLINKTBL показывает,как определитьотношения междудвумя таблицами.


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


  1. УстановитьсвойствоTable2.MasterSource = DataSource1

  2. УстановитьсвойствоTable2.MasterField = CustNo

  3. УстановитьсвойствоTable2.IndexName = CustNo


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

СвойствоMasterSource в Table2 определяетDataSource от которогоTable2 может получитьинформацию.То есть, онопозволяеттаблице ORDERS знать,какая записьв настоящеевремя являетсятекущей в таблицеCUSTOMERS.

Но тогда возникаетвопрос: Какаяеще информациянужна Table2 длятого, чтобыдолжным образомотфильтроватьсодержимоетаблицы ORDERS? Ответсостоит из двухчастей:


  1. Требуетсяимя поля покоторому связанныдве таблицы.

  2. Требуетсяиндекс по этомуполю в таблицеORDERS (в таблице‘многих записей’),которая будетсвязыватьсяс таблицейCUSTOMER(таблице вкоторой выбирается‘одна запись’).


Чтобы правильновоспользоватьсяинформациейописаннойздесь, Вы должнысначала проверить,что таблицаORDERS имеет нужныеиндексы. Еслиэтот индекспервичный,тогда не нужнодополнительноуказывать егов поле IndexName, и поэтомуВы можете оставитьэто поле незаполненнымв таблице TTable2(ORDERS). Однако, еслитаблица связанас другой черезвторичныйиндекс, то Выдолжны явноопределятьэтот индексв поле IndexName связаннойтаблицы.

В примере показанномздесь таблицаORDERS не имеет первичногоиндекса по полюCustNo, так что Выдолжны явнозадать в свойствеIndexName индекс CustNo.

Недостаточно,однако, простоyпомянуть имяиндекса, которыйВы хотитеиспользовать.Некоторыеиндексы могутсодержатьнесколькополей, так чтоВы должны явнозадать имяполя, по которомуВы хотите связатьдве таблицы.Вы должны ввестиимя ‘CustNo’ в свойствоTable2.MasterFields. Если Выхотите связатьдве таблицыбольше чем поодному полю,Вы должны внестив список всеполя, помещаясимвол ‘|’ междукаждым:


Table1.MasterFields:= ‘CustNo | SaleData | ShipDate’;


В данном конкретномслучае, выражение,показанноездесь, не имеетсмысла, так какхотя поля SaleData иShipDate также индексированы,но не дублируютсяв таблице CUSTOMER.Поэтому Выдолжны ввеститолько полеCustNo в свойствеMasterFields. Вы можетеопределитьэто непосредственнов редакторесвойств, илинаписать кодподобно показанномувыше. Крометого, поле (илиполя) связиможно получить,вызвав редакторсвязей - в ИнспектореОбъектов дваждыщелкните насвойство MasterFields(рис.10)


Рис.10:Редактор связейдля построениясвязанныхкурсоров.

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

Основныепонятия о TDataSource

Класс TDataSource используетсяв качествепроводникамежду TTable илиTQuery и компонентами,визуализирующимиданные, типаTDBGrid, TDBEdit и TDBComboBox (data-aware components). Вбольшинствеслучаев, все,что нужно сделатьс DataSource - это указатьв свойствеDataSet соответствующийTTable или TQuery. Затем,у data-aware компонентав свойствеDataSource указываетсяTDataSource, которыйиспользуетсяв настоящеевремя.

TDataSource также имеетсвойство Enabled, ионо может бытьполезно всякийраз, когда Выхотите временноотсоединить,например, DBGrid оттаблицы илизапроса. Этитребуется,например, еслинужно программнопройти черезвсе записи втаблице. Ведь,если таблицасвязана с визуальнымикомпонентами(DBGrid, DBEdit и т.п.), то каждыйраз, когда Вывызываете методTTable.Next, визуальныекомпонентыбудут перерисовываться.Даже если самосканированиев таблице двухили трех тысячзаписей незаймет многовремени, томожет потребоватьсязначительнобольше времени,чтобы столькоже раз перерисоватьвизуальныекомпоненты.В случаях подобныхэтому, лучшевсего установитьполе DataSource.Eabled в False. Этопозволит Вампросканироватьзаписи безперерисовкивизуальныхкомпонент. Этоединственнаяоперация можетувеличитьскорость внекоторыхслучаях нанесколько тысячпроцентов.

СвойствоTDataSource.AutoEdit указывает,переходит лиDataSet автоматическив режим редактированияпри вводе текстав data-aware объекте.

ИспользованиеTDataSource для проверкисостояния БД:

TDataSource имеет триключевых события,связанных ссостояниемБД


OnDataChange

OnStateChange

OnUpdateData


OnDataChangeпроисходитвсякий раз,когда Вы переходитена новую запись,или состояниеDataSet сменилосьс dsInactiveна другое, илиначато редактирование.Другими словами,если Вы вызываетеNext, Previous, Insert, или любойдругой запрос,который долженпривести кизменениюданных, связанныхс текущей записью,то произойдетсобытие OnDataChange. Еслив программенужно определитьмомент, когдапроисходитпереход надругую запись,то это можносделать в обработчикесобытия OnDataChange:


procedureTForm1.DataSource1DataChange(Sender: TObject; Field: TField);

begin

ifDataSource1.DataSet.State = dsBrowse then begin

DoSomething;

end;

end;


СобытиеOnStateChangeсобытие происходитвсякий раз,когда изменяетсятекущее состояниеDataSet. DataSet всегда знает,в каком состояниион находится.Если Вы вызываетеEdit, Append или Insert, то TTableзнает, что онтеперь находитсяв режиме редактирования(dsEdit или dsInsert). Аналогично,после того, какВы делаетеPost, то TTable знает чтоданные большене редактируется,и переключаетсяобратно в режимпросмотра(dsBrowse).

Dataset имеет шестьразличныхвозможныхсостояний,каждое из которыхвключено вследующемперечисляемомтипе:


TDataSetState= (dsInactive, dsBrowse, dsEdit, dsInsert,

dsSetKey,dsCalcFields);


В течение обычногосеанса работы,БД часто меняетсвое состояниемежду Browse, Edit, Insert идругими режимами.Если Вы хотитеотслеживатьэти изменения,то Вы можетереагироватьна них написавпримерно такойкод:


procedureTForm1.DataSource1StateChange(Sender: TObject);

var

S:String;

begin

caseTable1.State of

dsInactive:S := 'Inactive';

dsBrowse:S := 'Browse';

dsEdit:S := 'Edit';

dsInsert:S := 'Insert';

dsSetKey:S := 'SetKey';

dsCalcFields:S := 'CalcFields';

end;

Label1.Caption:= S;

end;


OnUpdateDataсобытие происходитперед тем, какданные в текущейзаписи будутобновлены.Например,OnUpdateEvent будет происходитьмежду вызовомPost и фактическимобновлениеминформациина диске.

События, генерируемыеTDataSource могут бытьочень полезны.Иллюстрациейэтого служитследующийпример. Этапрограммаработает стаблицей COUNTRY, ивключает TTable,TDataSource, пять TEdit, шестьTLlabel, восемь кнопоки панель. Действительноерасположениеэлементовпоказано нарис.11. Обратитевнимание, чтошестой TLabel расположенна панели внизуглавной формы.


Рис.11: ПрограммаSTATE показывает,как отслеживатьтекущее состояниетаблицы.


Длявсех кнопокнапишите обработчики,вроде:


procedureTForm1.FirstClick(Sender: TObject);

begin

Table1.First;

end;


В данной программеесть одна маленькаяхитрость, которуюВы должны понять,если хотитеузнать, какработает программа.Так как естьпять отдельныхредакторовTEdit на главнойформе, то хотелосьбы иметь некоторыйспособ обращатьсяк ним быстрои легко. Одинпростой способсостоит в том,чтобы объявитьмассив редакторов:


Edits:array[1..5] of TEdit;


Чтобы заполнитьмассив, Вы можетев событии OnCreateглавной формынаписать:


procedureTForm1.FormCreate(Sender: TObject);

var

i: Integer;

begin

for i := 1 to 5 do

Edits[i] :=TEdit(FindComponent('Edit' + IntToStr(i)));

Table1.Open;

end;


Код показанныйздесь предполагает,что первыйредактор, которыйВы будетеиспользоватьназовем Edit1, второйEdit2, и т.д. Существованиеэтого массивапозволяет оченьпросто использоватьсобытие OnDataChange, чтобысинхронизироватьсодержаниеобъектов TEdit ссодержимомтекущей записив DataSet:


procedureTForm1.DataSource1DataChange(Sender: TObject;

Field:TField);

var

i:Integer;

begin

fori := 1 to 5 do

Edits[i].Text:= Table1.Fields[i - 1].AsString;

end;


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

Всякий раз,когда вызываетсяPost, нужно выполнитьпротивоположноедействие, тоесть взятьинформациюиз редакторови поместитьее в текущуюзапись. Выполнитьэто действие,проще всегов обработчикесобытияTDataSource.OnUpdateData, котороепроисходитвсякий раз,когда вызываетсяPost:


procedureTForm1.DataSource1UpdateData(Sender: TObject);

var

i:Integer;

begin

fori := 1 to 5 do

Table1.Fields[i- 1].AsString := Edits[i].Text;

end;


Программабудет автоматическипереключатсяв режим редактированиякаждый раз,когда Вы вводитечто-либо в одномиз редакторов.Это делаетсяв обработчикесобытия OnKeyDown (укажитеэтот обработчикко всем редакторам):


procedureTForm1.Edit1KeyDown(Sender: TObject;

varKey: Word; Shift: TShiftState);

begin

ifDataSource1.State dsEdit then

Table1.Edit;

end;


Этоткод показывает,как Вы можетеиспользоватьсв-во StateDataSource, чтобы определитьтекущий режимDataSet.

Обновлениеметки в статуснойпанели происходитпри изменениисостояниятаблицы:


procedureTForm1.DataSource1StateChange(Sender: TObject);

var

s : String;

begin

caseDataSource1.State of

dsInactive: s:='Inactive';

dsBrowse :s:='Browse';

dsEdit :s:='Edit';

dsInsert :s:='Insert';

dsSetKey :s:='SetKey';

dsCalcFields :s:='CalcFields';

end;

Label6.Caption:=s;

end;


Данная программаявляетсядемонстрационнойи ту же задачуможно решитьгораздо проще,если использоватьобъекты TDBEdit.


ОтслеживаниесостоянияDataSet

В предыдущейчасти Вы узнали,как использоватьTDataSource, чтобы узнатьтекущее состоянииTDataSet. ИспользованиеDataSource - это простойпуть выполненияданной задачи.Однако, еслиВы хотите отслеживатьэти событиябез использованияDataSource, то можетенаписать своиобработчикисобытий TTable иTQuery:


propertyOnOpen

propertyOnClose

propertyBeforeInsert

propertyAfterInsert

propertyBeforeEdit

propertyAfterEdit

propertyBeforePost

propertyAfterPost

propertyOnCancel

propertyOnDelete

propertyOnNewRecord


Большинствоэтих свойствочевидны. СобытиеBeforePost функциональноподобно событиюTDataSource.OnUpdateData, котороеобъяснено выше.Другими словами,программа STATEработала быточно также, если бы Вы отвечалине на DataSource1.OnUpdateData а наTable1.BeforePost. Конечно,в первом случаеВы должен иметь TDataSource на форме, вто время, какво втором этогоне требуется.

32

Урок4: ОбъектTTable

Создание базданных в Delphi


Урок 5: КомпонентTTable. Созданиетаблиц
с помощьюкомпонентаTTable


Содержаниеурока 5:


Созданиетаблиц с помощьюкомпонентаTTable2

Заключение6


Обзор


Наэтом небольшомуроке мы завершимизучение возможностейсоздания таблиц.Как Вы помните,мы уже освоилидва способасоздания таблиц- с помощью утилитыDatabase Desktop, входящейв поставкуDelphi и с помощьюSQL-запросов, которыеможно использоватькак в WISQL (Windows Interactive SQL -клиентскаячасть Local InterBase), таки в компонентеTQuery.Теперь мы рассмотрим,как можно создаватьлокальныетаблицы в режимевыполненияс помощью компонентаTTable.

Созданиетаблиц с помощьюкомпонентаTTable

Длясоздания таблицкомпонентTTableимеет методCreateTable.Этот методсоздает новуюпустую таблицузаданной структуры.Данный метод(процедура)может создаватьтолько локальныетаблицы форматаdBase или Paradox.

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


var

Table1: TTable;

...

Table1:=TTable.Create(nil);

...


Передвызовом методаCreateTableнеобходимоустановитьзначения свойств

  • TableType-тип таблицы

  • DatabaseName-база данных

  • TableName-имя таблицы

  • FieldDefs-массив описанийполей

  • IndexDefs-массив описанийиндексов.


СвойствоTableTypeимеет тип TTableTypeи определяеттип таблицыв базе данных.Если это свойствоустановленов ttDefault,тип таблицыопределяетсяпо расширениюфайла, содержащегоэту таблицу:

  • Расширение.DBили без расширения:таблица Paradox

  • Расширение.DBF: таблица dBASE

  • Расширение.TXT: таблица ASCII (текстовыйфайл).


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

  • ttASCII:текстовый файл

  • ttDBase:таблица dBASE

  • ttParadox:таблица Paradox.


СвойствоDatabaseNameопределяетбазу данных,в которой находитсятаблица. Этосвойство можетсодержать:

  • BDE алиас

  • директорийдля локальныхБД

  • директорийи имя файлабазы данныхдля Local InterBase

  • локальныйалиас, определенныйчерез компонентTDatabase.


СвойствоTableNameопределяетимя таблицыбазы данных.


СвойствоFieldDefs(имеющее типTFieldDefs)для существующейтаблицы содержитинформациюобо всех поляхтаблицы. Этаинформациядоступна тольков режиме выполненияи хранится ввиде массиваэкземпляровкласса TFieldDef,хранящих данныео физическихполях таблицы(т.о. вычисляемыена уровне клиентаполя не имеютсвоего объектаTFieldDef).Число полейопределяетсясвойствомCount,а доступ к элементаммассива осуществляетсячерез свойствоItems:


propertyItems[Index: Integer]: TFieldDef;


При созданиитаблицы, передвызовом методаCreateTable,нужно сформироватьэти элементы.Для этого укласса TFieldDefsимеется методAdd:


procedureAdd(const Name: string; DataType: TFieldType; Size: Word;Required: Boolean);


Параметр Name,имеющий типstring,определяетимя поля. ПараметрDataType(тип TFieldType)обозначаеттип поля. Онможет иметьодно из следующихзначений, смыслкоторых ясениз их наименования:

TFieldType= (ftUnknown, ftString, ftSmallint, ftInteger, ftWord, ftBoolean,ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime, ftBytes,ftVarBytes, ftBlob, ftMemo,

ftGraphic);

Параметр Size(тип word)представляетсобой размерполя. Этотпараметр имеетсмысл толькодля полей типаftString,ftBytes,ftVarBytes,ftBlob,ftMemo,ftGraphic,размер которыхможет сильноварьироваться.Поля остальныхтипов всегдаимеют строгофиксированныйразмер, так чтоданный параметрдля них непринимаетсяво внимание.Четвертыйпараметр - Required- определяет,может ли полеиметь пустоезначение призаписи в базуданных. Еслизначение этогопараметра -true,то поле является“требуемым”,т.е. не можетиметь пустогозначения. Впротивномслучае полене является“требуемым”и, следовательно,допускаетзапись значенияNULL. Отметим, чтов документациипо Delphi и online-справочникедопущена ошибка- там отсутствуетупоминаниео четвертомпараметре дляметода Add.


ЕслиВы желаетеиндексироватьтаблицу поодному илинесколькимполям, используйтеметод Addдля свойстваIndexDefs,которое, какможно догадаться,также являетсяобъектом, т.е.экземпляромкласса TIndexDefs.Свойство IndexDefsдля существующейтаблицы содержитинформациюобо всех индексахтаблицы. Этаинформациядоступна тольков режиме выполненияи хранится ввиде массиваэкземпляровкласса TIndexDef,хранящих данныеоб индексахтаблицы. Числоиндексов определяетсясвойствомCount,а доступ к элементаммассива осуществляетсячерез свойствоItems:


propertyItems[Index: Integer]: TIndexDef;


Метод Addкласса TIndexDefsимеет следующийвид:


procedure Add(constName, Fields: string;
Options: TIndexOptions);


Параметр Name,имеющий типstring,определяетимя индекса.Параметр Fields(также имеющийтип string)обозначаетимя поля, котороедолжно бытьиндексировано,т.е. имя индексируемогополя. Составнойиндекс, использующийнесколькополей, можетбыть задансписком именполей, разделенныхточкой с запятой“;”,например:‘Field1;Field2;Field4’.Последнийпараметр - Options- определяеттип индекса.Он может иметьнабор значений,описываемыхтипом TIndexOptions:


TIndexOptions = setof (ixPrimary, ixUnique, ixDescending,

ixCaseInsensitive, ixExpression);


Поясним этизначения. ixPrimaryобозначаетпервичный ключ,ixUnique- уникальныйиндекс, ixDescending- индекс, отсортированныйпо уменьшениюзначений (длястрок - в порядке,обратномалфавитному),ixCaseInsensitive- индекс, “нечувствительный”к региструбукв, ixExpression- индекс повыражению.Отметим, чтоупоминаниео последнемзначении такжеотсутствуетв документациии online-справочнике.Опция ixExpressionпозволяет длятаблиц форматаdBase создаватьиндекс по выражению.Для этого достаточнов параметреFieldsуказать желаемоевыражение,например:'Field1*Field2+Field3'.Вообще говоря,не все опциииндексов применимыко всем форматамтаблиц. Нижемы приведемсписок допустимыхзначений длятаблиц dBase и Paradox:


Опциииндексов dBASE Paradox

---------------------------------------

ixPrimary

ixUnique

ixDescending

ixCaseInsensitive

ixExpression


Необходимопридерживатьсяуказанногопорядка примененияопций индексовво избежаниенекорректнойработы. Следуетотметить, чтодля форматаParadox опция ixUniqueможет использоватьсятолько вместес опцией ixPrimary(см. пример надиске - Рис. 1).

Итак,после заполнениявсех указанныхвыше свойстви вызова методовAddдля FieldDefsи IndexDefsнеобходимовызвать методкласса TTable- CreateTable:


with Table1 do

begin

DatabaseName:='dbdemos';

TableName:='mytest';

TableType:=ttParadox;

{Создатьполя}

with FieldDefsdo

begin

Add('Surname',ftString, 30, true);

Add('Name',ftString, 25, true);

Add('Patronymic',ftString, 25, true);

Add('Age',ftInteger, 0, false);

Add('Weight',ftFloat, 0, false);

end;

{Сгенерироватьиндексы}

with IndexDefsdo

begin

Add('I_Name','Surname;Name;Patronymic',
[ixPrimary, ixUnique]);

Add('I_Age','Age', [ixCaseInsensitive]);

end;

CreateTable;

end;

Рис. 1: ПрограммаCREATABL демонстрирует технику созданиятаблиц во времявыполнения



Индексыможно сгенерироватьи не только присоздании таблицы.Для того чтобысгенерироватьиндексы длясуществующейтаблицы, нужновызвать методAddIndexкласса TTable,набор параметровкоторого полностьюповторяет наборпараметровдля метода Addкласса TIndexDefs:


procedureAddIndex(const Name, Fields: string;
Options:TIndexOptions);


При этом дляметода AddIndexсправедливывсе замечанияпо поводу записиполей и опцийиндексов, сделанныевыше.


Заключение

Итак,мы познакомилисьс еще однимспособом созданиятаблиц - способом,использующимметод CreateTableкласса TTable.Использованиеданного способапридаст Вашемуприложениюмаксимальнуюгибкость, и Высможете строитьлокальныетаблицы “налету”. Сопутствующимметодом являетсяметод AddIndexкласса TTable,позволяющийсоздаватьиндексы дляуже существующейтаблицы. Подчеркнемеще раз, что данный способприменим толькодля локальныхтаблиц.

6

Урок5: Создание таблицс помощьюкомпонентаTTable



Создание базданных в Delphi


Урок6: Объект TQuery

СодержаниеУрока 6:

СодержаниеУрока 6:1

КраткийОбзор2

Основныепонятия о TQuery2

СвойствоSQL3

TQuery и Параметры6

Передачапараметровчерез TDataSource 10

Выполнениесоединениянесколькихтаблиц.12

Open илиExecSQL?14

Специальныесвойства TQuery15


КраткийОбзор

В этой главеВы узнаетенекоторыеосновные понятияо запросах(queries) и транзакциях.Это достаточноширокие понятия,поэтому обсуждениеразбито наследующиеосновные части:


  • ОбъектTQuery.

  • ИспользованиеSQL с локальными удаленнымсерверами(Select, Update, Delete и Insert).

  • ИспользованиеSQL для созданияобъединения(joins), связанныхкурсоров (linkedcursors) и программ,которые ведутпоиск заданныхзаписей.


СокращениеSQL означаетStructured Query Language - ЯзыкСтруктурированныхЗапросов, иобычно произноситьсялибо как "Sequel"либо " Ess Qu El”. Однако,как бы Вы егони произносили,SQL - это мощныйязык БД, которыйлегко доступениз Delphi, но которыйотличаетсяот родногоязыка Delphi. Delphi можетиспользоватьутвержденияSQL для просмотра таблиц, выполнятьобъединениетаблиц, создаватьотношенияодин-ко-многим,или исполнитьпочти любоедействие, котороемогут сделатьваши основныеинструментыБД. Delphi поставляетсяс Local SQL, так что Выможете выполнятьзапросы SQL приработе с локальнымитаблицами, бездоступа к SQLсерверу.

Delphi обеспечиваетподдержку “passthrough SQL”, это означаетто, что Вы можетесоставлятьпредложенияSQL и посылатьих непосредственносерверам Oracle,Sybase, Inrterbase и другим.“Pass through SQL” - это мощныймеханизм подвум причинам:


  1. Большинствосерверов могутобрабатыватьSQL запросы оченьбыстро, а этоозначает, чтоиспользуя SQLдля удаленныхданных, Вы получитеответ оченьбыстро.

  2. Есть возможностьсоставлятьSQL запросы, которыезаставят серверисполнитьспециализированныезадачи, недоступныечерез роднойязык Delphi.


Перед чтениемэтой статьиВы должны иметь,по крайнеймере, элементарноепонятие о серверахи различияхмежду локальнымии удаленными(remote) данными.

Основныепонятия о TQuery

ПредыдущийУрок был, в основном,посвящен объектуTTable, который служитдля доступак данным. ПрииспользованииTTable, возможендоступ ко всемунабору записейиз одной таблицы.В отличие отTTable, TQuery позволяетпроизвольнымобразом (в рамкахSQL) выбрать наборданных дляработы с ним.Во многом, методикаработы с объектомTQuery похожа наметодику работыс TTable, однако естьсвои особенности.


Вы может создатьSQL запрос используякомпонентTQuery следующимспособом:

  1. НазначитеПсевдоним(Alias) DatabaseName.

  2. Используйтесвойство SQL чтобыввести SQL запростипа
    “Select * from Country”.

  3. Установитесвойство Active вTrue


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


Две основныхвещи, которыеВы должны понятьпрежде, чемперейти дальше:


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

  • Delphi используетpass through SQL, поэтомудля разных SQLсерверов синтаксисможет бытьнесколькоразным. ВерсияSQL для локальныхтаблиц (Local SQL) оченьсильно урезан,по сравнениюсо стандартом.Чтобы узнатьо его возможностях,Вы должны прочитатьне только этустатью, но такжефайл LOCALSQL.HLP.


Вы увидите,что объектTQuery один из наиболееполезных игибких компонентов,доступных вDelphi. С ним Вы сможетевоспользоватьсявсей мощью,предоставляемойлидерами средипромышленныхSQL серверов, вродеInrterBase, Oracle или Sybase.

СвойствоSQL

Свойство SQL -вероятно, самаяважная частьTQuery. Доступ к этомусвойству происходитлибо черезИнспекторОбъектов вовремя конструированияпроекта (design time), илипрограммново время выполненияпрограммы (runtime).

Интересней,конечно, получитьдоступ к свойствуSQL во время выполнения,чтобы динамически изменять запрос.Например, еслитребуетсявыполнить триSQL запроса, тоне надо размещатьтри компонентаTQuery на форме. Вместоэтого можноразместитьодин и простоизменять свойствоSQL три раза. Наиболееэффективный,простой и мощныйспособ - сделатьэто черезпараметризованныезапросы, которыебудут объясненыв следующейчасти. Однако,сначала исследуемосновные особенностисвойства SQL, апотом рассмотримболее сложныетемы, типа запросовс параметрами.

Свойство SQL имееттип TStrings, которыйозначает чтоэто ряд строк,сохраняемыхв списке. Списокдействуеттакже, как имассив, но,фактически,это специальныйкласс с собственнымиуникальнымивозможностями.В следующихнесколькихабзацах будутрассмотренынаиболее частоиспользуемыесвойства.

При программномиспользованииTQuery, рекомендуетсясначала закрытьтекущий запроси очиститьсписок строкв свойстве SQL:


Query1.Close;

Query1.SQL.Clear;


Обратите внимание,что всегдаможно “безопасно”вызвать Close. Дажев том случае,если запросуже закрыт,исключительнаяситуациягенерироватьсяне будет.

Следующий шаг- добавлениеновых строкв запрос:


Query1.SQL.Add(‘Select* from Country’);

Query1.SQL.Add(‘whereName = ’’Argentina’’’);


Метод Add используетсядля добавленияодной или несколькихстрок к запросуSQL. Общий объемограничентолько количествомпамяти на вашеймашине.

Чтобы Delphi отработалзапрос и возвратилкурсор, содержащийрезультат ввиде таблицы,можно вызватьметод:


Query1.Open;


ДемонстрационнаяпрограммаTHREESQL показываетэтот процесс(см Рис.1)


Рис.1: ПрограммаTHREESQL показывает,как сделатьнесколькозапросов спомощью единственногообъекта TQuery.


ПрограммаTHREESQL используетособенностьлокальногоSQL, который позволяетиспользоватьшаблоны поискабез учета регистра(case insensitive). Например,следующий SQLзапрос:


Select* form Country where Name like ’C%’


возвращаетDataSet, содержащийвсе записи, гдеполе Name начинаетсяс буквы ‘C’.Следующийзапрос позволитувидеть всестраны, в названиикоторых встречаетсябуква ‘C’:


Select* from Country where Name like ‘%C%’;


Вот запрос,которое находитвсе страны,название которыхзаканчиваетсяна ‘ia’:


Select* from Country where Name like ‘%ia’;


Одна из полезныхособенностейсвойства SQL - этоспособностьчитать файлы,содержащиетекст запросанепосредственнос диска. Этаособенностьпоказана впрограммеTHREESQL.

Вот как этоработает. Вдиректориис примерамик данному урокуесть файл срасширениемSQL. Он содержаттекст SQL запроса.ПрограммаTHREESQL имеет кнопкус названиемLoad, которая позволяетВам выбратьодин из этихфайлов и выполнятьSQL запрос, сохраненныйв этом файле.

КнопкаLoad имеет следующийметод для событияOnClick:


procedureTForm1.LoadClick(Sender: TObject);

begin

ifOpenDialog1.Execute then

withQuery1 do begin

Close;

SQL.LoadFromFile(OpenDialog1.FileName);

Open;

end;

end;


Метод LoadClick сначалазагружаеткомпонентуOpenDialog и позволяетпользователювыбрать файлс расширениемSQL. Если файлвыбран, текущийзапрос закрывается,выбраный файлзагружаетсяс диска в св-воSQL, запрос выполняетсяи результатпоказываетсяпользователю.

TQuery и Параметры

Delphi позволяетсоставить“гибкую” формузапроса, называемуюпараметризованнымзапросом. Такиезапросы позволяютподставитьзначение переменнойвместо отдельныхслов в выражениях“where” или “insert”. Этапеременнаяможет бытьизменена практическив любое время.(Если используетсялокальный SQL,то можно сделатьзамену почтилюбого словав утвержденииSQL, но при этомта же самаявозможностьне поддерживаетсябольшинствомсерверов.)

Перед тем, какначать использоватьпараметризованныезапросы, рассмотримснова одно изпростых вышеупомянутыхпредложенийSQL:


Select* from Country where Name like ’C%’


Можно превратитьэто утверждениев параметризованныйзапрос заменивправую частьпеременнойNameStr:


select* from County where Name like :NameStr


В этом предложенииSQL, NameStr не являетсяпредопределеннойконстантойи может изменятьсялибо во времядизайна, либово время выполнения.SQL parser (программа,которая разбираеттекст запроса)понимает, чтоон имеет делос параметром,а не константойпотому, чтопараметрупредшествуетдвоеточие":NameStr". Это двоеточиесообщает Delphi онеобходимостизаменить переменнуюNameStr некоторойвеличиной,которая будетизвестна позже.

Обратите внимание, слово NameStr быловыбрано абсолютнослучайно.Использоватьможно любоедопустимоеимя переменной, точно также,как выбираетсяидентификаторпеременнойв программе.

Есть два путиприсвоитьзначение переменнойв параметризованномзапросе SQL. Одинспособ состоитв том, чтобыиспользоватьсвойство Paramsобъекта TQuery. Второй- использоватьсвойство DataSourceдля полученияинформациииз другогоDataSet. Вот ключевыесвойства длядостиженияэтих целей:


propertyParams[Index: Word];

functionParamByName(const Value: string);

propertyDataSource;


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


  1. ЗакрытьTQuery

  2. Подготовитьобъект TQuery, вызвавметод Prepare

  3. Присвоитьнеобходимыезначения свойствуParams

  4. ОткрытьTQuery


Второй шагвыполняетсяв том случае,если данныйтекст запросавыполняетсявпервые, в дальнейшемего можно опустить.


Вот фрагменткода, показывающийкак это можетбыть выполненопрактически:


Query1.Close;

Query1.Prepare;

Query1.Params[0].AsString:= ‘Argentina’;

Query1.Open;


Этот код можетпоказатьсянемного таинственным.Чтобы понятьего, требуетсявнимательныйпострочныйанализ. Прощевсего начатьс третьей строки,так как свойствоParams является“сердцем” этогопроцесса.

Params - это индексированноесвойство, котороеимеет синтаксискак у свойстваFields для TDataSet. Например,можно получитьдоступ к первойпеременнойв SQL запросе,адресуя нулевойэлемент в массивеParams:


Params[0].AsString:= ‘”Argentina”’;


ЕслипараметризованныйSQL запрос выглядиттак:


select* from Country where Name = :NameStr


токонечный результат(т.е. то, что выполнитсяна самом деле)- это следующеепредложениеSQL:


select* from Country where Name = “Argentina”


Все, что произошло,это переменной:NameStr было присвоено значение "Аргентина"через свойствоParams. Таким образом,Вы закончилипостроениепростого утвержденияSQL.


Еслив запросе содержитсяболее одногопараметра, тодоступатьсяк ним можноизменяя индекс у свойстваParams


Params[1].AsString:= ‘SomeValue’;


либоиспользуядоступ по именипараметра


ParamByName(‘NameStr’).AsString:=’”Argentina”’;


Итак, параметризованныеSQL запросы используютпеременные,которые всегданачинаютсяс двоеточия,определяяместа, кудабудут переданызначения параметров.

Прежде, чемиспользоватьпеременнуюParams, сначала можновызвать Prepare. Этотвызов заставляетDelphi разобратьваш SQL запроси подготовитьсвойство Params так,чтобы оно "былоготово принять”соответствующееколичествопеременных.Можно присвоитьзначение переменнойParams без предварительноговызова Prepare, ноэто будет работатьнесколькомедленнее.

После того,как Вы вызывалиPrepare, и после того,как присвоилинеобходимыезначения переменнойParams, Вы должнывызвать Open, чтобызакончитьпривязку переменныхи получитьжелаемый DataSet. Внашем случае,DataSet должен включатьзаписи где вполе “Name” стоит“Argentina”.

Рассмотримработу с параметрамина примере(программаPARAMS.DPR). Для созданияпрограммы,разместитена форме компонентыTQuery, TDataSource, TDBGrid и TTabSet. Соединитекомпонентыи установитев свойствеTQuery.DatabaseName псевдонимDBDEMOS. См. рис.2


Рис.2: ПрограммаPARAMS во время дизайна.

В обработчикесобытия дляформы OnCreate напишемкод, заполняющийзакладки дляTTabSet, кроме того,здесь подготавливаетсязапрос:


procedureTForm1.FormCreate(Sender: TObject);

var

i: Byte;

begin

Query1.Prepare;

fori:=0 to 25 do

TabSet1.Tabs.Add(Chr(Byte('A')+i));

end;


ТекстSQL запроса вкомпонентеQuery1:


select* from employee where LastName like :LastNameStr


Запрос выбираетзаписи из таблицыEMPLOYEE, в которыхполе LastName похоже(like) на значениепараметра:LastNameStr. Параметрбудет передаватьсяв момент переключениязакладок:


procedureTForm1.TabSet1Change(Sender: TObject;

NewTab: Integer;

var AllowChange:Boolean);

begin

with Query1 dobegin

Close;

Params[0].AsString:=

'"'+TabSet1.Tabs.Strings[NewTab]+'%"';

Open;

end;

end;


Рис.3:ПрограммаPARAMS во время выполнения.


Передачапараметровчерез TDataSource

В предыдущемУроке Вы виделиспособ созданияотношенияоднин-ко-многиммежду двумятаблицами.Теперь речьпойдет о выполнениитого же самогодействия сиспользованиемобъекта TQuery. Этотспособ болеегибок в томотношении, чтоон не требуетиндексациипо полям связи.

Объект TQuery имеетсвойство DataSource,которое можетиспользоватьсядля того, чтобысоздать связьс другим DataSet. Неимеет значения,является лидругой DataSet объектомTTable, TQuery, или некоторыйдругим потомкомTDataSet. Все что нужнодля установлениясоединения- это удостовериться,что у того DataSetесть связанныйс ним DataSource.

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

Рассмотритеследующийпараметризованныйзапрос:


select* from Orders where CustNo = :CustNo


В этом запросе:CustNo - связывающаяпеременная,которой должнобыть присвоенозначение изнекоторогоисточника.Delphi позволяетиспользоватьполе TQuery.DataSource чтобы указать другойDataSet, которыйпредоставитэту информациюавтоматически.Другими словами,вместо того,чтобы использоватьсвойство Params и“вручную”присваиватьзначения переменной,эти значенияпеременноймогут бытьпросто взятыавтоматическииз другой таблицы. Кроме того,Delphi всегда сначалапытается выполнитьпараметризованныйзапрос используясвойство DataSource,и только потом(если не былонайдено какое-тозначение параметра)будет пытатьсяполучить значениепеременнойиз свойстваParams. При полученииданных из DataSourceсчитается, чтопосле двоеточиястоит имя поляиз DataSource. При изменениитекущей записив главном DataSetзапрос будетавтоматическипересчитываться.

Давайте переделаемпример из прошлогоурока (LINKTBL - связываниедвух таблиц).Создайте новыйпроект, положитена форму одиннабор TTable, TDataSource иTDBGrid. Привяжитеего к таблицеCUSTOMER. Положите наформу второйнабор - TQuery, TDataSource иTDBGrid и свяжитеобъекты междусобой. (см рис.4).

В свойстве SQLнаберите текстзапроса:

select* from Orders where CustNo = :CustNo

Всвойстве DatabaseNameдля Query1 укажитеDBDEMOS.

Всвойстве DataSourceдля Query1 укажитеDataSource1.

ПоставьтеActive = True и запуститепрограмму.


Рис.4:ПрограммаLINKQRY - связанныекурсоры с помощьюSQL


Выполнениесоединениянесколькихтаблиц.

Вы видели чтотаблицы CUSTOMERS иORDERS связаны вотношенииодин-ко-многим,основанномуна поле CustNo. ТаблицыORDERS и ITEMS также связаныотношенииодин-ко-многим,только черезполе OrderNo.

Более конкретно,каждый заказкоторый существуетв таблице ORDERSбудет иметьнесколькозаписей в таблицеITEMS, связанныхс этим заказом.Записи из таблицыITEMS определяюттип и количествоизделий, связанныхс этим заказом.

Пример.

НектоИванов Ф.П. 1 мая1995г. заказалследующее:

  1. Гайка4х-угольная - 50 штук

  2. Вентиль- 1 штука


Анекто СидорчукЮ.Г. 8 декабря1994г. заказал:

  1. М/схемаКР580 ИК80 - 10 штук

  2. ТранзисторКТ315 - 15 штук

  3. Мотокпровода - 1 штука


В ситуацииподобной этой,иногда прощевсего "соединить"данные из таблицORDERS и ITEMS так, чтобырезультирующийDataSet содержалинформациюиз обеих таблиц:


ИвановФ.П. 1 мая 1995г Гайка4х-угольная50штук

ИвановФ.П. 1 мая 1995г Вентиль 1 штука

СидорчукЮ.Г. 8 декабря1994гМ/схема КР580ИК80 10 штук

СидорчукЮ.Г. 8 декабря1994г ТранзисторКТ315 15 штук

СидорчукЮ.Г. 8 декабря1994гМоток провода1 штука


Слияние этихдвух таблицназывается"соединение"и это одно изфундаментальныхдействий, которыеВы можете выполнитьна наборе двухили большетаблиц.

Взяв таблицыORDERS и ITEMS из подкаталогаDEMOS\DATA, их можносоединить ихтаким путем,что поля CustNo, OrderNo иSaleDate из таблицыORDERS будут “слиты”с полями PartNo и Qtyиз таблицыITEMS и сформируютновый DataSet, содержащийвсе пять полей.Grid содержащийрезультирующийDataSet показан нарис.5


Рис.5: Соединениетаблиц ORDERS и ITEMS можетбыть сделанотак, что формируетсяновый DataSet содержащийполя из каждойтаблицы.


Имеется существенноеразличие междусвязаннымикурсорами исоединеннымитаблицами.Однако ониимеют две общиечерты:


  • Ите, и другиеиспользуютдве или болеетаблиц

  • Каждыйтаблица связанас другой поодному илиболее одинаковыхполей.


Соединениетаблиц ORDERS и ITEMS можетбыть выполненоединственнымSQL запросом, которыйвыглядит так:


select

O.CustNo, O.OrderNo,O.SaleDate, I.PartNo, I.Qty

fromOrders O, Items I

whereO.OrderNo = I.OrderNo


Этотзапрос состоитиз четырехразличныхчастей:


  1. ВыражениеSelect определяет,что Вы хотитеполучить - курсор, содержащийнекоторуюформу DataSet.

  2. Затем идетсписок полейкоторые Выхотите включитьв dataset. Этот списоквключает поляCustNo, OrderNo, SaleDate, PartNo и Qty. Первыетри поля изтаблицы ORDERS, адва других -из таблицыITEMS.

  3. Выражениеfromобъявляет, чтоВы работаетес двумя таблицами,одна называетсяORDERS, а другая ITEMS.Для краткости,в запросеиспользуетсяособенностьSQL, которая позволяетВам ссылатьсяна таблицуORDERS буквой O, а натаблицу ITEMS буквойI.

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

Open или ExecSQL?

После того,как составленSQL запрос, естьдва различныхспособа выполнитьего. Если Выхотите получитькурсор, то нужновызывать Open. Есливыражение SQLне подразумеваетвозвращениекурсора, тонужно вызыватьExecSQL. Например,если происходитвставка, удалениеили обновлениеданных (т.е. SQLзапросы INSERT, DELETE,UPDATE), то нужно вызыватьExecSQL. Тоже самоеможно сказатьпо-другому:Open вызываетсяпри запросетипа SELECT, а ExecSQL - вовсех остальныхслучаях.

Воттипичный SQL запрос,который используетсядля удалениязаписи из таблицы:


deletefrom Country where Name = ‘Argentina’;


Этот запросудалил бы любуюзапись из таблицыCOUNTRY, которая имеетзначение "Argentina"в поле Имя.

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


deletefrom Country where Name = :CountryName


Вэтом случаепеременная:CountryName может бытьизменена вовремя выполнения:


Query2.Prepare;

Query2.Params[0]:= ‘Argentina’;

Query2.ExecSQL;


Код сначалавызывает Prepare,чтобы сообщитьDelphi что он долженразобрать SQLзапрос и подготовитьсвойство Params.Следующим шагомприсваиваетсязначение свойствуParams и затем выполняетсяподготовленныйSQL запрос. Обратитевнимание, чтоон выполняетсячерез ExecSQL, а неOpen.

ПрограммаINSQUERY из примеровDelphi демонстрируетэту технику(проектC:\DELPHI\DEMOS\DB\INSQUERY.DPR)

Специальныесвойства TQuery

Есть несколькосвойств, принадлежащихTQuery, которые ещене упоминались:


propertyUniDirectional: Boolean;

propertyHandle: HDBICur;

propertyStmtHandle: HDBIStmt;

propertyDBHandle: HDBIDB;


СвойствоUniDirectional используетсядля того, чтобыоптимизироватьдоступ к таблице.Если Вы установитеUniDirectional в True, то Вы можетеперемещатьсяпо таблицеболее быстро,но Вы сможетедвигатьсятолько вперед.

Свойство StmtHandleсвязано сосвойствомHandle TDataSet. То есть, оновключеноисключительнодля того, чтоВы могли делатьвызовы Borland Database Engineнапрямую. Принормальныхобстоятельствах,нет никакойнеобходимостииспользоватьэто свойство,так как компонентыDelphi могут удовлетворитьпотребностямибольшинствапрограммистов.Однако, еслиВы знакомы сBorland Database Engine, и если Вызнаете чтосуществуютнекоторыевозможностине поддерживаемыев VCL, то Вы можетеиспользоватьTQuery.StmtHandle, или TQuery. Handle, чтобысделать вызовнапрямую вengine.

Следующийфрагмент кода показываетдва запросак BDE:


var

Name: array[0..100] of Char;

Records: Integer;

begin

dbiGetNetUserName(Name);

dbiGetRecordCount(Query1.Handle,Records);

end;

16

Урок6:Объект TQuery

Создание базданных в Delphi


Урок7: Редактор DataSet,Вычисляемыеполя

СодержаниеУрока 7:


Урок7: Редактор DataSet,Вычисляемыеполя1

СодержаниеУрока 7:1

Обзор2

РедакторDataSet2

ВычисляемыеПоля5

УправлениеTDBGrid во времявыполнения9

Обзор

В этой статьевы узнаете оРедактореDataSet и о способахуправлениякомпонентомTDBGrid во время выполненияпрограммы.Здесь же будутрассмотренывычисляемыеполя - весьмаценная особенностьРедактораDataSet.

Примеры, которыевы увидите вэтой статье,продемонстрируют основные способы,которыми пользуютсябольшинствопрограммистовдля показатаблиц БДпользователям.Для пониманиябольшей частиматериалатребуется общеезнание средыи языка Delphi.

РедакторDataSet

Редактор DataSetможет бытьвызван с помощьюобъектов TTable илиTQuery. Чтобы начатьработать с ним,положите объектTQuery на форму, установитепсевдонимDBDEMOS, введите SQL запрос"select * from customer" и активизируйтеего (установивсв-во Active в True).

Откройте комбобокс“Object Selector” вверхуИнспектораОбъектов - внастоящее времятам имеетсядва компонента:TForm и TQuery.

Нажмите правуюкнопку мышина объектеTQuery и в контекстномменю выберитепункт “Fields Editor”.Нажмите кнопкуAdd - появитьсядиалог Add Fields, какпоказано нарис.1


Рис.1: ДиалогAdd Fields РедактораDataSet.

По-умолчанию,все поля в диалогевыбраны. Нажмитена кнопку OK, чтобывыбрать всеполя, и закройтередактор. Сновазагляните в“Object Selector”, теперьздесь появилосьнесколько новыхобъектов, (см.рис.2)


Рис.2: Object Selectorпоказываетв списке всеобъекты созданныев РедактореDataSet. Вы можететакже найтиэтот списокв определениикласса TForm1.


Эти новые объектыбудут использоватьсядля визуальногопредставления таблицы CUSTOMERпользователю.

Вот полныйсписок объектов,которые толькочто созданы:


Query1CustNo:TFloatField;

Query1Company:TStringField;

Query1Addr1:TStringField;

Query1Addr2:TStringField;

Query1City:TStringField;

Query1State:TStringField;

Query1Zip:TStringField;

Query1Country:TStringField;

Query1Phone:TStringField;

Query1FAX:TStringField;

Query1TaxRate:TFloatField;

Query1Contact:TStringField;

Query1LastInvoiceDate:TDateTimeField;


Я вырезал ивставил этотсписок из определениякласса TForm1, котороеможно найтив окне Редактораисходноготекста. Происхождениеимен показанныхздесь, должнобыть достаточноочевидно. Часть"Query1" беретсяпо-умолчаниюот имени объектаTQuery, а вторая половинаот имени поляв таблице Customer.Если бы мы сейчаспереименовалиобъект Query1 в Customer,то получилибы такие имена:


CustomerCustNo

CustomerCompany


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

Любой объект,созданный вредактореDataSet являетсянаследникомкласса TField. Точныйтип потомказависит от типаданных в конкретномполе. Например,поле CustNo имееттип TFloatField, а полеQuery1City имеет типTStringField. Это два типаполей, которыеВы будете встречатьнаиболее часто.Другие типывключают типTDateTimeField, которыйпредставленполем Query1LastInvoiceDate, иTIntegerField, который невстречаетсяв этой таблице.

Чтобы понять,что можно делатьс потомкамиTField, откройтеBrowser, выключитепросмотр полейPrivate и Protected, и просмотритесвойства иметоды Public и Publishedсоответствующихклассов.

Наиболее важноесвойство называетсяValue. Вы можетеполучить доступк нему так:


procedureTForm1.Button1Click(Sender: TObject);

var

d:Double;

S:string;

begin

d:= Query1CustNo.Value;

S:= Query1Company.Value;

d:=d+1;

S:= 'Zoo';

Query1CustNo.Value:= d;

Query1Company.Value:= S;

end;


В коде, показанномздесь, сначалаприсваиваютсязначения переменнымd и S. Следующиедве строкиизменяют этизначения, апоследний двеприсваиваютновые значенияобъектам. Неимеет большогосмысла писатькод, подобныйэтому, в программе,но этот кодслужит лишьдля того, чтобыпродемонстрироватьсинтаксис,используемыйс потомкамиTField.

Свойство Valueвсегда соответствуеттипу поля, ккоторому оноотносится.Например уTStringFields - string, TCurrencyFields - double. Однако,если вы отображаетеполе типаTCurrencyField с помощьюкомпонент,“чувствительныхк данным”(data-aware: TDBEdit, TDBGrid etc.), то онобудет представленастрокой типа:"$5.00".

Это могло бызаставить васдумать, что уDelphi внезапноотключилсястрогий контрольтипов. ВедьTCurrencyField.Value объявленакак Double, и если Выпробуете присвоитьему строку, Выполучите ошибку“type mismatch” (несоответствиетипа). Вышеупомянутыйпример демонстрируетна самом делесвойства объектоввизуализацииданных, а неослаблениепроверки типов.(Однако, естьвозможностьполучить значениеполя уже преобразованноек другому типу.Для этого уTField и его потомковимеется наборметодов типаAsString или AsFloat. Конечно,преобразованиепроисходиттолько тогда,когда имеетсмысл.)

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


S:= Query1.Fields[0].FieldName;

S:= Query1CustNo.FieldName;


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


S:= Query1.Fields[0].Name;

S:= Query1CustNo.Name;


Для таблицыCUSTOMER, первый примервернет строку"CustNo", а любая изстрок второгопримера строку"Query1CustNo".

ВычисляемыеПоля

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


  • выполнениевычисленийпо двум илиболее полямв DataSet, и отображениерезультатавычисленийв третьем поле.

  • имитация соединениядвух таблицс возможностьюредактироватьрезультатсоединения.


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


Эта программасвязывает тритаблицы в отношенииодин ко многим.В частности,ORDERS и ITEMS связаныпо полю OrderNo, а ITEMS иPARTS связаны пополю PartNo. (В таблицеORDERS хранятся всезаказы; в таблицеITEMS - предметы,указанные взаказах; PARTS - справочникпредметов). Впрограмме можноперемещатьсяпо таблицеORDERS и видеть связанныйс текущим заказомсписок включенныхв него предметов.ПрограммаCALC_SUM достаточносложная, нохорошо иллюстрируетмощность вычисляемыхполей.


Последовательностьсоздания проектаCALC_SUM:

  • Создайте новыйпроект (File|New Project) иудалите изнего форму (вМенеджереПроекта View|ProjectManager)

  • Выберите экспертаформ БД из менюHelp.

  • На первом экране,выберите "Createa master/detail form" и "Create a form using TQueryObjects".

  • Нажмите кнопкуNext и выберитетаблицу ORDERS.DB изпсевдонимаБД DBDEMOS.

  • Нажмите Next ивыберите поляOrderNo, CustNo, SaleDate, ShipDate и ItemsTotal изтаблицы ORDERS.DB.

  • Нажмите Next ивыберите"Horizontal" из расстановкикомпонентовdbEdit на форме.

  • Нажмите Next ивыберите таблицуITEMS.DB.

  • В двух следующихэкранах выберитевсе поля изтаблицы и поместитеих в grid.

  • Нажмите Next ивыберите полеOrderNo из Master и Detail ListBoxes, иНажмите кнопкуAdd.

  • Нажмите Next исгенерируйтеформу.


Требуетсямного слов длятого, чтобыописать процесспоказанныйвыше, но, фактически,выполнениекоманд в Экспертеформ БД легкои интуитивно.

Выделите первыйиз двух объектовTQuery и установятсвойство Active вTrue. Для Query2 в свойствеSQL напишите текстзапроса:


select * from Items I, Parts P

where (I.OrderNo =:OrderNo)and

(I.PartNo=P.PartNo)


Активизируйтеобъект Query2 (Active установитев True) и вызовитередактор DataSet(Fields Editor) для него.Вызовите диалогAdd Fields и добавьтеполя OrderNo, PartNo, Qty и ListPrice.

Нажмите Define иведите словоTotal в поле FieldName. УстановитеField Type в CurrencyField. Проверьтечто Calculated CheckBox отмечен.Нажмите Ok и закройтередактор DataSet.

Простой процессописанный впредыдущемабзаце, показываеткак создатьвычисляемоеполе. Если посмотретьв DBGrid, то можновидеть, что тамтеперь естьеще одно пустоеполе. Для того,чтобы поместитьзначение в этополе, откройтев ИнспектореОбъектов страницусобытий дляобъекта Query2 исделайте двойнойщелчок наOnCalcFields. Заполнитесозданный методтак:


procedureTForm2.Query2CalcFields(DataSet: TDataSet);

begin

Query2NewTotalInvoice.Value:= 23.0;

end;


После запускапрограммы полеTotal будет содержитстроку $23.00.

Это показывает,насколькопросто создатьвычисляемоеполе, котороепоказываетправильносформатированныеданные. На самомделе это поледолжно показыватьнечто другое- произведениеполей Qty (количество)и ListPrice (цена). Дляэтого вышеприведенныйкод для событияOnCalcFields нужно изменитьследующимобразом:


procedureTForm1.Query2CalcFields(DataSet: TDataset);

begin

Query2Total.Value:=Query2Qty.Value*Query2ListPrice.Value;

end;


Если теперьзапуститьпрограмму, тополе Total будетсодержатьтребуемоезначение.

В обработчикесобытия OnCalcFields можновыполнять иболее сложныевычисления(это будет показанопозже), однакоследует помнить,что это вызываетсоответствующеезамедлениескорости работыпрограммы.

Теперь давайтедобавим вычисляемоеполе для первойтаблицы (Query1, ORDERS),которое будетотображатьсумму значенийиз поля Total второйтаблицы (Query2) дляданного заказа.Вызовите редакторDataSet для объектаQuery1 и добавьтевычисляемоеполе NewItemsTotal типаCurrencyField. В обработчикесобытия OnCalcFields дляQuery1 нужно подсчитатьсумму и присвоитьее полю NewItemsTotal:


procedureTForm1.Query1CalcFields(DataSet: TDataset);

var

R : Double;

begin

R:=0;

with Query2 dobegin

DisableControls;

Close;

Open;

repeat

R:=R+Query2Total.Value;

Next;

until EOF;

First;

EnableControls;

end;

Query1NewItemsTotal.Value:=R;

end;


Вданном примересумма подсчитываетсяс помощью простогоперебора записей,это не самыйоптимальныйвариант - можно,например, дляподсчета суммыиспользоватьдополнительныйобъект типаTQuery. Метод DisableControlsвызываетсядля того, чтобыотменить перерисовкуDBGrid при сканированиитаблицы. ЗапросQuery2 переоткрываетсядля уверенностив том, что еготекущий наборзаписей соответствуеттекущему заказу.

Поместите наформу еще одинэлемент DBEdit ипривяжите егок Query1, полю NewItemsTotal. Запуститепрограмму, еепримерный видпоказан нарис.3

Рис.3: ПрограммаCALC_SUM


Как видно изпрограммы,наличие поляItemsTotal в таблицеORDERS для данногопримера необязательнои его можнобыло бы удалить(однако, ононеобходимов других случаях).


УправлениеTDBGrid во время выполнения

Объект DBGrid можетбыть полностьюреконфигурированво время выполненияпрограммы. Выможете прятатьи показыватьколонки, изменятьпорядок показаколонок и ихширину.

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


dgEditing Установленпо-умолчаниюв true, позволяетпользователюредактироватьgrid. Вы можететакже установитьсвойство ReadOnlygrid в True или False.
dgAlwaysShowEditor Всегдапоказыватьредактор.
dgTitles Показыватьназвания колонок.
dgIndicator Показыватьнебольшиеиконки слева.
dgColumnResize Можетли пользовательменять размерколонки.
dgColLines Показыватьлинии междуколонками.
dgRowLines Показыватьлинии междустроками.
dgTabs Можетли пользовательиспользоватьtab и shift-tab для переключениямежду колонками.
dgRowSelect Выделятьвсю записьцеликом.
dgAlwaysShowSelection Всегдапоказыватьвыбранныезаписи.
dgConfirmDelete Подтверждатьудаление.
dgCancelOnExit Отменаизмененийпри выходеиз DBGrid.
dgMultiSelect Одновременноможет бытьвыделена большечем одна запись.

Как объявленов этой структуре:

DBGridOption= (dgEditing, dgAlwaysShowEditor, dgTitles, dgIndicator,dgColumnResize, dgColLines, dgRowLines, dgTabs,dgRowSelect,dgAlwaysShowSelection, dgConfirmDelete, dgCancelOnExit,dgMultiSelect);


Например Выможете установитьопции в Runtime написавтакой код:


DBGrid1.Options:= [dgTitles, dgIndicator];


Если Вы хотитевключать ивыключатьопции, это можносделать с помощьюлогическихопераций. Например,следующий кодбудет добавлятьdgTitles к текущемунабору параметров:


DBGrid1.Options:= DBGrid1.Options + [dgTitles];


Пусть естьпеременнаяShowTitles типа Boolean, тогдаследующий кодпозволяютвключать ивыключатьпараметр однойкнопкой:


procedureTForm1.Button3Click(Sender: TObject);

begin

ifShowTitles then

DBGrid1.Options :=DBGrid1.Options + [dgTitles]

else

DBGrid1.Options :=DBGrid1.Options - [dgTitles];

ShowTitles:= not ShowTitles;

end;


Если Вы хотитескрыть полев run-time, то можетеустановитьсвойство visible вfalse:


Query1.FieldByName(‘CustNo’).Visible:= False;

Query1CustNo.Visible:= False;


Обе строки кодавыполняютидентичнуюзадачу. Чтобыпоказать полеснова, установитевидимый в true:


Query1.FieldByName(‘CustNo’).Visible:= True;

Query1CustNo.Visible:= True;


Если Вы хотитеизменить положениеколонки в Runtime,можете простоизменить индекс,(первое полев записи имеетиндекс нуль):


Query1.FieldByName(‘CustNo’).Index:= 1;

Query1CustNo.Index:= 2;


По-умолчанию,поле CustNo в таблицеCustomer являетсяпервым. Код впервой строкеперемещаетэто поле вовторую позицию,а следующаястрока перемещаетего в третьюпозицию. Помните,что нумерацияполей начинаетсяс нуля, такприсвоениесвойству Index 1делает полевторым в записи.Первое полеимеет Index 0.

Когда Вы изменяетеиндекс поля,индексы другихполей в записиизменяютсяавтоматически.

Если Вы хотитеизменить ширинуколонки в Runtime,только изменитесвойство DisplayWidthсоответствующегоTField.


Query1.FieldByName(‘CustNo’).DisplayWidth:= 12;

Query1CustNo.DisplayWidth:= 12;


Величина 12 относитсяк числу символов,которые могутбыть показаныв видимом элементе.


ПрограммаDBGR_RT показываеткак работатьс DBGrid в Runtime. Программадостаточнопроста, кромедвух небольшихчастей, которыеописаны ниже.Первая частьпоказывает,как создатьcheck box в Runtime, а втораяпоказывает,как изменитьпорядок пунктовв listbox в Runtime.

При созданииформы (событиеOnCreate) ListBox заполняетсяименами полей,далее создаетсямассив объектовCheckBox, соответствующийполям в таблице.Сперва всеCheckBox’ы выбраныи все поля втаблице видимы.Программаузнает черезTTable1 имена полейи присваиваетих свойствуCaption соответствующегоCheckBox. Кроме того,обработчикусобытия OnClick всехCheckBox’ов присваиваетсяпроцедураChBClick, которая ивключает/выключаетполя в DBGrid.


procedureTForm1.FormCreate(Sender: TObject);

var

i : Word;

R: Array[0..49] of TCheckBox;

begin

{Fill ListBox}

ListBox1.Clear;

for i:=0 toTable1.FieldCount-1 do

ListBox1.Items.Add(Table1.Fields[i].FieldName);


{Make CheckBoxes}

for i:=0 toTable1.FieldCount-1 do begin

R[I] :=TCheckBox.Create(Self);

R[I].Parent :=ScrollBox1;

R[I].Caption :=Table1.Fields[i].FieldName;

R[I].Left := 10;

R[I].Top := I *CheckBox1.Height + 5;

R[I].Width :=200;

R[I].Checked :=True;

R[I].OnClick :=ChBClick;

end;

end;


Большая частькода в этомпримере выполняетотносительнопростые задачи,типа назначенияимен и положенийcheck boxes. Вот две ключевыхстроки:


R[I]:= TCheckBox.Create(Self);

R[I].Parent :=ScrollBox1;


Первая строкисоздает CheckBox сзаданным Owner(Владельцем).Вторая строкиназначаетParent (Родителя) дляCheckBox. Чтобы понятьразличия междуРодителем иВладельцем,посмотритесоответствующиесвойства вonline-help.


Программасодержит ListBox,который показываеттекущий порядокполей в DataSet. Дляизмененияпорядка полейв DataSet (а, следовательно,в DBGrid) используютсядве кнопки. Принажатии на однуиз кнопок, выбранноев ListBox’е полеперемещаетсяна одну позициювверх или вниз.Синхронно сэтим меняетсяи порядок полейв DBGrid. Код, показанныйниже, изменяетIndex поля для Table1,изменяя, такимобразом, позициюполя в DBGrid. Этиизменениякасаются тольковизуальногопредставленияDataSet. Физическиданные на дискене изменяются.


procedureTForm1.downButtonClick(Sender: TObject);

var

i: Integer;

begin

with ListBox1 do

if(ItemIndex-1) then begin

i := ItemIndex;

{move ListBoxitem}

Items.Move(i,i+1);

ItemIndex :=i+1;

{move Field}

Table1.Fields[i].Index:=i+1;

end;

end;

Последняястрока в примерекак раз та, котораяфактическиизменяет индексколонки, которуюпользовательхочет переместить.Две строки коданепосредственноперед ней перемещаюттекущую строкув ListBox на новуюпозицию.

Внешнийвид программыDBGR_RT показан нарис.4

Рис.4: ПрограммаDBGR_RT

Урок 7 :РедакторDataSet, вычисляемыеполя

13



Созданиебаз данных вDelphi


Урок 8:Управлениесоединениемс базой данных(класс TDataBase, объектSession)


Содержаниеурока 8:


Обзор2

КлассTDataBase2

ОбъектSession7

Указаниесетевого протоколапри соединениис БД7


Обзор

В даннойстатье рассказываетсяоб управлениисоединениемс базой данныхпри помощикомпонентыTDataBase и объектаTSession, который создаетсяв программеавтоматически.Описываютсяпроцедурысоздания локальногопсевдонимабазы данныхи доступа ктаблицам Paradox попаролю.


КлассTDataBase

Объект типаTDataBase не являетсяобязательнымпри работе сбазами данных,однако онпредоставляетряд дополнительныхвозможностейпо управлениюсоединениемс базой данных.TDataBase служит для:


  • Созданияпостоянногосоединенияс базой данных

  • Определениясобственногодиалога присоединениис базой данных(опрос пароля)

  • Созданиялокальногопсевдонимабазы данных

  • Измененияпараметровпри соединении

  • Управлениятранзакциями


TDataBaseявляется невидимымво время выполненияобъектом. Оннаходится настранице “DataAccess” ПалитрыКомпонент. Длявключения впроект TDataBase нужно“положить”его на главноеокно вашейпрограммы.


Созданиепостоянногосоединенияс базой данных

Есливы работаетес базой данных,то перед началомработы выполняетсяпроцедурасоединенияс этой базой.В процедурусоединения,кроме прочего,входит опросимени и пароляпользователя(кроме случаяработы с локальнымитаблицамиParadox и dBase через IDAPI).Если в программене используетсяTDataBase, то процедурасоединениявыполняетсяпри открытиипервой таблицыиз базы данных.Соединениес базой данныхобрывается,когда в программезакрываетсяпоследняятаблицы из этойбазы (это происходитв том случае,если свойствоKeepConnectionsобъекта Sessionустановленов False, но об этомчуть позже).Теперь, еслиснова открытьтаблицу, топроцедураустановкисоединенияповторитсяи это можетбыть достаточнонеудобно дляпользователя.Чтобы соединениене обрывалосьдаже в том случае,когда нет открытыхтаблиц даннойбазы, можноиспользоватькомпонент типаTDataBase. В свойствеAliasName укажите псевдонимбазы данных,с которой работаетпрограмма; всвойстве DatabaseName- любое имя(псевдоним БД),на котороебудут ссылатьсятаблицы вместостарого псевдонимабазы. СвойствоConnected установитев True - процедурасоединенияс базой будетвыполнятьсяпри запускепрограммы. И,наконец, свойствоKeepConnection нужно установитьв True (см. рис.1).


Рис.A:Свойства TDataBase вИнспектореобъектов

Внашем примере,после заданиясвойств DataBase1 нужноу всех таблиц,работающихс IBLOCAL в свойствеDatabaseName поставитьLoc_IBLOCAL.


Определениесобственногодиалога присоединениис базой данных

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


Рис.B:Диалог авторизациипользователя


При желанииможно изменитьвнешний виддиалога иливообще егоотменить. Дляэтого используютсясвойства исобытия классаTDataBase - LoginPrompt, Paramsи OnLogin.

Чтобыотключить опросимени и пароляустановитесвойство LoginPromptв False. При этом всвойстве Paramsтребуется вявном виде (вовремя дизайналибо во времявыполнения)указать имяи пароль пользователя.Например, впрограмме можнонаписать (домомента соединенияс базой, напримерв событии дляForm1 OnCreate):


DataBase1.LoginPrompt:=False;

DataBase1.Params.Clear;

DataBase1.Params.Add(‘USERNAME=SYSDBA’);

DataBase1.Params.Add(‘PASSWORD=masterkey’);

DataBase1.Connected:=True;


Чтобыиспользоватьсвой собственныйдиалог, в которомможно опрашиватьне только имяи пароль пользователя,но и, например,сетевой протокол- создайте обработчиксобытия OnLogin дляDataBase1:


procedureTForm1.Database1Login(Database: TDatabase;

LoginParams:TStrings);

begin

Form2.ShowModal;

ifForm2.ModalResult = mrOK then

withLoginParams do begin

Values['USERNAME'] := User_Name;

Values['PASSWORD']:= User_Pass;

end;

end;


ЗдесьForm2 -новое окно-диалогдля ввода имении пароля, User_Nameи User_Pass- строки, кудасохраняютсявведенные имяи пароль.


Созданиелокальногопсевдонимабазы данных


Обычно,псевдоним базыданных(Alias) определяетсяв утилитеконфигурацииBDE и информацияо нем сохраняетсяв файле конфигурацииIDAPI.CFG. Однако, впрограмме можноиспользоватьне только ранееопределенныйв утилитеконфигурацииBDE псевдонимбазы данных,но и так называемыйлокальный (т.е.видимый тольковнутри даннойпрограммы)псевдоним. Этоиногда бываетнужно, например,для того, чтобыобезопаситьпрограмму вслучае удаленияиспользуемогопсевдонимаиз файла конфигурацииBDE.

Длятого, чтобысоздать локальныйпсевдоним БД,положите наглавное окнопроекта компонентDataBase1. Дальнейшиедействия можновыполнить спомощью ИнспектораОбъектов, ноудобнее этосделать черезредактор компонент.Щелкните дваждымышкой на DataBase1 -появится диалог,показанныйна рис.3



Рис.C:Редактор компонентыкласса TDataBase


Вэтом диалогетребуетсяуказать имябазы данных- это будет еелокальныйпсевдоним, накоторый ссылаютсятаблицы (свойствоDatabaseName); тип драйвера(в нашем примереэто INTRBASE); а такжепараметры,используемыепри соединениис базой данных.Получить списокпараметровв поле “Parameter Overrides”можно по нажатиюкнопки “Defaults”.Набор параметровзависит от типаБД, с которойвы работаете.Этим параметрамнужно присвоитьтребуемыезначения - указатьпуть к серверу,имя пользователяи т.д. После выходаиз редакторакомпонент имя,указанное вполе “Name” появитсяв списке именбаз данных длякомпонент типаTDataSet (TTable, TQuery etc.).


Изменениепараметровпри соединении


Иногдатребуетсяизменить определенныев утилитеконфигурацииBDE параметры,используемыепри установлениисоединенияс БД. Это можносделать вовремя дизайнас помощью диалога,показанногона рис.3, в поле“Parameter Overrides”. Либо вовремя выполненияпрограммы (допопытки соединения)прямым присвоениемсвойству Paramsобъекта DataBase1:


DataBase1.Params.Add(‘LANGDRIVER=ancyrr’);


Управлениетранзакциями


TDataBase позволяетначать в БДтранзакцию(метод StartTransaction), закончить(Commit) или откатитьее (RollBack). Кроме того, можно изменятьуровень изоляциитранзакций(свойствоTransIsoltion).


TransIsolationOracleSybase andInformixInterBase

MicrosoftSQL


DirtyreadRead committedRead committedDirty ReadRead committed


Readcommitted(Default)Read committedRead committedRead committedReadcommitted


RepeatablereadRepeatable readRead committedRepeatable ReadRepeatable Read


“DirtyRead” - внутри вашейтекущей транзакциивидны все изменения, сделанныедругими транзакциями,даже если ониеще не завершилисьпо Commit. “Read Committed” - виднытолько “закоммитченные”изменения,внесенные вбазу. “Repeatable Read” - внутритранзакциивидны те данные,что были в базена момент началатранзакции,даже если тамна самом делеуже имеютсяизменения.


ОбъектSession

ОбъектSession, имеющий типTSession создаетсяавтоматическив программе,работающейс базами данных(в этом случаеDelphi подключаетв программумодуль DB). Вамне нужно заботитьсяо создании иуничтоженииданного объекта,но его методыи свойствамогут бытьполезны в некоторыхслучаях. В этомкомпонентесодержитсяинформацияобо всех базахданных, с которымиработает программа.Ее можно найтив свойствеDataBases.Со свойствомKeepConnectionsданного объектамы уже знакомы.Это свойствоопределяет,нужно ли сохранятьсоединениес базой, еслив программенет ни однойоткрытой таблицыиз этой базы.NetDir -директория,в которой лежитобщий сетевойфайл PDOXUSRS.NET, необходимыйBDE. PrivateDir- директориядля хранениявременныхфайлов.

С помощьюметодов объектаSession можно получитьинформациюо настройкахBDE, например, списоквсех псевдонимов,драйверов базданных илисписок всехтаблиц в базе.

Ещеодно важноеназначениеобъекта Session - доступс его помощьюк таблицамParadox, защищеннымпаролем. Прежде,чем открытьтакую таблицу,требуетсявыполнить методAddPassword :


Session.AddPassword(‘my_pass’);


Удалить парольможно с помощьюметода RemovePassword илиRemoveAllPasswords.


Указаниесетевого протоколапри соединениис БД

В случае сInterBase можно в явномвиде указать,какой сетевойпротокол используетсяпри соединениис базой данных.Эта установкавыполняетсялибо в утилитеконфигурацииBDE, либо в программе- нужно изменитьпараметр “SERVERNAME”, которыйсодержит полныйпуть к файлус базой данных.

Итак:

ПротоколПараметрSERVER NAME

TCP/IPIB_SERVER:PATH\DATABASE.GDB(nt:c:\ib\base.gdb )( unix:/ib/base.gdb )

IPX/SPXIB_SERVER:PATH\DATABASE.GDB(nw@sys:ib\base.gdb )

NetBEUI\IB_SERVER\PATH\DATABASE.GDB(\nt\c:\ib\base.gdb )


8

Урок8 : Управлениесоединениемс базой данных



Создание базданных в Delphi


Chapter 23

Урок9: Управлениетранзакциями


Содержаниеурока 9:


Обзор2

SQL-выражениядля управлениятранзакциями2

Запусктранзакции2

Завершениетранзакции4

Управлениетранзакциямив Delphi4

Обзор

Всеоперации, выполняемыес данными наSQL сервере, происходятв контекстетранзакций.Транзакция- это групповаяоперация, т.е.набор действийс базой данных;самым существеннымдля этих действийявляется правилолибо все,либо ни чего.Если во времявыполненияданного наборадействий, накаком-то этапеневозможнопроизвестиочередноедействие, тонужно выполнитьвозврат базыданных к начальномусостоянию(произвестиоткат транзакции).Таким образом(при правильномпланированиитранзакций),обеспечиваетсяцелостностьбазы данных.В данном урокеобъясняется,как начинать,управлять изавершатьтранзакциис помощью SQLвыражений. Атак же рассматриваетсявопрос обиспользованиитранзакцийв приложениях,созданных вDelphi. Вся приведеннаяинформациякасается InterBase.

SQL-выражениядля управлениятранзакциями

Дляуправлениятранзакциямиимеется тривыражения:


SETTRANSACTION -Начинает транзакциюи определяетее поведение.


COMMIT-Сохраняетизменения,внесенныетранзакцией,в базе данныхи завершаеттранзакцию.


ROLLBACK- Отменяетизменения,внесенныетранзакцией,и завершаеттранзакцию.


Запусктранзакции

Выполнятьтранзакцииможно, например,из Windows Interactive SQL, из программы,из сохраненнойпроцедуры илитриггера. Вобщем виде,синтаксискоманды SQL длязапуска транзакции:


SETTRANSACTION [Access mode] [Lock Resolution]

[IsolationLevel] [Table Reservation]


Значения,принимаемыепо-умолчанию:

выражение

SETTRANSACTION

равносильновыражению

SETTRANSACTION READ WRITE WAIT ISOLATION LEVEL SNAPSHOT


AccessMode - определяеттип доступак данным. Можетпринимать двазначения:

  • READ ONLY - указывает,что транзакцияможет толькочитать данныеи не можетмодифицироватьих.

  • READ WRITE - указывает,что транзакцияможет читатьи модифицироватьданные. Этозначение принимаетсяпо умолчанию.


Пример:


SETTRANSACTION READ WRITE


IsolationLevel - определяетпорядок взаимодействияданной транзакциис другими вданной базе.Может приниматьзначения:

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

  • SNAPSHOT TABLE STABILITY - предоставляеттранзакцииисключительныйдоступ к таблицам,которые онаиспользует.Другие транзакциисмогут толькочитать данныеиз них.

  • READ COMMITTED - позволяеттранзакциивидеть текущеесостояниебазы.


Конфликты,связанные сблокировкойзаписей происходятв двух случаях:

  • Транзакцияпытаетсямодифицироватьзапись, котораябыла измененаили удаленауже после еестарта. Транзакциятипа READ COMMITTED можетвносить измененияв записи, модифицированныедругими транзакциямипосле их завершения.

  • Транзакцияпытаетсямодифицироватьтаблицу, котораязаблокированадругой транзакциейтипа SNAPSHOT TABLE STABILITY.


LockResolution - определяетход событийпри обнаруженииконфликтаблокировки.Может приниматьдва значения:

  • WAIT - значение поумолчанию.Ожидает разблокировкитребуемойзаписи. Послеэтого пытаетсяпродолжитьработу.

  • NO WAIT - немедленновозвращаетошибку блокировкизаписи.


TableReservation - позволяеттранзакцииполучитьгарантированныйдоступ необходимогоуровня к указаннымтаблицам. Существуетчетыре уровнядоступа:

  • PROTECTED READ - запрещаетобновлениетаблицы другимитранзакциями,но позволяетим выбиратьданные из таблицы.

  • PROTECTED WRITE - запрещаетобновлениетаблицы другимитранзакциями,читать данныеиз таблицымогут толькотранзакциитипа SNAPSHOT или READCOMMITTED.

  • SHARED READ - самый либеральныйуровень. Читатьмогут все,модифицировать- транзакцииREAD WRITE.

  • SHARED WRITE - транзакцииSNAPSHOT или READ COMMITTED READ WRITE могутмодифицироватьтаблицу, остальные- только выбиратьданные.


Завершениетранзакции

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

  • COMMIT- сохраняетвнесенныетранзакциейизменения вбазу данных.Это означает,что транзакциязавершенауспешно.

  • ROLLBACK - откат транзакции.Транзакциязавершаетсяи никаких измененийв базу данныхне вносится.Данная операциявыполняетсяпри возникновенииошибки привыполненииоперации (например,при невозможностиобновить запись).


Управлениетранзакциямив Delphi

Прежде всего,транзакциив Delphi бывают явныеи неявные.

Явнаятранзакция- это транзакция,начатая и завершеннаяс помощью методовобъекта DataBase:StartTransaction, Commit, RollBack.Посленачала явнойтранзакции,все изменения,вносимые вданные относятсяк этой транзакции.

Другого способаначать явнуютранзакцию,нежели с использованиемDataBase, нет. (Точнееговоря, такаявозможностьесть, но этопотребуетобращения кфункциям APIInterBase. Однако, этоуже достаточнонизкоуровневоепрограммирование.) Следовательно,в рамках одногосоединениянельзя начатьдве транзакции.

Неявнаятранзакциястартует примодификацииданных, еслив данный моментнет явной транзакции.Неявная транзакциявозникает,например, привыполненииметода Postдля объектовTable иQuery. Тоесть, если Выотредактировализапись, в DBGrid ипереходитена другую запись,то это влечетза собой выполнениеPost, что,в свою очередь,приводит кначалу неявнойтранзакции,обновлениюданных внутритранзакциии ее завершению.Важно отметить,что неявнаятранзакция,начатая с помощьюметодов Post,Delete, Insert, Append и т.д.заканчиваетсяавтоматически.

Длямодификацииданных можетиспользоватьсяи PassThrough SQL- SQL-выражение,выполняемоес помощью методаExecSQL классаTQuery. Выполнениемодификациичерез PassThrough SQL такжеприводит кстарту неявнойтранзакции.Дальнейшееповедениетранзакции,начатой такимпутем, определяетсязначениемпараметраSQLPASSTHRU MODE для псевдонимабазы данных(или тот-же параметрв св-ве ParamsобъектаDataBase). Этот параметрможет приниматьтри значения:

  • SHAREDAUTOCOMMIT - словоSHARED указываетна то, что обавида транзакций(черезPassthrough SQL и через методыTTable и TQuery) разделяютодно и то жесоединениек базе данных.Слово AUTOCOMMIT указываетна то, что неявнаятранзакция,начатая черезPassthrough SQL, завершаетсяпосле выполнениядействия помодификацииданных (автоматическивыполняетсяCOMMIT).

  • SHAREDNOAUTOCOMMIT - отличаетсяот предыдущеготем, что неявнаятранзакция,начатая черезPassthrough SQL, не завершаетсяпосле выполнения,ее нужно явнозавершить,выполнивSQL-выражение“COMMIT”.

  • NOTSHARED - транзакцииразных типовработают черезразные соединенияс базой. Данноезначение параметраподразумеваеттакже NOAUTOCOMMIT.То есть всенеявныеPassthroughSQL-транзакциинужно завершатьявно - выполняяSQL-выражение“COMMIT”для Passtrough SQL.


Рассмотримвозможныесценарии поведениятранзакцийпри разныхзначенияхпараметра.

Впервом случае,если нет в данныймомент начатойтранзакции,то попыткамодификацияданных методамиTTable или TQuery, как ивыполнениечерез Passtrough SQL какой-либооперации приведетк старту неявнойтранзакции.После выполнения,такая транзакциябудет автоматическизавершена (еслине возниклоошибки по ходутранзакции).Если уже имеетсяначатая явно(метод StartTransactionобъекта DataBase)транзакция,то изменениябудут проходитьв ее контексте.Все транзакциииспользуютодно и то-жесоединение.

Вовтором случаевсе происходит,как в первом. Отличие в том,что неявнаяPassthroughSQL-транзакцияне завершается,пока не будетвыполненакоманда “COMMIT”.

Втретьем случае,при выполнениикоманды Passthrough SQL,будет установленоеще одно соединение,начата неявнаятранзакцияи выполненыдействия помодификацииданных. Транзакцияне будет завершена,пока не будетвыполненакоманда “COMMIT”.Наличие транзакции,начатой явнос помощью DataBaseникак не отразитсяна ходе выполненияPassthroughSQL-транзакции.Пока PassthroughSQL-транзакцияне завершится,изменения,внесенные ей,не будут видныв объектахTable и Query, работающихчерез другоесоединение.PassthroughSQL-транзакцииможно рассматриватьв некоторомсмысле, кактранзакциииз другогоприложения.


Взаимодействиетранзакцийданной программыс транзакциямииз других приложенийопределяетсясвойствомTransIsolationобъекта DataBase. ДляInterBase имеет смыслдва значения:tiReadCommittedи tiRepeatableRead. Выполнениеметода StartTransactionв этих двухслучаях равносильновыполнениюSQL-выражений,соответственно:


SETTRANSACTION READ WRITE WAIT ISOLATION LEVEL READ COMMITTED

и

SETTRANSACTION READ WRITE WAIT ISOLATION LEVEL SNAPSHOT

6

Урок 9 :Управлениетранзакциями



Создание базданных в Delphi


Урок 10: Основыязыка SQL

Содержаниеурока 10:


Обзор2

Составязыка SQL2

Реляционныеоперации. Командыязыка манипулированияданными4

КомандаSELECT10

Простейшиеконструкциикоманды SELECT10

Списокполей10

Все поля11

Все поляв произвольномпорядке11

Блобы11

Вычисления12

Литералы12

Конкатенация13

ИспользованиеквалификатораAS13

Работас датами14

Агрегатныефункции15

ПредложениеFROM команды SELECT16

Ограниченияна число выводимыхстрок16

Операциисравнения16

BETWEEN18

IN20

LIKE21

CONTAINING22

IS NULL22

Логическиеоператоры23

Преобразованиетипов (CAST)25

Изменениепорядка выводимыхстрок (ORDER BY)25

Упорядочиваниес использованиемимен столбцов26

Упорядочиваниес использованиемномеров столбцов28

Устранениедублирования(модификаторDISTINCT)29

Соединение(JOIN)30

Внутренниесоединения31

Самосоединения34

Внешниесоединения35

Обзор

SQL(обычно произносимыйкак "СИКВЭЛ"или “ЭСКЮЭЛЬ”)символизируетсобой СтруктурированныйЯзык Запросов.Это - язык, которыйдает Вам возможностьсоздавать иработать вреляционныхбазах данных,являющихсянаборами связаннойинформации,сохраняемойв таблицах.


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

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

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


СтандартSQL определяетсяANSI (АмериканскимНациональнымИнститутомСтандартов)и в данное времятакже принимаетсяISO (МеждународнойОрганизациейпо Стандартизации).Однако, большинствокоммерческихпрограмм базданных расширяютSQL без уведомленияANSI, добавляяразличныеособенностив этот язык,которые, какони считают,будут весьмаполезны. Иногдаони нескольконарушают стандартязыка, хотяхорошие идеиимеют тенденциюразвиватьсяи вскоре становитьсястандартами"рынка" самипо себе в силуполезностисвоих качеств.

Наданном урокемы будем, в основном,следоватьстандарту ANSI,но одновременноиногда будетпоказыватьи некоторыенаиболее общиеотклоненияот его стандарта.

Точноеописание особенностейязыка приводитсяв документациина СУБД, которуюВы используете.SQL системы InterBase 4.0соответствуетстандартуANSI-92 и частичностандартуANSI-III.


Составязыка SQL

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

Поэтому,в язык SQL в качествесоставныхчастей входят:

  • язык манипулированияданными (Data ManipulationLanguage, DML)

  • язык определенияданных (Data DefinitionLanguage, DDL)

  • язык управленияданными (Data ControlLanguage, DCL).


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


Языкманипулированияданными используется,как это следуетиз его названия,для манипулированияданными в таблицахбаз данных. Онсостоит из 4основных команд:


SELECT(выбрать)

INSERT(вставить)

UPDATE(обновить)

DELETE(удалить).


Языкопределенияданных используетсядля созданияи измененияструктуры базыданных и еесоставныхчастей - таблиц,индексов,представлений(виртуальныхтаблиц), а такжетриггеров исохраненныхпроцедур. Основнымиего командамиявляются:


CREATE DATABASE(создать базуданных)

CREATE TABLE(создать таблицу)

CREATE VIEW(создать виртуальнуютаблицу)

CREATE INDEX(создать индекс)

CREATETRIGGER(создатьтриггер)

CREATEPROCEDURE(создатьсохраненнуюпроцедуру)

ALTER DATABASE(модифицироватьбазу данных)

ALTER TABLE(модифицироватьтаблицу)

ALTER VIEW(модифицироватьвиртуальнуютаблицу)

ALTER INDEX(модифицироватьиндекс)

ALTERTRIGGER(модифицироватьтриггер)

ALTERPROCEDURE(модифицироватьсохраненнуюпроцедуру)

DROP DATABASE(удалить базуданных)

DROP TABLE(удалить таблицу)

DROP VIEW(удалить виртуальнуютаблицу)

DROP INDEX(удалить индекс)

DROPTRIGGER(удалитьтриггер)

DROPPROCEDURE(удалитьсохраненнуюпроцедуру).


Языкуправленияданными используетсядля управленияправами доступак данным ивыполнениемпроцедур вмногопользовательскойсреде. Болееточно его можноназвать “языкуправлениядоступом”. Онсостоит из двухосновных команд:


GRANT (датьправа)

REVOKE(забрать права).


Сточки зренияприкладногоинтерфейсасуществуютдве разновидностикоманд SQL:

  • интерактивныйSQL

  • встроенныйSQL.

ИнтерактивныйSQL используетсяв специальныхутилитах (типаWISQL или DBD), позволяющихв интерактивномрежиме вводитьзапросы сиспользованиемкоманд SQL, посылатьих для выполненияна сервер иполучать результатыв предназначенномдля этого окне.ВстроенныйSQL используетсяв прикладныхпрограммах,позволяя импосылать запросык серверу иобрабатыватьполученныерезультаты,в том числекомбинируяset-ориентированныйи record-ориентированныйподходы.

Мыне будем приводитьточный синтаксискоманд SQL, вместоэтого мы рассмотримих на многочисленныхпримерах, чтонамного болееважно для пониманияSQL, чем точныйсинтаксис,который можнопосмотретьв документациина Вашу СУБД.


Итак,начнем с рассмотрениякоманд языкаманипулированияданными.

Реляционныеоперации. Командыязыка манипулированияданными

Наиболееважной командойязыка манипулированияданными являетсякоманда SELECT. Закажущейсяпростотой еесинтаксисаскрываетсяогромное богатствовозможностей.Нам важно научитьсяиспользоватьэто богатство!

Наданном урокепредполагается,если не оговоренопротивное, чтовсе командыязыка SQL вводятсяинтерактивнымспособом. Вкачествеинформационнойосновы дляпримеров мыбудем использоватьбазу данных“Служащиепредприятия”(employee.gdb), входящуюв поставкуDelphi и находящуюся(по умолчанию)в поддиректории\IBLOCAL\EXAMPLES.

Рис. 1:Структурабазы данныхEMPLOYEE


Нарис.1 приведенасхема базыданных EMPLOYEE дляLocal InterBase, нарисованнаяс помощьюCASE-средстваS Designor (см. доп. урок).На схеме показанытаблицы базыданных и взаимосвязи,а также обозначеныпервичные ключии их связи свнешними ключами.Многие из примеров,особенно вконце урока,являются весьмасложными. Однако,не следует наэтом основанииделать вывод,что так сложенсам язык SQL. Дело,скорее, в том,что обычные(стандартные)операции настолькопросты в SQL, чтопримеры такихопераций оказываютсядовольнонеинтереснымии не иллюстрируютполной мощностиэтого языка.Но в целяхсистемностимы пройдем повсем возможностямSQL: от самых простых- до чрезвычайносложных.

Начнемс базовых операцийреляционныхбаз данных.Таковыми являются:

  • выборка(Restriction)

  • проекция(Projection)

  • соединение(Join)

  • объединение(Union).


Операциявыборки позволяетполучить всестроки (записи)либо частьстрок однойтаблицы.


SELECT *FROM countryПолучитьвсе строки
таблицы Country


COUNTRY CURRENCY

=========================

USA Dollar

England Pound

Canada CdnDlr

Switzerland SFranc

Japan Yen

Italy Lira

France FFranc

Germany D-Mark

Australia ADollar

HongKong HKDollar

Netherlands Guilder

Belgium BFranc

Austria Schilling

Fiji FDollar


Вэтом примереи далее - длябольшей наглядности- все зарезервированныеслова языкаSQL будем писатьбольшими буквами.Красным цветомбудем записыватьпредложенияSQL, а светло-синим- результатывыполнениязапросов.


SELECT *FROM country

WHEREcurrency = “Dollar”Получитьподмножествострок таблицыCountry, удовлетворяющееусловию Currency =“Dollar”


Результатпоследнейоперации выглядитследующимобразом:


COUNTRY CURRENCY

=========================

USA Dollar


Операцияпроекциипозволяетвыделить подмножествостолбцов таблицы.Например:


SELECTcurrency FROM countryПолучитьсписок
денежныхединиц


CURRENCY

==========

Dollar

Pound

CdnDlr

SFranc

Yen

Lira

FFranc

D-Mark

ADollar

HKDollar

Guilder

BFranc

Schilling

FDollar


Напрактике оченьчасто требуетсяполучить некоеподмножествостолбцов истрок таблицы,т.е. выполнитькомбинациюRestriction и Projection. Дляэтого достаточноперечислитьстолбцы таблицыи наложитьограниченияна строки.


SELECTcurrency FROM country

WHEREcountry = “Japan”Найтиденежную
единицуЯпонии


CURRENCY

==========

Yen


SELECTfirst_name, last_name

FROMemployee

WHEREfirst_name = "Roger"Получитьфамилии
работников,
которых зовут“Roger”

FIRST_NAME LAST_NAME

===================================

Roger De Souza

Roger Reeves


Этипримеры иллюстрируютобщую формукоманды SELECT вязыке SQL (для однойтаблицы):


SELECT(выбрать)специфицированныеполя

FROM (из)специфицированнойтаблицы

WHERE(где)некотороеспецифицированноеусловие являетсяистинным


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


SELECTfirst_name, last_name, proj_name

FROMemployee, project

WHEREemp_no = team_leaderПолучитьсписок
руководителейпроектов


FIRST_NAME LAST_NAME PROJ_NAME

=============================== ====================

Ashok Ramanathan Video Database

Pete Fisher DigiPizza

Chris Papadopoulos AutoMap

Bruce Young MapBrowser port

MaryS. MacDonald Marketing project 3


Операцияобъединенияпозволяетобъединятьрезультатыотдельныхзапросов понесколькимтаблицам вединую результирующуютаблицу. Такимобразом, предложениеUNION объединяетвывод двух илиболее SQL-запросовв единый наборстрок и столбцов.


SELECTfirst_name, last_name, job_country

FROMemployee

WHEREjob_country = "France"

UNION

SELECTcontact_first, contact_last, country

FROMcustomer

WHEREcountry = "France"Получитьсписок
работникови заказчиков,
проживающихво Франции


FIRST_NAME LAST_NAME JOB_COUNTRY

================================ ===============

Jacques Glon France

Michelle Roche France


Длясправки, приведемобщую формукоманды SELECT, учитывающуювозможностьсоединениянесколькихтаблиц и объединениярезультатов:


SELECT[DISTINCT]список_выбираемых_элементов(полей)

FROMсписок_таблиц(или представлений)

[WHEREпредикат]

[GROUP BYполе(или поля) [HAVINGпредикат]]

[UNIONдругое_выражение_Select]

[ORDER BYполе(или поля) илиномер (номера)];

Рис. 2: Общийформат командыSELECT

Отметим, чтопод предикатомпонимаетсянекотороеспецифицированноеусловие (отбора),значение которогоимеет булевскийтип. Квадратныескобки означаютнеобязательностьиспользованиядополнительныхконструкцийкоманды. Точкас запятой являетсястандартнымтерминаторомкоманды. Отметим,что в WISQL и в компонентеTQueryставить конечныйтерминаторне обязательно.При этом там,где допустимодин пробелмежду элементами,разрешеноставить любоеколичествопробелов ипустых строк- выполняя желаемоеформатированиедля большейнаглядности.


Гибкостьи мощь языкаSQL состоит в том,что он позволяетобъединитьвсе операцииреляционнойалгебры в однойконструкции,“вытаскивая”таким образомлюбую требуемуюинформацию,что очень частои происходитна практике.

КомандаSELECT

Простейшиеконструкциикоманды SELECT

Итак,начнем с рассмотренияпростейшихконструкцийязыка SQL. Послетакого рассмотрениямы научимся:

  • назначатьполя, которыедолжны бытьвыбраны

  • назначать квыборке “всеполя”

  • управлять“вертикальным”и “горизонтальным”порядком выбираемыхполей

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

  • производитьвычисленияв списке выбираемыхэлементов

  • использоватьлитералы всписке выбираемыхэлементов

  • ограничиватьчисло возвращаемыхстрок

  • формироватьсложные условияпоиска, используяреляционныеи логическиеоператоры

  • устранятьодинаковыестроки изрезультата.


Списоквыбираемыхэлементов можетсодержатьследующее:

  • имена полей

  • *

  • вычисления

  • литералы

  • функции

  • агрегирующиеконструкции

Список полей

SELECTfirst_name, last_name, phone_no

FROMphone_listполучитьсписок
имен,фамилий и служебныхтелефонов
всехработниковпредприятия


FIRST_NAME LAST_NAME PHONE_NO

================================= ====================

Terri Lee (408) 555-1234

OliverH. Bender (408) 555-1234

MaryS. MacDonald (415) 555-1234

Michael Yanowski (415) 555-1234

Robert Nelson (408) 555-1234

Kelly Brown (408) 555-1234

Stewart Hall (408) 555-1234

...

Отметим,что PHONE_LIST - это виртуальнаятаблица (представление),созданная вInterBase и основаннаяна информациииз двух таблиц- EMPLOYEE и DEPARTMENT. Она непоказана нарис.1, однако,как мы уже указывалив общей структурекоманды SELECT, к нейможно обращатьсятак же, как и к“настоящей”таблице.

Все поля

SELECT *

FROMphone_listполучитьсписок служебныхтелефонов
всехработниковпредприятия
совсей необходимойинформацией


EMP_NOFIRST_NAME LAST_NAME PHONE_EXT LOCATION PHONE_NO

================ ========= ========= ============= ==============

12Terri Lee 256 Monterey (408) 555-1234

105Oliver H. Bender 255 Monterey (408) 555-1234

85Mary S. MacDonald 477 San Francisco (415) 555-1234

127Michael Yanowski 492 San Francisco (415) 555-1234

2Robert Nelson 250 Monterey (408) 555-1234

109Kelly Brown 202 Monterey (408) 555-1234

14Stewart Hall 227 Monterey (408) 555-1234

...

Все поляв произвольномпорядке

SELECTfirst_name, last_name, phone_no,
location, phone_ext,emp_no

FROMphone_listполучитьсписок служебныхтелефонов
всехработниковпредприятия
совсей необходимойинформацией,
расположивих в требуемомпорядке


FIRST_NAMELAST_NAME PHONE_NO LOCATION PHONE_EXT EMP_NO

=================== ============== ============= ========= ======

Terri Lee (408) 555-1234 Monterey 256 12

OliverH. Bender (408) 555-1234 Monterey 255 105

MaryS. MacDonald (415) 555-1234 San Francisco 477 85

Michael Yanowski (415) 555-1234 San Francisco 492 127

Robert Nelson (408) 555-1234 Monterey 250 2

Kelly Brown (408) 555-1234 Monterey 202 109

Stewart Hall (408) 555-1234 Monterey 227 14

...

Блобы

Получениеинформациио BLOb выглядитсовершенноаналогичнообычным полям.Полученныезначения можноотображатьс использованиемdata-aware компонентDelphi, например, TDBMemoили TDBGrid.Однако, в последнемслучае придетсясамому прорисовыватьсодержимоеблоба (например,через OnDrawDataCell).Подробнее обэтом см. на уроке, посвященномработе с полями.


SELECTjob_requirement
FROM jobполучитьсписок
должностныхтребований
ккандидатамна работу


JOB_REQUIREMENT:

Nospecific requirements.


JOB_REQUIREMENT:

15+years in finance or 5+ years as a CFO

witha proven track record.

MBAor J.D. degree.


...

Вычисления

SELECTemp_no, salary, salary * 1.15

FROMemployeeполучитьсписок номеров
служащихи их зарплату,
в том числеувеличеннуюна 15%


EMP_NO SALARY

============================ ======================

2 105900.00 121785

4 97500.00 112125

5 102750.00 118162.5

8 64635.00 74330.25

9 75060.00 86319

11 86292.94 99236.87812499999

12 53793.00 61861.95

14 69482.62 79905.01874999999

...


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

Например,в выраженииcol1 +col2 * col3 сначаланаходитсяпроизведениезначений столбцовcol2и col3,а затем результатэтого умноженияскладываетсясо значениемстолбца col1.А в выражении(col1+ col2) * col3 сначалавыполняетсясложение значенийстолбцов col1и col2,и только послеэтого результатумножаетсяна значениестолбца col3.

Литералы

Дляпридания большейнаглядностиполучаемомурезультатуможно использоватьлитералы. Литералы- это строковыеконстанты,которые применяютсянаряду с наименованиямистолбцов и,таким образом,выступают вроли “псевдостолбцов”.Строка символов,представляющаясобой литерал,должна бытьзаключена водинарные илидвойные скобки.


SELECTfirst_name, "получает",salary, "долларовв год"

FROMemployeeполучитьсписок сотрудников
иих зарплату


FIRST_NAME SALARY

=================== ========== ==============

Robert получает 105900.00 долларов вгод

Bruce получает 97500.00 долларов вгод

Kim получает 102750.00 долларов вгод

Leslie получает 64635.00 долларов вгод

Phil получает 75060.00 долларов вгод

K.J. получает 86292.94 долларов вгод

Terri получает 53793.00 долларов вгод

...

Конкатенация

Имеетсявозможностьсоединять дваили более столбца,имеющие строковыйтип, друг с другом,а также соединятьих с литералами.Для этогоиспользуетсяоперация конкатенации(||).


SELECT"сотрудник" || first_name || " " || last_name

FROMemployeeполучитьсписок всехсотрудников


==============================================

сотрудникRobert Nelson

сотрудникBruce Young

сотрудникKim Lambert

сотрудникLeslie Johnson

сотрудникPhil Forest

сотрудникK. J. Weston

сотрудникTerri Lee

сотрудникStewart Hall

...

ИспользованиеквалификатораAS

Дляпридания наглядностиполучаемымрезультатамнаряду с литераламив списке выбираемыхэлементов можноиспользоватьквалификаторAS. Данный квалификаторзаменяет врезультирующейтаблице существующееназвание столбцана заданное.Это наиболееэффективныйи простой способсоздания заголовков(к сожалению,InterBase, как уже отмечалось,не поддерживаетиспользованиерусских буквв наименованиистолбцов).


SELECTcount(*) AS number

FROMemployeeподсчитатьколичествослужащих


NUMBER

===========

42


SELECT"сотрудник" || first_name || " " || last_name ASemployee_list

FROMemployeeполучитьсписок всехсотрудников


EMPLOYEE_LIST

==============================================

сотрудникRobert Nelson

сотрудникBruce Young

сотрудникKim Lambert

сотрудникLeslie Johnson

сотрудникPhil Forest

сотрудникK. J. Weston

сотрудникTerri Lee

сотрудникStewart Hall

...

Работа сдатами

Мыуже рассказывалио типах данных,имеющихся вразличных СУБД,в том числе ив InterBase. В разныхсистемах имеетсяразличное числовстроенныхфункций, упрощающихработу с датами,строками идругими типамиданных. InterBase, ксожалению,обладает достаточноограниченнымнабором такихфункций. Однако,поскольку языкSQL, реализованныйв InterBase, соответствуетстандарту, тов нем имеютсявозможностиконвертациидат в строкии гибкой работыс датами. Внутреннедата в InterBase содержитзначения датыи времени. Внешнедата может бытьпредставленастроками различныхформатов, например:

  • “October 27,1995”

  • “27-OCT-1994”

  • “10-27-95”

  • “10/27/95”

  • “27.10.95”

Кромеабсолютныхдат, в SQL-выраженияхможно такжепользоватьсяотносительнымзаданием дат:

  • “yesterday”вчера

  • “today”сегодня

  • “now”сейчас(включая время)

  • “tomorrow”завтра


Датаможет неявноконвертироватьсяв строку (изстроки), если:

  • строка,представляющаядату, имеетодин из вышеперечисленныхформатов;

  • выражениене содержитнеоднозначностейв толкованиитипов столбцов.


SELECTfirst_name, last_name, hire_date

FROMemployee

WHEREhire_date > '1-1-94'получитьсписок сотрудников,
принятыхна работу после
1января 1994 года


FIRST_NAME LAST_NAME HIRE_DATE

=================================== ===========

Pierre Osborne 3-JAN-1994

John Montgomery 30-MAR-1994

Mark Guckenheimer 2-MAY-1994


Значениядат можно сравниватьдруг с другом,сравниватьс относительнымидатами, вычитатьодну дату издругой.


SELECTfirst_name, last_name, hire_date

FROMemployee

WHERE'today' - hire_date > 365 * 7 + 1
получитьсписок служащих,
проработавшихна предприятии
кнастоящемувремени
более7 лет


FIRST_NAME LAST_NAME HIRE_DATE

=================================== ===========

Robert Nelson 28-DEC-1988

Bruce Young 28-DEC-1988


Агрегатныефункции

Кагрегирующимфункциям относятсяфункции вычислениясуммы (SUM), максимального(SUM) и минимального(MIN) значенийстолбцов,арифметическогосреднего (AVG), атакже количествастрок, удовлетворяющихзаданномуусловию (COUNT).


SELECTcount(*), sum (budget), avg (budget),

min(budget), max (budget)

FROMdepartment

WHEREhead_dept = 100вычислить:количествоотделов,
являющихсяподразделениями
отдела 100 (Маркетинги продажи),
ихсуммарный,средний, мини-мальныйи максимальныйбюджеты


COUNT SUM AVG MIN MAX

================= ========== ========== ===========

5 3800000.00 760000.00 500000.00 1500000.00

ПредложениеFROM команды SELECT

ВпредложенииFROM перечисляютсявсе объекты(один или несколько),из которыхпроизводитсявыборка данных(рис.2). Каждаятаблица илипредставление,о которых упоминаетсяв запросе, должныбыть перечисленыв предложенииFROM.

Ограниченияна число выводимыхстрок

Числовозвращаемыхв результатезапроса строкможет бытьограниченопутем использованияпредложенияWHERE, содержащегоусловия отбора(предикат, рис.2).Условие отборадля отдельныхстрок можетприниматьзначения true,falseили unnown.При этом запросвозвращаетв качестверезультататолько те строки(записи), длякоторых предикатимеет значениеtrue.

Типыпредикатов,используемыхв предложенииWHERE:

  • сравнениес использованиемреляционныхоператоров

=равно

неравно

!=неравно

>больше

>=большеили равно

  • BETWEEN

  • IN

  • LIKE

  • CONTAINING

  • IS NULL

  • EXIST

  • ANY

  • ALL

Операциисравнения

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

Чтоже может бытьэлементомсравнения?Элементомсравнения можетвыступать:

  • значение поля

  • литерал

  • арифметическоевыражение

  • агрегирующаяфункция

  • другая встроеннаяфункция

  • значение (значения),возвращаемыеподзапросом.

При сравнениилитераловконечные пробелыигнорируются.Так, предложениеWHEREfirst_name = ‘Петр ‘будет иметьтот же результат,что и предложениеWHEREfirst_name = ‘Петр’.


SELECTfirst_name, last_name, dept_no

FROMemployee

WHEREjob_code = "Admin"получитьсписок сотрудников
(иномера ихотделов),
занимающихдолжность
администраторов


FIRST_NAME LAST_NAME DEPT_NO

=================================== =======


Terri Lee 000

Ann Bennet 120

SueAnne O'Brien 670

Kelly Brown 600


SELECTfirst_name, last_name, dept_no,

job_country

FROMemployee

WHEREjob_country "USA"получитьсписок сотрудников
(атакже номераих отделов
истрану),
работающихвне США


FIRST_NAME LAST_NAME DEPT_NO JOB_COUNTRY

=============================== ======= ==============

Ann Bennet 120 England

Roger Reeves 120 England

Willie Stansbury 120 England

Claudia Sutherland 140 Canada

Yuki Ichida 115 Japan

Takashi Yamamoto 115 Japan

Roberto Ferrari 125 Italy

Jacques Glon 123 France

Pierre Osborne 121 Switzerland

BETWEEN

ПредикатBETWEEN задает диапазонзначений, длякоторого выражениепринимаетзначение true.Разрешено такжеиспользоватьконструкцию NOT BETWEEN.


SELECTfirst_name, last_name, salary

FROMemployee

WHEREsalary BETWEEN 20000 AND 30000
получитьсписок сотрудников,
годоваязарплатакоторых
больше20000 и меньше 30000


FIRST_NAME LAST_NAME SALARY

========================= ===============

Ann Bennet 22935.00

Kelly Brown 27000.00


Тотже запрос сиспользованиемоператоровсравнения будетвыглядетьследующимобразом:


SELECTfirst_name, last_name, salary

FROMemployee

WHEREsalary >= 20000

ANDsalary годоваязарплатакоторых
больше20000 и меньше 30000


FIRST_NAME LAST_NAME SALARY

========================= ===============

Ann Bennet 22935.00

Kelly Brown 27000.00


Запросс предикатомBETWEEN может иметьследующий вид:


SELECTfirst_name, last_name, salary

FROMemployee

WHERElast_name BETWEEN "Nelson" AND "Osborne"
получитьсписок сотрудников,
фамилиикоторых начинаются
с“Nelson”
и заканчиваются“Osborne”


FIRST_NAME LAST_NAME SALARY

============================== ================

Robert Nelson 105900.00

Carol Nordstrom 42742.50

SueAnne O'Brien 31275.00

Pierre Osborne 110000.00


Значения,определяющиенижний и верхнийдиапазоны,могут не являтьсяреальнымивеличинамииз базы данных.И это оченьудобно - ведьмы не всегдаможем указатьточные значениядиапазонов!


SELECTfirst_name, last_name, salary

FROMemployee

WHERElast_name BETWEEN "Nel" AND "Osb"
получитьсписок сотрудников,
фамилиикоторых находятся
между “Nel” и “Osb”


FIRST_NAME LAST_NAME SALARY

============================== ================

Robert Nelson 105900.00

Carol Nordstrom 42742.50

SueAnne O'Brien 31275.00


В данном примерезначений “Nel”и “Osb” в базе данныхнет. Однако,все сотрудники,входящие вдиапазон, внижней частикоторого началофамилий совпадаетс “Nel” (т.е. выполняетсяусловие “большеили равно”), ав верхней частифамилия неболее “Osb” (т.е.выполняетсяусловие “меньшеили равно” - аименно “O”, “Os”,“Osb”), попадутв выборку. Отметим,что при выборкес использованиемпредикатаBETWEEN поле, на котороенакладываетсядиапазон, считаетсяупорядоченнымпо возрастанию.


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


SELECTfirst_name, last_name, hire_date

FROMemployee

WHEREhire_date NOT BETWEEN "1-JAN-1989" AND"31-DEC-1993"получитьсписок самых“старых”
исамых “молодых”(по времени
поступленияна работу)
сотрудников


FIRST_NAME LAST_NAME HIRE_DATE

=============================== ===========

Robert Nelson 28-DEC-1988

Bruce Young 28-DEC-1988

Pierre Osborne 3-JAN-1994

John Montgomery 30-MAR-1994

Mark Guckenheimer 2-MAY-1994

IN

ПредикатIN проверяет,входит ли заданноезначение,предшествующееключевому слову“IN” (например,значение столбцаили функцияот него) в указанныйв скобках список.Если заданноепроверяемоезначение равнокакому-либоэлементу всписке, то предикатпринимаетзначение true.Разрешено такжеиспользоватьконструкцию NOT IN.


SELECTfirst_name, last_name, job_code

FROMemployee

WHEREjob_code IN ("VP", "Admin", "Finan")
получитьсписок сотрудников,
занимающихдолжности
“вице-президент”,“администратор”,
“финансовыйдиректор”


FIRST_NAME LAST_NAME JOB_CODE

=============================== ========

Robert Nelson VP

Terri Lee Admin

Stewart Hall Finan

Ann Bennet Admin

SueAnne O'Brien Admin

MaryS. MacDonald VP

Kelly Brown Admin


Авот примерзапроса, использующегопредикат NOT IN:


SELECTfirst_name, last_name, job_country

FROMemployee

WHEREjob_country NOT IN

("USA","Japan", "England")
получитьсписок сотрудников,
работающихне в США, не вЯпонии
и нев Великобритании


FIRST_NAME LAST_NAME JOB_COUNTRY

=============================== ===============

Claudia Sutherland Canada

Roberto Ferrari Italy

Jacques Glon France

Pierre Osborne Switzerland

LIKE

ПредикатLIKE используетсятолько с символьнымиданными. Онпроверяет,соответствуетли данное символьноезначение строкес указанноймаской. В качествемаски используютсявсе разрешенныесимволы (с учетомверхнего инижнего регистров),а также специальныесимволы:

%- замещаетлюбое количествосимволов (в томчисле и 0),

_ - замещаеттолько одинсимвол.

Разрешено такжеиспользоватьконструкцию NOT LIKE.


SELECTfirst_name, last_name

FROMemployee

WHERElast_name LIKE "F%"получитьсписок сотрудников,
фамилиикоторых начинаютсяс буквы “F”


FIRST_NAME LAST_NAME

===================================

Phil Forest

Pete Fisher

Roberto Ferrari


SELECTfirst_name, last_name

FROMemployee

WHEREfirst_name LIKE "%er"получитьсписок сотрудников,
именакоторых заканчиваютсябуквами “er”

FIRST_NAME LAST_NAME

===================================

Roger De Souza

Roger Reeves

Walter Steadman


Атакой запроспозволяетрешить проблемупроизношения(и написания)имени:


SELECTfirst_name, last_name

FROMemployee

WHEREfirst_name LIKE "Jacq_es"
найтисотрудника(ов),
в имени которого
неизвестнопроизношение
буквы передокончанием“es”


FIRST_NAME LAST_NAME

===================================

Jacques Glon


Чтоделать, еслитребуется найтистроку, котораясодержит указанныевыше специальныесимволы (“%”,“_”) в качествеинформационныхсимволов? Естьвыход! Для этогос помощью ключевогослова ESCAPE нужноопределитьтак называемыйescape символ,который, будучипоставленнымперед символом“%” или “_”, укажет,что этот символявляетсяинформационным.Escape символ неможет бытьсимволом “\”(обратная косаячерта) и, вообщеговоря, долженпредставлятьсобой символ,никогда непоявляющийсяв упоминаемомстолбце какинформационныйсимвол. Частодля этих целейиспользуютсясимволы “@”и “~”.


SELECTfirst_name, last_name

FROMemployee

WHEREfirst_name LIKE "%@_%" ESCAPE "@"
получитьсписок сотрудников,
вимени которыхсодержится“_”
(знак подчеркивания)

CONTAINING

ПредикатCONTAINING аналогиченпредикату LIKE,за исключениемтого, что он нечувствителенк региструбукв. Разрешенотакже использоватьконструкцию NOT CONTAINING.


SELECTfirst_name, last_name

FROMemployee

WHERElast_name CONTAINING "ne"
получитьсписок сотрудников,
фамилиикоторых содержатбуквы
“ne”, “Ne”,“NE”, “nE”


FIRST_NAME LAST_NAME

===================================

Robert Nelson

Ann Bennet

Pierre Osborne


IS NULL

ВSQL-запросах NULLозначает, чтозначение столбцанеизвестно.Поисковыеусловия, в которыхзначение столбцасравниваетсяс NULL, всегда принимаютзначение unknown(и, соответственно,приводят кошибке), впротивоположностьtrueили false,т.е.

WHERE dept_no = NULL

или даже

WHERE NULL = NULL.


Предикат IS NULL принимаетзначение trueтолько тогда,когда выражениеслева от ключевыхслов “IS NULL” имеетзначение null(пусто, не определено).Разрешено такжеиспользоватьконструкцию IS NOT NULL, котораяозначает “непусто”, “имееткакое-либозначение”.


SELECTdepartment, mngr_no

FROMdepartment

WHEREmngr_no IS NULLполучитьсписок отделов,
в которых ещене назначены
начальники


DEPARTMENT MNGR_NO

================================

Marketing

SoftwareProducts Div.

SoftwareDevelopment

FieldOffice: Singapore


ПредикатыEXIST, ANY, ALL, SOME, SINGULAR мы рассмотримв разделе,рассказывающемо подзапросах.


Логическиеоператоры

Клогическимоператорамотносятсяизвестныеоператоры AND,OR, NOT, позволяющиевыполнятьразличныелогическиедействия: логическоеумножение (AND,“пересечениеусловий”), логическоесложение (OR,“объединениеусловий”), логическоеотрицание (NOT,“отрицаниеусловий”). Внаших примерахмы уже применялиоператор AND.Использованиеэтих операторовпозволяет гибко“настроить”условия отборазаписей.


ОператорAND означает,что общий предикатбудет истиннымтолько тогда,когда условия,связанные по“AND”, будут истинны.

ОператорOR означает,что общий предикатбудет истинным,когда хотя быодно из условий,связанных по“OR”, будет истинным.

ОператорNOT означает,что общий предикатбудет истинным,когда условие,перед которымстоит этотоператор, будетложным.


Водном предикателогическиеоператорывыполняютсяв следующемпорядке: сначалавыполняетсяоператор NOT,затем - AND и толькопосле этого- оператор OR.Для измененияпорядка выполненияоператоровразрешаетсяиспользоватьскобки.


SELECTfirst_name, last_name, dept_no,

job_code,salary

FROMemployee

WHEREdept_no = 622

ORjob_code = "Eng"

ANDsalary

ORDER BYlast_nameполучитьсписок служащих,
занятыхв отделе 622
или
на должности“инженер” сзарплатой
невыше 40000


FIRST_NAME LAST_NAME DEPT_NO JOB_CODE SALARY

========================= ======= ======== ===========

JenniferM. Burbank 622 Eng 53167.50

Phil Forest 622 Mngr 75060.00

T.J. Green 621 Eng 36000.00

Mark Guckenheimer 622 Eng 32000.00

John Montgomery 672 Eng 35000.00

Bill Parker 623 Eng 35000.00

Willie Stansbury 120 Eng 39224.06


SELECTfirst_name, last_name, dept_no,

job_code,salary

FROMemployee

WHERE(dept_no = 622

ORjob_code = "Eng")

ANDsalary

ORDER BYlast_nameполучитьсписок служащих,
занятыхв отделе 622
илина должности“инженер”,
зарплатакоторых не выше40000


FIRST_NAME LAST_NAME DEPT_NO JOB_CODE SALARY

========================= ======= ======== ===========

T.J. Green 621 Eng 36000.00

Mark Guckenheimer 622 Eng 32000.00

John Montgomery 672 Eng 35000.00

Bill Parker 623 Eng 35000.00

Willie Stansbury 120 Eng 39224.06


Преобразованиетипов (CAST)

ВSQL имеется возможностьпреобразоватьзначение столбцаили функциик другому типудля более гибкогоиспользованияопераций сравнения.Для этогоиспользуетсяфункция CAST.

Типыданных могутбыть конвертированыв соответствиисо следующейтаблицей:


Из типаданныхВ типданных

---------------------------------------

NUMERICCHAR,VARCHAR, DATE

CHAR,VARCHARNUMERIC, DATE

DATECHAR, VARCHAR,DATE


SELECTfirst_name, last_name, dept_no

FROMemployee

WHERECAST(dept_no AS char(20))

CONTAINING"00"получитьсписок сотрудников,
занятыхв отделах,
номеракоторых содержат“00”


FIRST_NAME LAST_NAME DEPT_NO

=================================== =======

Robert Nelson 600

Terri Lee 000

Stewart Hall 900

Walter Steadman 900

MaryS. MacDonald 100

OliverH. Bender 000

Kelly Brown 600

Michael Yanowski 100


Изменениепорядка выводимыхстрок (ORDER BY)

Порядоквыводимых строкможет бытьизменен с помощьюопционального(дополнительного)предложенияORDER BY в концеSQL-запроса. Этопредложениеимеет вид:


ORDER BY [ASC | DESC]


Порядокстрок можетзадаватьсяодним из двухспособов:

  • именамистолбцов

  • номерамистолбцов.


Способупорядочиванияопределяетсядополнительнымизарезервированнымисловами ASC и DESC.Способом поумолчанию -если ничегоне указано -являетсяупорядочивание“по возрастанию”(ASC). Если же указанослово “DESC”, тоупорядочиваниебудет производиться“по убыванию”.

Подчеркнемеще раз, чтопредложениеORDER BY должно указыватьсяв самом концезапроса.

Упорядочиваниес использованиемимен столбцов

SELECTfirst_name, last_name, dept_no,

job_code,salary

FROMemployee

ORDER BYlast_nameполучитьсписоксотрудников,
упорядоченныйпо фамилиям
в алфавитномпорядке


FIRST_NAME LAST_NAME DEPT_NO JOB_CODE SALARY

========================= ======= ======== ===========

Janet Baldwin 110 Sales 61637.81

OliverH. Bender 000 CEO 212850.00

Ann Bennet 120 Admin 22935.00

Dana Bishop 621 Eng 62550.00

Kelly Brown 600 Admin 27000.00

JenniferM. Burbank 622 Eng 53167.50

Kevin Cook 670 Dir 111262.50

Roger De Souza 623 Eng 69482.62

Roberto Ferrari 125 SRep 99000000.00

...


SELECTfirst_name, last_name, dept_no,

job_code,salary

FROMemployee

ORDER BYlast_name DESCполучитьсписоксотрудников,
упорядоченныйпо фамилиям
в порядке,обратном алфавитному


FIRST_NAME LAST_NAME DEPT_NO JOB_CODE SALARY

========================= ======= ======== ===========

Katherine Young 623 Mngr 67241.25

Bruce Young 621 Eng 97500.00

Michael Yanowski 100 SRep 44000.00

Takashi Yamamoto 115 SRep 7480000.00

Randy Williams 672 Mngr 56295.00

K.J. Weston 130 SRep 86292.94

Claudia Sutherland 140 SRep 100914.00

Walter Steadman 900 CFO 116100.00

Willie Stansbury 120 Eng 39224.06

Roger Reeves 120 Sales 33620.62

...


Столбец,определяющийпорядок выводастрок, не обязательнодожен присутствоватьв списке выбираемыхэлементов(столбцов):


SELECTfirst_name, last_name, dept_no,

job_code

FROMemployee

ORDER BYsalaryполучитьсписоксотрудников,
упорядоченныйпо их зарплате


FIRST_NAME LAST_NAME DEPT_NO JOB_CODE

============================== ======= ========

Ann Bennet 120 Admin

Kelly Brown 600 Admin

SueAnne O'Brien 670 Admin

Mark Guckenheimer 622 Eng

Roger Reeves 120 Sales

Bill Parker 623 Eng

Упорядочиваниес использованиемномеров столбцов

SELECTfirst_name, last_name, dept_no,

job_code,salary * 1.1

FROMemployee

ORDER BY5получитьсписоксотрудников,
упорядоченныйпо их зарплате
с10% надбавкой


FIRST_NAME LAST_NAME DEPT_NO JOB_CODE

========================= ======= ======== ===========

Ann Bennet 120 Admin 25228.5

Kelly Brown 600 Admin 29700

SueAnne O'Brien 670 Admin 34402.5

Mark Guckenheimer 622 Eng 35200

Roger Reeves 120 Sales 36982.6875

Bill Parker 623 Eng 38500


Допускаетсяиспользованиенесколькихуровней вложенностипри упорядочиваниивыводимойинформациипо столбцам;при этом разрешаетсясмешивать обаспособа.


SELECTfirst_name, last_name, dept_no,

job_code,salary * 1.1

FROMemployee

ORDER BYdept_no, 5 DESC, last_name
получитьсписоксотрудников,
упорядоченныйсначала по
номерам отделов,
в отделах - поубыванию их
зарплаты (с10%),
а в пределаходной зарплаты- по фамилиям


FIRST_NAME LAST_NAME DEPT_NO JOB_CODE

===================== ======= ======== ===============

OliverH. Bender 000 CEO 234135

Terri Lee 000 Admin 59172.3

MaryS. MacDonald 100 VP 122388.75

Michael Yanowski 100 SRep 48400.000000001

Luke Leung 110 SRep 75685.5

Janet Baldwin 110 Sales 67801.59375

Takashi Yamamoto 115 SRep 8228000.0000001

Yuki Ichida 115 Eng 6600000.0000001

Устранениедублирования(модификаторDISTINCT)

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

Иногда(в зависимостиот задачи) бываетнеобходимоустранить всеповторы строкиз результирующегонабора. Этойцели служитмодификаторDISTINCT. Данныймодификаторможет бытьуказан толькоодин раз в спискевыбираемыхэлементов идействует навесь список.


SELECTjob_code

FROMemployeeполучитьсписок должностейсотрудников


JOB_CODE

========

VP

Eng

Eng

Mktg

Mngr

SRep

Admin

Finan

Mngr

Mngr

Eng

...


Данныйпример некорректнорешает задачу“получения”списка должностейсотрудниковпредприятия,так как в немимеются многочисленныеповторы, затрудняющиевосприятиеинформации.Тот же запрос,включающиймодификаторDISTINCT, устраняющийдублирование,дает верныйрезультат.


SELECTDISTINCT job_code

FROMemployeeполучить списокдолжностейсотрудников


JOB_CODE

========

Admin

CEO

CFO

Dir

Doc

Eng

Finan

Mktg

Mngr

PRel

SRep

Sales

VP


Дваследующихпримера показывают,что модификаторDISTINCT действуетна всю строкусразу.


SELECTfirst_name, last_name

FROMemployee

WHEREfirst_name = "Roger"получитьсписок служащих,
именакоторых - Roger


FIRST_NAME LAST_NAME

===================================

Roger De Souza

Roger Reeves


SELECTDISTINCT first_name, last_name

FROMemployee

WHEREfirst_name = "Roger"получитьсписок служащих,
именакоторых - Roger


FIRST_NAME LAST_NAME

===================================

Roger De Souza

Roger Reeves


Соединение(JOIN)

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

Послеизучения этогораздела мыбудем способны:

  • соединятьданные из несколькихтаблиц в единуюрезультирующуютаблицу;

  • задавать именастолбцов двумяспособами;

  • записыватьвнешние соединения;

  • создаватьсоединениятаблицы с собой.


Операциисоединенияподразделяютсяна два вида -внутренниеи внешние. Обавида соединенийзадаются впредложенииWHERE запроса SELECT спомощью специальногоусловия соединения.Внешние соединения(о которых мыпоговоримпозднее) поддерживаютсястандартомANSI-92 и содержатзарезервированноеслово “JOIN”, в товремя как внутренниесоединения(или простосоединения)могут задаватьсякак без использованиятакого слова(в стандартеANSI-89), так и с использованиемслова “JOIN” (встандартеANSI-92).

Связываниепроизводится,как правило,по первичномуключу однойтаблицы и внешнемуключу другойтаблицы - длякаждой парытаблиц. Приэтом оченьважно учитыватьвсе поля внешнегоключа, иначерезультат будетискажен. Соединяемыеполя могут (ноне обязаны!)присутствоватьв списке выбираемыхэлементов.ПредложениеWHERE может содержатьмножественныеусловия соединений.Условие соединенияможет такжекомбинироватьсяс другими предикатамив предложенииWHERE.


Внутренниесоединения

Внутреннеесоединениевозвращаеттолько те строки,для которыхусловие соединенияпринимаетзначение true.


SELECTfirst_name, last_name, department

FROMemployee, department

WHEREjob_code = "VP"получитьсписок сотрудников,
состоящихв должности“вице-
президент”,а также названия
ихотделов


FIRST_NAME LAST_NAME DEPARTMENT

=============================== ======================

Robert Nelson Corporate Headquarters

MaryS. MacDonald Corporate Headquarters

Robert Nelson Sales and Marketing

MaryS. MacDonald Sales and Marketing

Robert Nelson Engineering

MaryS. MacDonald Engineering

Robert Nelson Finance

MaryS. MacDonald Finance

...


Этотзапрос (“безсоединения”)возвращаетневерный результат,так как имеющиесямежду таблицамисвязи не задействованы.Отсюда и появляетсядублированиеинформациив результирующейтаблице. Правильныйрезультат даетзапрос с использованиемоперации соединения:


SELECTfirst_name, last_name, department

FROMemployee, department

WHEREjob_code = "VP"

ANDemployee.dept_no = department.dept_no


именатаблиц

получитьсписок сотрудников,
состоящихв должности“вице-
президент”,а также названия
ихотделов


FIRST_NAME LAST_NAME DEPARTMENT

=============================== ======================

Robert Nelson Engineering

MaryS. MacDonald Sales and Marketing


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

Замечание1: в одном запросенельзя смешиватьиспользованиенаписания иментаблиц и ихалиасов.

Замечание2: алиасы таблицмогут совпадатьс их именами.


SELECTfirst_name, last_name, department

F

ROMemployee e, department d

WHEREjob_code = "VP"

AND e.dept_no = d.dept_no


алиасытаблиц

получитьсписок сотрудников,
состоящихв должности“вице-
президент”,а также названия
ихотделов


FIRST_NAME LAST_NAME DEPARTMENT

=============================== ======================

Robert Nelson Engineering

MaryS. MacDonald Sales and Marketing


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


SELECTfirst_name, last_name, job_title,

department

FROMemployee e, department d, job j

WHEREd.mngr_no = e.emp_no

ANDe.job_code = j.job_code

ANDe.job_grade = j.job_grade

ANDe.job_country = j.job_country
получитьсписок сотрудников
сназваниямиих должностей
и названиямиотделов


FIRST_NAMELAST_NAME JOB_TITLE DEPARTMENT

====================== ======================= ======================

Robert Nelson Vice President Engineering

Phil Forest Manager Quality Assurance

K.J. Weston Sales Representative Field Office: East Coast

Katherine Young Manager Customer Support

Chris Papadopoulos Manager Research and Development

Janet Baldwin Sales Co-ordinator Pacific Rim Headquarters

Roger Reeves Sales Co-ordinator European Headquarters

Walter Steadman Chief Financial Officer Finance


Вданном примерепоследние триусловия необходимыв силу того,что первичныйключ в таблицеJOB состоит изтрех полей -см. рис.1.


Мырассмотреливнутренниесоединенияс использованиемстандартаANSI-89. Теперь опишемновый (ANSI-92) стандарт:

  • условия соединениязаписываютсяв предложенииFROM, в которомслева и справаот зарезервированногослова “JOIN” указываютсясоединяемыетаблицы;

  • условия поиска,основанныена правойтаблице, помещаютсяв предложениеON;

  • условия поиска,основанныена левой таблице,помещаютсяв предложениеWHERE.


SELECTfirst_name, last_name, department

FROMemployee e JOIN department d

ONe.dept_no = d.dept_no

ANDdepartment = "Customer Support"

WHERElast_name starting with "P"
получитьсписок служащих
(азаодно и названиеотдела),
являющихсясотрудникамиотдела
“CustomerSupport”, фамилиикото-
рых начинаютсяс буквы “P”


FIRST_NAME LAST_NAME DEPARTMENT

============================ ===================

Leslie Phong Customer Support

Bill Parker Customer Support


Самосоединения

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


SELECTone.last_name, two.last_name,

one.hire_date

FROMemployee one, employee two

WHEREone.hire_date = two.hire_date

ANDone.emp_no получитьпары фамилийсотрудников,
которыеприняты наработу в один
и тот же день


LAST_NAME LAST_NAME HIRE_DATE

======================================== ===========

Nelson Young 28-DEC-1988

Reeves Stansbury 25-APR-1991

Bishop MacDonald 1-JUN-1992

Brown Ichida 4-FEB-1993


SELECTd1.department, d2.department, d1.budget

FROMdepartment d1, department d2

WHEREd1.budget = d2.budget

ANDd1.dept_no получитьсписок паротделов с
одинаковымигодовыми бюджетами


DEPARTMENT DEPARTMENT BUDGET

======================== ========================= =========

SoftwareDevelopment Finance 400000.00

FieldOffice: East Coast Field Office: Canada 500000.00

FieldOffice: Japan Field Office: East Coast 500000.00

FieldOffice: Japan Field Office: Canada 500000.00

FieldOffice: Japan Field Office: Switzerland 500000.00

FieldOffice: Singapore Quality Assurance 300000.00

FieldOffice: Switzerland Field Office: East Coast 500000.00


Внешниесоединения

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

Вспомним,запрос вида


SELECTfirst_name, last_name, department

FROMemployee e, department d

WHEREe.dept_no = d.dept_no


возвращаеттолько те строки,для которыхусловие соединения (e.dept_no= d.dept_no) принимаетзначение true.

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


Существуетдва вида внешнегосоединения: LEFT JOIN и RIGHT JOIN.


В левом соединении(LEFT JOIN) запросвозвращаетвсе строкииз левой таблицы(т.е. таблицы,стоящей слеваот зарезервированногословосочетания“LEFT JOIN”) и толькоте из правойтаблицы, которыеудовлетворяютусловию соединения.Если же в правойтаблице ненайдется строк,удовлетворяющихзаданномуусловию, то врезультатеони замещаютсязначениямиnull.

Для правогосоединения- все наоборот.


SELECTfirst_name, last_name, department

FROMemployee e LEFT JOIN department d

ONe.dept_no = d.dept_no
получитьсписок сотрудников
иназвание ихотделов,
включаясотрудников,еще
не назначенныхни в какой отдел


FIRST_NAME LAST_NAME DEPARTMENT

============================= =====================

Robert Nelson Engineering

Bruce Young Software Development

Kim Lambert Field Office: East Coast

Leslie Johnson Marketing

Phil Forest Quality Assurance

...


Вданном запросевсе сотрудникиоказалисьраспределеныпо отделам,иначе названияотделов заместилисьбы значениемnull.


Авот примерправого соединения:


SELECTfirst_name, last_name, department

FROMemployee e RIGHT JOIN department d

ONe.dept_no = d.dept_no
получитьсписок сотрудников
иназвание ихотделов,
включаяотделы, в которыееще
не назначенысотрудники


FIRST_NAME LAST_NAME DEPARTMENT

============================ =========================

Terri Lee Corporate Headquarters

OliverH. Bender Corporate Headquarters

MaryS. MacDonald Sales and Marketing

Michael Yanowski Sales and Marketing

Robert Nelson Engineering

Kelly Brown Engineering

Stewart Hall Finance

Walter Steadman Finance

Leslie Johnson Marketing

Carol Nordstrom Marketing

Software Products Div.

Bruce Young Software Development

...


Врезультирующийнабор входити отдел “Software ProductsDiv.” (а также отдел“Field Office: Singapore”, не представленныйздесь), в которомеще нет ни одногосотрудника.

36

Урок 10:Основы языкаSQL



29

С

озданиебаз данных вDelphi
Урок 11:Генерацияотчетов

Содержание

Урок 11:Генерацияотчетов1

Содержание1

1. Компонентыдля построенияотчетов2

2. КомпонентTQuickRep3

Свойства4

Методы7

События9

3. КомпонентTQRBand9

4. Созданиепростейшегоотчета11

5. ИспользованиекомпонентаTQREXPR14

6. ИспользованиеTQRBand для представлениязаголовковстолбцов18

7. ИспользованиеTQRBand для показазаголовка иподвала страницы.18

8. ИспользованиекомпонентаTQRSysData19

9. Группировкиданных20

10. Множественнаягруппировкаданных23

11. Построениеотчета главный-детальный24

12. Построениекомпозитногоотчета28


1.Компонентыдля построенияотчетов

На страницепалитры компонентовQReport расположеноболее двухдесятков компонентов,применяемыхдля построенияотчетов.

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

TQRBand –заготовкадля расположенияданных, заголовков,титула отчетаи др. Отчет, восновном, строитсяиз компонентовTQRBand, которыереализуют:

  • областьзаголовкаотчета;

  • областьзаголовкастраницы;

  • областьзаголовкагруппы;

  • областьназваний столбцовотчета;

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

  • областьподвала группы;

  • областьподвала страницы;

  • областьподвала отчета.

TQRStringsBand –имеет то женазначение,что и TQRBand.Отличаетсявстроеннымсписком строкItems, содержимоекоторого становитсявидным в режимепечати и предварительногопросмотра, еслина компонентTQRStringsBand положенкомпонентTQRExpr. Длякаждой строкив Items выводитсясвоя полосаTQRStringsBand.

TQRSubDetail –дочерняяполоса. Привязываетсяк родительскойполосе и служитдля ее расширения.Любая полосаможет статьродительскойс помощью установкизначения Trueв ее свойствоHasChild.

TQRGroup –применяетсядля группировокданных в отчетах.

TQRLabel –позволяетразместитьв отчете произвольнуютекстовуюстроку.

TQRDBText –служит длявывода в отчетсодержимоготекстовогополя набораданных.

TQRExpr –применяетсядля выводазначений, являющихсярезультатомвычисленийвыражений.Алгоритм вычислениявыраженийстроится припомощи редактораформул данногокомпонента.

TQRSysData –служит длявывода в отчетесистемнойвеличины: даты,времени, номерастраницы и т.п.

TQRMemo –вставляетв отчет многостраничныйтекст.

TQRExprMemo – используетсядля созданиямногострочныхвычисляемыхполей.

TQRRichText –вставляетв отчет многострочныйтекст в форматеRTF.

TQRDBRichText –служит длявывода в отчетеполей НД, содержащихмногострочныйтекст в форматеRTF.

TQRShape –служит длявывода в отчетеграфическихфигур, например,прямоугольников.

TQRImage –служит длявывода в отчетеграфическойинформации,источникомкоторой являетсяполе набораданных.

TQRPreview –базовый компонентдля созданиянестандартныхокон предварительногопросмотра.Стандартноеокно реализуетсяс помощью методаPreview компонентаTQuickRep.

TQRXXXFilter –фильтрующиекомпонентыдля преобразованияотчета в текст,страницу HTMLи т.п. при печатиотчета.

TQRChart –служит длявстраиванияв отчет графиков.


2.КомпонентTQuickRep

При размещенииэтого компонентана форме в нейпоявляетсясетка отчета(рис.1). В дальнейшемв этой сеткерасполагаютсясоставные частиотчета, например,полосы TQRBand(рис.2).


Рис.1. Пустая сеткаотчета. Образуетсяпосле размещенияна форме компонентаTQuickRep.


Рис.2. Сетка отчетас размещеннымив ней компонентамиотчета.


Перечислимважнейшиесвойства, методыи события компонентаTQuickRep.


Свойства

Свойство

Назначение

propertyBands: TQuickRepBands;

Объект Bandsсодержитлогическиесвойства, которыепосле установкив них значенийTrue включаютв отчет: HasColumnHeader– заголовкистолбцов;HasDetail –детальнуюинформацию;HasPageFooter –подвал страницы;HasPageHeader –заголовокстраницы;HasSummary –подвалотчета; HasTitle– заголовокотчета.

propertyDataset:TDataSet;

Указываетнабор данныхна основе которогосоздаетсяотчет. Еслинужно вывестисвязаннуюинформациюиз несколькихтаблиц БД, ееобъединяютв одном НД припомощи компонентаTQuery.Информациюиз несколькихсвязанных НДможно включатьв отчет, еслиэти НД связаныв приложенииотношениемглавный-подчиненный.В этом случаев качествеНД отчетауказываетсяглавный набор,а ссылка насоответствующиеподчиненныенаборы осуществляетсяв компонентахTQRSubDetail.Если в отчетнужно включитьинформациюиз несвязанныхНД, применяетсякомпозитныйотчет, то естьотчет, составленныйиз группы другихотчетов.

propertyFrame:TQRFrame;

Определяетпараметрырамки отчета:Color –цвет линий;DrawBottom –наличие линииснизу;DrawLeft –наличиелинии слева;DrawRight –наличиелинии справа;DrawTop –наличиелинии сверху;Style –стильлинии (сплошная,пунктирнаяи т.п.); Width –толщиналинии в пикселях.

propertyOptions:TQuickReportOptions;

Содержитмножество изследующихлогическихзначений:HasFirstHeader –разрешаетпечатать заголовокпервой страницы;HasLastFooter –разрешаетпечатать подвалпоследнейстраницы;Compression –разрешаетсжимать отчетпри выводеего в метафайл.

propertyPage:TQRPage;

Определяетпараметрыстраницы отчета.Все подсвойстваэтого сложногосвойства доступныв окне ReportSetting (см. нижегруппы Pagesize и Margin окнаредакторасвойств).

propertyPrintIfEmpty:boolean;

Разрешает/запрещаетпечатать отчетв том случаеесли он несодержит данных.

propertyReportTitle:String;

Имя отчета(не его заголовок!). Используетсядля идентификацииотчета в заданиина сетевуюпечать, возвращаетсякомпонентомQRSysData приData =ReportTitle иможет использоватьсядля набораодного изнесколькихдоступныхотчетов.

propertyShowProgress:boolean;

Разрешает/запрещаетпоказыватьиндикаторпроцесса печатиотчета.

propertySnapToGrid:boolean;

Еслисодержит True,размещаемыев отчете компонентыпривязываютсяк сетке отчета.

typeTQRUnits = (Inches,MM,Pixels, Native, Characters);

propertyUnits:TQRUnits;

Определяетединицы измерениярасстоянийв отчете: Inches– дюймы;MM –миллиметры;Pixels –пиксели;Native –внутренниеединицы TQuickRep(равны0,1 мм); Characters –символы текста.

propertyZoom: Integer;

Определяетмасштаб отображенияотчета (в процентахот его размеровна листе бумаги)на этапе разработки.Может иметьзначение вдиапазоне1..300. Значениесвойства неучитываетсяпри печатиотчета или врежиме егопредварительногопоказа.

Многие свойстваотчета можноустановитьна этапе конструированияс помощью редакторасвойств – вызовителокальное менюкомпонентаTQuickRep и выберитеопцию ReportSettings.

Рис.3. Окно установкипараметровотчета.


Группа Paper size задаетхарактеристикистраницы: ееформат (A4 210 x 270 mm),ширину (Width), длину(Length) и направлениепечати – вдолькороткой сторонылиста (Portait) иливдоль длинной(Landscape).

Группа элементовMargin указываетполя отчета:сверху (Top),снизу (Bottom),слева (Left),справа(Right),а также количествоколонок (Numberof columns) и расстояниемежду ними(Column Space).

С помощью элементовгруппы Otherможно задатьшрифт (Font),его высоту(Size)и используемыеединицы измерениядлины (Units).

Группа Pageframe определяетсвойства рамки:наличие линиисверху (Top),снизу (Bottom),слева (Left),справа(Right),цвет линий(Color)и их толщину(Width).

Группа Bandsопределяетналичие полосзаголовкови подвалов(Page header – заголовокстраницы;Title – заголовокотчета; Columnheader – заголовокколонок; Detailband – полосадля детальнойинформации;Page footer – подвалстраницы; Summary– подвал отчета),а также высотусоответствующейполосы (строкаLength справаот переключателявыбора). Послевыбора типаи высоты полосыона появляетсяв отчете, еслиокно закрытокнопкой OKили была нажатакнопка Applay.ЭлементыPrint first page header иPrint last page footer управляютсоответственнопечатью заголовкана первой страницеи подвала наего последнейстранице.


Методы

Метод

Назначение

procedureNewColumn;

Реализуетвывод информациив следующейколонке отчета,а если определенаединственнаяколонка, – вего следующейстранице.

procedureNewPage;

Реализуетвывод информациив следующейстранице отчета.

procedurePrepare;

Готовитотчет для выводав файл (см. нижепримечание1).

procedurePreview;

Выводитстандартноеокно предварительногопросмотра(см. ниже примечание2).

procedurePrint;

Печатаетотчет на принтере.

procedurePrintBackGround;

Инициируетпечать отчетав фоновом режиме(в отдельномпотоке команд).После завершенияпечати вызываетсяобработчиксобытия OnAfterPrint.

procedurePrinterSetup;

Вызываетстандартноеокно установкипараметровпринтера.

Примечание1.

Для выводаотчета в файлнужно сначалаподготовитьего с помощьюобращения кметоду Prepare, затемсохранить вфайле методомSave объектаTQuickRep.QRPrinter, после чегоуничтожитьэтот объекти поместитьNIL в свойствеTQuickRep.QRPrinter:


MyReport.Prepare;

MyReport.QRPrinter.Save(‘REport.QRP’);

MyReport.QRPrinter.Free;

MyReport.QRPrinter:= NIL;


Примечание2.

Стандартноеокно предварительногопросмотрапоказано нарис. 4.

Рис.4. Окно предварительногопросмотраотчета.


Чтобы на этапеконструированияпросмотретьв окне предварительногопросмотрасодержимоеотчета в томвиде, как онбудет выводитьсяна печать, нужновыбрать опциюPreview во вспомогательномменю компонентаQuickRep. Следуетзаметить, чтопри этом небудут виднынекоторыеданные, напримерзначения вычисляемыхполей. Они будутвыводитьсятолько во времявыполнения.

Н

азначениеинструментальныхкнопок окна:

М

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

О

тображаетотчет в масштабе1:1.

М

асштабируетотчет так, чтобыширина страницыотчета соответствовалаширине окна.

П

оказываетпервую (последнюю)страницу отчета.

П

оказываетпредыдущую(следующую)страницу отчета.

Вызываетстандартноеокно настройкипринтера (печатаетотчет).

С

охраняетотчет в файле(загружаетотчет из файла).

События

Событие

Назначение

propertyAfterPreview : TQRAfterPreviewPrint;

Возникаетв момент закрытияокна предварительногопросмотраотчета.

propertyAfterPrint:TQRAfterPrintEvent;

Наступаетпосле печатиотчета илиего подготовкик печати.

propertyBeforePrint:TQRBeforePrintEvent;

Наступаетв момент началагенерацииотчета (до выдачиокна предварительногопросмотраотчета илидо его печати).

propertyOnEndPage:procedure (Sender:TObject);

Возникаетв момент подготовкик генерациипоследнейстраницы отчета.

propertyOnNeedData:procedure(Sender:TObject; varMoreData:boolean);

Используетсяпри созданииотчета по данным,которые берутсяне из НД, а изтекстовогофайла, спискастрок, массива и т.п. В параметреMoreDataобработчикдолжен вернутьTrue,если источникданных ещене исчерпан.

propertyOnPreview:procedure(Sender:TObject);

Используетсядля связыванияс отчетомнестандартногоокна просмотра(см. ниже).

propertyOnStartPage:procedure (Sender:TObject);

Возникаетв момент подготовкик генерациипервой страницыотчета.

С помощью компонентаQRPreviewпрограммистможет создатьнестандартноеокно предварительногопросмотра. Длясвязи с отчетомиспользуетсясобытие OnPreviewпо следующейсхеме:


ProcedureRepForm.MyREportOnPreviewEvent(Sender: TObject);

begin

MyPrevForm.QRPreview1.QRPrinter:= TQRPrinter(Sender);

MyPreviewForm.Show;

end;


Чтобы явноеприведениетипа TQRPrinter(Sender) сталовозможным,необходимассылка на модульQRPrntr в предложенииUses соответствующегомодуля (в примере– модуля RepForm).


3.КомпонентTQRBand

КомпонентыTQRBand являютсяосновнымичастями отчетаи используютсядля размещенияна них отображающихкомпонентов,таких как TQRLabel,TQRDBText,TQRImage ит.п.


Свойства компонента:

Свойство

Назначение

propertyAlignToBottom: boolean;

Еслиимеет значениеTrueполосапечатаетсянепосредственнонад подваломстраницы вместообычногорасположениясправа/снизуот предыдущейполосы.

typeTQRBandType = (rbTitle,rbPageHeader,rbDetail,rbPageFooter, rbSummary, rbGroupHeader, rbGroupFooter,rbSubDetail, rbColumnHeader);

propertyBandType:TQRBandType;

Указываетназначениеполосы:rbTitle– содержитзаголовокотчета; rbPageHeader– содержитзаголовокстраницы (напервой страницепечатаетсяпод rbTitle);rbDetaul –содержитинформациюиз НД; выводитсявсякий разпри переходена новую записьНД; эта полосаповторяетсядля всех записейDataSet,начиная с первойзаписи и заканчиваяпоследней;позицированиена первую записьи последовательныйих переборосуществляетсякомпонентомTQuickRepавтоматически;rbPageFooter– содержитподвал страницы;выводится вконце каждойстраницы отчетапосле всехдругих полос;rbSummary –подвал отчета;выводится напоследнейстранице отчетапосле всейиной информации,но перед подваломпоследнейстраницы;rbGroupHeader –содержит заголовокгруппы; применяетсяпри группировкахинформациив отчете ивыводитсявсякий разпри выводеновой группы;rbGroupFooter– содержитподвал группы;выводитсявсякий разпри окончаниивывода группы,после всехданных группы;rbSubDetail– содержитдетальнуюинформациюиз подчиненногоНД при выводев отчете информациииз двух илиболее наборовданных, связанныхв приложениикак главный-подчиненный;этот тип назначаетсяполосе автоматическипри размещениина форме компонентаTQRSubDetail;rbColumnHeader– содержитзаголовкистолбцов;размещаетсяна каждой страницеотчета послезаголовкастраницы.

propertyEnabled:boolean;

Разрешает/запрещаетпечать полосы.

propertyForceNewColumn:boolean;

Если содержитTrue, полоса печатаетсяв следующейколонке.

propertyForceNewPage:boolean;

Еслисодержит True,полосапечатаетсяна новой странице.

propertyHasChild:boolean;

Еслисодержит True,полоса имеетдочернюю полосуTChildBand.УстановкаTrueв этосвойствоавтоматическисоздает в отчетедочернюю полосу.


События


propertyAfterPrint: TQRAfterPrintEvent;


и


propertyBeforePrint: TQRBeforePrintEvent;


наступаютсоответственнодо и после печатиполосы. Метод


functionAddPrintable(PrintableClass: TQRNewComponentClass): TQRPrintable;


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

withDetailBand1.AddPrintable(TQRLabel) do

begin

Size.Left := 20;

Size.Top := 5;

Caption := ‘Новаяполоса’;

end;


var

aLabel: TQRLabel;

begin

aLabel:= TQRLabel.Create(ReportForm);

aLabel.Parent :=DetailBand1;

withaLabel do

begin

Size.Left := 20;

Size.Top := 5;

Caption := ‘Новаяполоса’;

end;

end;


4.Созданиепростейшегоотчета

КомпонентыTQuickRep иTQRBand являютсяминимальнодостаточнымидля созданияпростейшегоотчета, несодержащеговнутри себягруппировокинформации.

Пусть имеетсятаблица БДRashod.DB, содержащаясведения оботпуске материаловсо склада. Всостав ТБДвходят поля

  • N_RASH– уникальныйномер событияотпуска товара;

  • DEN– номер дня;

  • MES– номер месяца;

  • GOD– номер года;

  • TOVAR– наименованиеотпущенноготовара;

  • POKUP– наименованиепокупателя;

  • KOLVO– количествоединиц отпущенноготовара.

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

Создадим простейшийотчет, состоящийиз заголовкаи сведений оботпуске товара.В отчет включаютсявсе факты отпускатовара. Сортировкапроизводитсяпо номеру событияотпуска товара.Для этого разместимна форме компонентTTable, свяжемего с таблицейRashod.DB и откроем(Active = True).Разместимна форме компонентTQuickRep.Поместим вего свойствоDataSet значениеTable1, назначивтаким образомотчету НД, записикоторого будутвыводитьсяв отчете. Добавимв отчет компонентTQRBand. В егосвойство BandTypeкомпонентаQRBand1 по умолчаниюбудет установленозначение rbTitle,то есть компонентQRBand1 определяетзаголовокотчета Разместимна QRBand1 компонентTQRLabel. Установимв свойствоCaption этогокомпонентазначение Отпусктоваров сосклада и выберемв свойстве Fontжирный наклонныйшрифт высотой16 пунктов. Видформы отчетак этому моментупоказан нарис.5.

Рис.5. В отчете определентолько егозаголовок.


Теперь разместимв отчете данные,соответствующиетекущей записитаблицы Rashod.Для этогопоместим вотчет новыйкомпонентTQRBand (имяQRBand2) иустановим вего свойствоBandType значениеrbDetail. Затемразместим наполосе QRBand2шестькомпонентовTQRDBText.Свяжем этикомпонентыс полями НД –N_RASH, TOVAR,KOLVO,DEN, MES,GOD. Дляэтого в свойствоDataSet каждогокомпонентаQRDBText установимзначение Table1,а в свойствоDataField –имя соответствующегополя. Вид отчетак этому моментупоказан нарис.6.


Рис.6. Отчет с заголовкоми группой детальнойинформации.


Для просмотраполучившегосяотчета щелкнемпо нему правойкнопкой мышии из всплывающегоменю выберемэлемент Preview.Окно предварительногопросмотраотчета показанона рис. 7.


Рис.7. Содержимоеотчета в окнепредварительногопросмотра.


Чтобы окнопредварительногопросмотраоткрывалосьпри активизацииформы, создадимтакой обработчиксобытия OnActivateформы:


procedureTForm1.FormActivate(Sender: TObject);

begin

QuickRep1.Preview;

end;


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


procedureTForm1.QuickRep1AfterPreview(Sender: TObject);

begin

Form1.Close;

end;


5.ИспользованиекомпонентаTQREXPR

Из рис.7 видно,что в простейшемотчете выводитсядата, составленнаяиз трех полей– DEN, MES, GOD. Объединимзначения изэтих полей водно значение,являющеесярезультатомвычислениявыражения.Выражение вотчетах формируетсяпри помощикомпонентаTQRExpr. Удалимиз компонентаQRBand2 компонентыQRDBText4,QRDBText5 и QRDBText6,связанные сполями DEN,MES, GOD.Вместо нихразместим вотчете компонентTQRExpr (имяQRExpr1).

Выражения вTQRExpr формируютсяс помощьюспециальногоредактора,который вызываетсяв окне инспектораобъектов кнопкойв поле данныхсвойства Expressionэтого компонента(рис.8).


Рис.8. Окно редактораформул компонентаTQRExpr.


В поле Enterexpression можноввести илиотредактироватьвыражение,которое обычносостоит из именполей НД, преобразующихфункций и переменных,связанныхоперациямиотношения.Имена полейНД добавляютсяв текущее положениекурсора (полеEnter expression)с помощьювспомогательногоокна, связанногос кнопкой Function,а переменные– с кнопкойVariable.

Нажмите кнопкуFunction, в левомокне выберитекатегорию Other(другие) ифункцию STRв правом окне– эта функцияпреобразуетчисловое значениев строковое.Нажмите Continue,чтобы перейтик вводу параметров(рис.9). Надписьнад строкойввода окнаExpression Wizard напоминаето том, что выбраннаянами функцияимеет одинчисловой параметр.


Рис.9. Формированиечасти выражения.


Для его вводанажмите кнопкусправа от строкиввода – на экраневновь появитсяначальное окноредактораформул. Посколькумы хотим преобразоватьв строку номердня, нажмитекнопку Databasefield и выберитеполе DENв списке полейтаблицы Table1.Нажмите OK,чтобы завершитьввод параметра.В поле Enterexpression будет сформированачасть формулы– STR(Table1.DEN).На панелиInsert at cursor position нажмемкнопку «+» ивручную введемразделитель‘.’ (рис.10).


Рис.10. Создание частиформулы выражения.


Продолжитеформироватьвыражение так,чтобы в концеконцов оноприобрело такойвид:

STR(Table1.DEN)+ ‘.’ + STR(Table1.MES) + ‘.’ +STR(Table1.GOD)

(возможнопроще ввестиего вручную).Затем нажмитекнопку OK,чтобы закрытьокно редактораформул. С помощьюИнспектораобъектов установитев свойствоAutoSize компонентаQRExpr1 значениеFalse, изменитеразмеры компонентатак, чтобы онмог отображатьпримерно 10 символов,и установитевыравниваниевправо (свойствоAlignment = taRightJustify).Запуститережим предварительногопросмотрасодержимогоотчета (рис.11).Как видим, датаотпуска товараприобрела болеепривычный вид.


Рис.11. Результатвычислениявыраженияпоявился вотчете.


Замечание.

Другим способомсоставлениязначения датыиз трех полеймогло бы бытьсоздание вычисляемогополя (например,SumData) иопределениеалгоритмавычисленияего значенияв таком обработчикесобытия OnCalcFields:


procedureTForm1.TableCalcFields(DataSet: TDataSet);

begin

Table1SumData.Value:= Table1DEN.AsString + ‘.’ +

Table1MES.AsString +‘.’ + Table1GOD.AsString;

end;


6.ИспользованиеTQRBand для представлениязаголовковстолбцов

КомпонентTQRBand, укоторого всвойство BandTypeустановленозначениеrbColumnHeader, используетсядля размещениязаголовковстолбцов. Собственнозаголовкистолбцов формируютсяпри помощикомпонентовTQRLabel.

В рассмотренномв предыдущихразделах отчетеразместимкомпонентTQRBand (имяQRBand3) иустановим всвойства Captionэтих компонентовсоответственнозначения №№,Товар, Количество,Дата. В свойствахFont компонентоввыберем наклонныйи подчеркнутыйшрифт. Вызовемокно предварительногопросмотраотчета – длякаждой страницыотчета теперьбудут выводитьсяназвания столбцов(рис.12).


Рис.12.В отчете появилисьзаголовкистолбцов.


7.ИспользованиеTQRBand для показазаголовка иподвала страницы.

КомпонентTQRBand, у которогов свойствоBandType установленозначениеrbPageHeader, используетсядля показазаголовкастраницы, аесли это свойствоустановленов rbPageFooter, – для показаподвала страницы.Заголовоквыводится вначале каждойстраницы, аподвал – в ееконце. Информацияв заголовкеи подвале страницыможет формироватьсяна основестатическоготекста (компонентыTQRLabel), значенийполей (компонентыTQRDBText) и результатоввычисленийвыражений(компонентыTQRExpr).

Вернувшиськ предыдущемупримеру, разместимв отчете компонентTQRBand (имя QRBand4) иустановим вего свойствоBandType значениеrbPageHeader. Не будемразмещать взаголовкеникакого текста,просто отчеркнемлинию вверхустраницы. Дляэтого установимв свойствокомпонентастраницыFrame.DrawTop значениеTrue, что обеспечиваетвывод линиипо верхнемукраю области,занимаемойкомпонентом.Аналогичнымобразом определимв отчете компонентподвала страницы(имя QRBand5)и установимв его свойствоFrame.DrawBottom значениеTrue, чтообеспечиваетвывод линиипо нижнему краюобласти, занимаемойкомпонентом.

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


8.ИспользованиекомпонентаTQRSysData

КомпонентTQRSysDataиспользуетсядля показавспомогательнойи системнойинформации.Вид показываемойинформацииопределяетсясвойством


propertyData: TQRSysDataType;


Нижеуказаны возможныезначения этогосвойства.

Значение

Чтовыводится

qrsColumnNo

Номертекущей колонкиотчета (дляодноколоночногоотчета всегда1).

qrsDate

Текущаядата.

qrsDateTime

Текущиедата и время.

qrsDetailCount

Количествозаписей в НД,а при использованиинесколькихНД – количествозаписей в главномнаборе. Дляслучая, когдаНД представленкомпонентомTQuery,эта возможностьможет бытьнедоступной,что связанос характеромработы компонентаTQuery,который возвращаетстолько записей,сколько необходимодля использованияв текущий момент,а остальныепредоставляетпо мере надобности.

qrsDetailNo

Номертекущей записив НД.

qrsPageNumber

Номертекущей страницыотчета.

qrsPageCount

Общееколичествостраниц отчета.

qrsReportTitle

Заголовокотчета.

qrsTime

Текущеевремя

Разместим вкомпонентеQRBand5 подвалаотчета двакомпонентаTQRSysData. Всвойство Dataпервого изних установимзначение qrsDate,второго –qrsPageNumber. Врежиме предварительногопросмотраувидим, чтотеперь в подвалестраницы выводятсяномер страницыи текущая дата(рис.13)


Рис.13. Показ номерастраницы итекущей датыв подвале страницы.


9.Группировкиданных

Для группировокинформациииспользуетсякомпонентTQRGroup. Егосвойство Expressionуказываетнекотороевыражение,которое используетсядля группировки,иными словами,в группу входятзаписи, удовлетворяющиеусловию этоговыражения. Присмене выраженияпроисходитсмена группы.

Для каждойгруппы выводятсяее заголовоки подвал. В качествезаголовкагруппы используетсякомпонентTQRBand со значениемсвойства BandType,равным rbColumnHeader,а в качествеподвала – созначениемrbGroupFooter. СвойствоFooterBandкомпонентаTQRGroup должносодержатьссылку на компонентподвала группы.В заголовкегруппы, какправило, выводитсягруппирующеевыражение, ав подвале группы– агрегированнаяинформация:суммарные,средние и т.п.значения погруппе в целом.


Пример.

Построим отчето расходе товарасо склада, вкотором информациягруппируетсяпо наименованиютовара. Дляэтого определимнабор данныхотчета (компонентTTable, имяTable1).Установим уНД текущиминдекс по полюTOVAR (всвойствеFieldIndexNamesили IndexName).Разместимв отчете:


  • заголовокотчета – компонентTQRBand сименем QRBand1,свойствоBandType=rbTitle;

  • заголовокстолбцов –компонентTQRBand сименем QRBand2,свойствоBandType=rbColumnHeader;

  • группу– компонентTQRGroup сименем QRGroup1;

  • областьдетальнойинформации– TQRBandс именем QRBand3,свойствоBandType=rbDetail;

  • подвалгруппы – TQRBandс именем QRBand4,свойствоBandType=rbGroupFooter;

В компонентеQRGroup1установим:

  • в свойствоFooterBandзначениеQRBand4;

  • всвойство ExpressionзначениеTable1.TOVAR,которое являетсяформулой истроится вредактореформул.


Посколькусвойство Expressionне визуализируетзначения выражения,необходиморазместитьв группе компонентTQRExpr (имя QRExpr1) иопределитьзначение егосвойства Expressionтак, чтобы оносодержалоTable1.TOVAR.

В компонентеподвала группыQRBand4 будемподсчитыватьсумму по полюKOLVO (суммуотпущенногоконкретноготовара). Дляэтого разместимв подвале группыкомпонентTQRExpr (имяQRExpr2) и определитьзначение егосвойства Expressionтак, чтобы оносодержалоформулу SUM(Table1.TOVAR).

В группе детальнойинформацииразместимкомпонентыTQRDBText,связанныес полями Pokupи Kolvo.Заполним областиотчета статическимтекстом, какэто показанона рис.14.


Рис.14. Макет отчетас группировкойпо товару.


Рис.15. Отчет с группировкойпо товару вокне предварительногопрсмотра.


10.Множественнаягруппировкаданных

Часто внутригруппы должнысодержатьсядругие группы,например, поназванию товараи внутри каждойгруппы – попокупателям.В этом случаевнутри однойгруппы определяютдругую посредствомдополнительныхкомпонентовTQRGroup.

Пусть требуетсяпредставитьв отчете сведенияо расходе товаровсо склада группируяданные по товарам,а внутри группы– по покупателям.Установимтекущий индекспо полям TOVAR,POKUP.Общий вид отчетана этапе разработкиприводитсяна рис.17, а в окнепредварительногопросмотра –на рис.18.


Рис.17.Макет отчетас вложеннымигруппами.


Рис. 18.Отчет с вложеннымигруппами.


11.Построениеотчета главный-детальный

Еслинеобходимопостроить отчетна основе болеечем одной ТБД,можно поступитьдвумя способами:

  1. с помощьюкомпонентаTQuery произвестисоединениеданных из несколькихтаблиц БД водин НД, послечего определитьв отчете нужныегруппировки;

  2. создатьв приложениипо одному НДна каждую таблицу,соединить этинаборы междусобой связьюглавный-детальный(используясвойстваMasterSource,MasterFieldsнабора данных)и применитьв отчете компонент(или несколькокомпонентов)TQRSubDetail длявывода информациииз детальногоНД (или группыдетальных НД);для выводаинформациииз главногоНД, как и в обычныхотчетах, применяетсякомпонентTQRBand, укоторого всвойстве BandTypeустановленозначение rbDetail.

Построениеотчета дляпервого случаяосуществляетсяаналогичнотому, как этоописано выше.Построениеотчета длявторого случаяимеет некоторыеотличительныеособенности.Рассмотримвторой способ.

КомпонентTQRSubDetailпредназначендля показа вотчете информациииз детальногоНД. Его свойство


PropertyDataSet: 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).Разместим такжееще один компонентTTable (им RashodTable), ассоциированныйс ТБД RASHOD.DB,и установиммежду НД связьглавный-детальный.Для этого установимв свойствоRashodTable.MasterSource значениеDS_TovaryTable, а в свойствоRashodTable.MasterFields значениеTOVAR (рис.19).


Рис.19. Установкасвязи главный-детальный.


Заметим, чтопосле установлениясвязей НД и НДRashodTable текущиминдексом долженбыть индекспо полю Tovar(свойствоRashodTable.IndexFieldNames).

Приступим кразработкеотчета. Определимзаголовокотчета – компонентTQRBand с именемQRBand1, в свойствоBandType которогоустановленозначение rbTitle.Установимв качествеосновного НДотчета TovaryTable,указав QuickRep1.DataSet= TovaryTable.Разместимв отчете компонентTQRBand сименем QRBand2и установимв его свойствоBandType значениеrbDetail. Этоткомпонент будетиспользоватьсядля отображениядетальнойинформациииз НД TovaryTable.

Разместим вотчете компонентTQRSubTetail (имя QRSubDetail).Установим вего свойствоDataSet значениеRashodTable, связав такимобразом данныйкомпонент сподчиненнымНД. Разместимв области компонентаQRSubDetail три компонентаTQRDBText и свяжем ихсоответственнос полями Pokup,Kolvo и D НД RashodTable (полеD определенов НД RashodTable каквычисляемоепо значениямполей DEN, MES, GOD).Разместим вобласти компонентаQRBand2 заголовкистолбцов.

Вид формы отчетапоказан нарис.20.


Рис.20. Макет отчета,в которомпоказываютсязаписи из связанныхнаборов данных.


В результирующемотчете (рис.21)для каждойзаписи НД TovaryTableвыводятсяподчиненныеей записи изНД RashodTable.


Рис.21. Отчет, в которомпоказываютсязаписи из связанныхнаборов данных.


Замечание.

Если необходимоопределитьзаголовок иподвал дляинформации,группируемойв компонентеTQRSubDetail, следуетвоспользоватьсясвойством


propertyBands: TQRSubDetailGroupBands;


этогокомпонента,которое имеетдва логическихподсвойства(HasHeader иHasFooter),указывающихна наличие илиотсутствиесоответственнозаголовка иподвала.


12.Построениекомпозитногоотчета

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

Композитныйотчет реализуетсяпри помощикомпонентаTQRCompositeReport. В егообработчикесобытия OnAddReportранее определенныепростые отчетыдобавляютсяв списковоесвойство Report.Например, так:


propertyTCompositnyjOtchet.QRCompositeReport1AddReports(Sender: TObject);

begin

withQRCompositeReport1 do

begin

Reports.Add(ManyGroup.QuickRep1);

Reports.Add(Prostoj.QuickRep1);

end

end;


В этом примерекомпозитныйотчет составляетсяиз двух отчетов:QuickRep1(определенныйв форме ManyGroupQuickRep1(определенныйв форме Prostoj).Почать композитногоотчета или егопредварительныйпросмотросуществляетсятак же, как дляпростых отчетов,например


QRCompositeReport1.Preview;


Нарис.22 показанкомпозитныйотчет, построенныйиз двух ранееразработанныхнами отчетов– простейшегоотчета и отчетас группировкамиданных.


Рис.22. Композитныйотчет, составленныйиз двух простыхотчетов.


У


рок11: Создание отчетов

Разработкабаз данных вDelphi


Вводный

Вводныйурок

Вводныйурок.doc

Урок01

НастройкаBDE

Урок01.doc

Урок02

Созданиетаблиц спомощью
DatabaseDesktop

Урок02.doc

Урок03

Созданиетаблиц спомощью
SQL-запросов

Урок03.doc

Урок04

Обзорвизуальныхкомпонент.
Компонентыработы с базамиданных

Урок04.doc

Урок05

КомпонентTtable. Созданиетаблиц с помощьюкомпонентаTTable

Урок05.doc

Урок06

КомпонентTQuery

Урок06.doc

Урок07

РедакторDataSet, вычисляемыеполя

Урок07.doc

Урок08

КомпонентTDatabase

Урок08.doc

Урок09

Управлениетранзакциями

Урок09.doc

Урок10

Основыязыка SQL

Урок10.doc

Урок11

Генерацияотчетов

Урок11.doc