В большинстве случаев, база данных и таблицы в ней проектируются и создаются заранее, например, такими утилитами, как Database Desktop, или СУБД MS Access. Однако иногда приходится создавать таблицы программно, то есть, не во время проектирования приложения, а во время его работы. Предположим, каждый пользователь программы должен иметь свою собственную таблицу в базе данных, или даже свою собственную БД. Сделать это заранее программист не может, ведь неизвестно, сколько пользователей на одном ПК будут работать с программой, и какие у них будут имена.
1. BDE. Простая таблица.
Наиболее простой способ создания таблицы без индексов предлагает механизм доступа к данным BDE. Плюсы данного способа в простоте выполнения, в возможности создания таблиц как текстового типа, так и dBase, Paradox или FoxPro. Суть данного способа заключается в предварительном создании объектов-полей в редакторе полей компонента TTable. Это также означает, что еще на этапе проектирования можно настроить формат объектов-полей по своему усмотрению.
Рассмотрим этот способ на примере.
Создайте новое приложение.
Форму назовите fMain, сохраните модуль под именем Main, а проект в целом назовите как угодно. На форму установите простую панель, очистите ее свойство Caption, а свойству Align присвойте значение alTop.
В левой части панели установите рядом две простые кнопки, а в правой - компонент TDBNavigator с вкладки Data Controls Палитры компонентов.
Ниже панели установите сетку TDBGrid, в ее свойстве Align выберите значение alClient.
У кнопок измените свойство Caption: на первой кнопке напишите "Создать таблицу", на второй - "Открыть таблицу".
Также нам потребуется еще четыре не визуальных компонента. Прямо на сетку, или в любое другое место установите компонент TTable с вкладки BDE, компонент TDataSource с вкладки Data Access, и компоненты TSaveDialog и TOpenDialog с вкладки Dialogs.
Подготовим диалоговые компоненты. Выделите их и присвойте свойству Filter обоих компонентов строку Таблицы dBase|*.dbf
Таким образом, мы указали, что диалоги будут работать только с таблицами типа dBase. Кроме того, у обоих диалогов измените свойство DefaultExt, указав там: dbf
Это свойство указывает расширение файла по умолчаниию, если пользователь не назначит расширения сам.
В свойстве DataSet компонента DataSource1 выберите таблицу Table1.
В свойстве DataSource сетки DBGrid1 и навигатора DBNavigator1 выберите имеющийся DataSource1. Теперь при открытии таблицы она будет отображаться в сетке, а навигатор позволит управлять ей.
Теперь настраиваем компонент Table1. Табличный компонент TTable имеет свойство TableType; компонент TADOTable такого свойства не имеет. Это свойство указывает на тип используемой или создаваемой таблицы. Свойство может иметь следующие значения:
Значения свойства TableType компонента TTable | |
Значение | Описание |
ttASCI | Таблица содержится в формате обычного текстового файла. Строки и поля разделяются специальными символами - разделителями. Имя файла таблицы имеет расширение *.TXT |
ttDBase | Таблица содержится в формате dBase, файл по умолчанию имеет расширение *.DBF |
ttDefault | Компонент определяет тип таблицы по расширению имени файла таблицы. При создании таблицы, если не указано расширение имени файла, принимается тип Paradox. |
ttFoxPro | Таблица содержится в формате FoxPro, файл по умолчанию также имеет расширение *.DBF |
ttParadox | Таблица содержится в формате Paradox, файл по умолчанию имеет расширение *.DB |
Если выбран тип таблицы (не ttDefault), то будет использован этот тип вне зависимости от расширения указанного имени файла таблицы.
В свойстве TableType компонента Table1 выберите значение ttDBase, то есть, таблица будет работать только с типом dBase. Далее дважды щелкните по компоненту, открыв редактор полей.
Нам нужно будет добавить запланированные ранее поля. Щелкните по редактору правой кнопкой, выберите команду New Field (Новое поле).
В поле Name впишите имя поля, например, FCeloe.
В поле Type выберите тип поля Integer.
В поле Size нужно указывать размер поля, но это справедливо только для текстовых полей и полей типов Memo или BLOB
Убедитесь, что переключатель Field Type установлен на Data, это создаст пустое поле указанного типа. Нажав кнопку "ОК" добавьте объект-поле в редактор полей.
Таким же образом создайте еще несколько разнотипных полей. Каждому полю присвойте уникальное имя. Важно, чтобы вы добавляли только те типы полей, которые поддерживаются выбранным типом таблиц, в нашем случае это dBase. При добавлении типа Memo укажите размер от 1 до 255, например, 50. В этом случае в файле таблицы *.dbf будет сохранен текст поля в 50 символов. Текст, который не уместится в этот размер, будет сохранен в файле Memo с таким же именем, но с расширением *.dbt.
Делать табличный компонент активным на этапе проектирования не нужно. Итак, не имея базы данных, не имея физической таблицы, мы заранее установили тип таблицы и нужные нам поля. Также возможно сразу настроить нужные форматы для каждого поля, изменяя такие его свойства, как DisplayFormat, EditMask, DisplayLabel и др.
Далее осталось непосредственно создать и открыть таблицу. Дважды щелкните по кнопке "Создать таблицу", сгенерировав для нее событие. В процедуру этого события впишите код:
//если пользователь не выбрал таблицу, выходим: if not SaveDialog1.Execute then Exit; //закроем таблицу, если вдруг уже есть открытая: Table1.Close; //вначале устанавливаем адрес базы данных: Table1.DatabaseName:= ExtractFilePath(SaveDialog1.FileName); //теперь устанавливаем имя таблицы: Table1.TableName:= SaveDialog1.FileName; //физически создаем таблицу: Table1.CreateTable; //и открываем ее: Table1.Open; //запишем имя открытой таблицы: fMain.Caption:= 'Таблица - '+ Table1.TableName;Метод CreateTable() компонента-таблицы создает файл таблицы, и дополнительные файлы (Memo, индексные), если они нужны.
В свойстве DatabaseName табличного компонента вы можете установить любой необходимый вам адрес, мы использовали папку, выбранную диалогом SaveDialog.
Для кнопки "Открыть таблицу" код будет почти таким же:
//если пользователь не выбрал таблицу, выходим: if not OpenDialog1.Execute then Exit; //закроем таблицу, если вдруг уже есть открытая: Table1.Close; //вначале устанавливаем адрес базы данных: Table1.DatabaseName:= ExtractFilePath(OpenDialog1.FileName); //теперь устанавливаем имя таблицы: Table1.TableName:= OpenDialog1.FileName; //открываем таблицу: Table1.Open; //запишем имя открытой таблицы: fMain.Caption:= 'Таблица - '+ Table1.TableName;BDE. Таблица с ключом и индексами.
В задачу данного раздела входит создание таблицы Paradox с различными типами полей, с первичным ключом и индексами по текстовому полю как в возрастающем, так и в убывающем порядке. Редактор полей компонента TTable при этом вызывать не нужно, добавлять поля мы тоже будем программно. Создайте главную форму такой же, как в предыдущем примере, только кнопка там будет одна. При нажатии на эту кнопку мы должны открыть таблицу, если она существует, или создать и открыть новую таблицу. Располагаться таблица должна в той же папке, откуда запущено приложение. Файл с таблицей Paradox назовем Proba.db, файлы с Memo и индексные файлы сгенерируются автоматически, также с именем Proba, но с разными расширениями.
На форму добавьте компонент TTable с вкладки BDE, свойству Name которого присвойте значение TMy (вместо Table1), а свойству TableType значение ttParadox. Если у вас в приложении есть сетка DBGrid и (или) навигатор DBNavigator, то добавьте также компонент DataSource, который необходимо подключить к таблице TMy, а сетку и навигатор - подключить к DataSource. Здесь следует иметь в виду одну деталь: описание методов создания полей и индексов хранится в модуле DBTables, который подключается к вашей форме сразу, как вы установите компонент TTable. Если же вы используете модуль данных, и устанавливаете табличный компонент там, то и создавать таблицу нужно тоже в этом модуле, а в главной форме лишь вызывать процедуру создания таблицы. Но в нашем простом примере модуля данных нет, модуль DBTables указан в разделе Uses главной формы, и никаких проблем возникнуть не должно.
Код нажатия на кнопку выглядит так:
{Если таблицы нет - создаем и открываем ее, если есть- просто открываем}procedure TfMain.Button1Click(Sender: TObject);begin //если таблица есть - открываем ее и выходим: if FileExists(ExtractFilePath(Application.ExeName) + 'Proba.db') then begin TMy.DatabaseName:= ExtractFilePath(Application.ExeName); TMy.TableName:= 'Proba.db'; TMy.Open; Exit; end; //if {Если дошли до этого кода, значит таблицы еще нет. Указываем данные таблицы:} TMy.DatabaseName:= ExtractFilePath(Application.ExeName); TMy.TableType:= ttParadox; TMy.TableName:= 'Proba'; {Создаем поля:} with TMy.FieldDefs do begin //вначале очистим: Clear; //добавляем поле-счетчик типа автоинкремент: with AddFieldDef do begin Name:= 'Key'; DataType:= ftAutoInc; Required:= True; end; //with //добавляем текстовое поле: with AddFieldDef do begin Name:= 'Name'; DataType:= ftString; Size:= 30; end; //with //добавляем поле дата: with AddFieldDef do begin Name:= 'Date'; DataType:= ftDate; end; //with //добавляем логическое поле: with AddFieldDef do begin Name:= 'MyLog'; DataType:= ftBoolean; end; //with //добавляем целое поле: with AddFieldDef do begin Name:= 'MyInt'; DataType:= ftInteger; end; //with //добавляем вещественное поле: with AddFieldDef do begin Name:= 'MyReal'; DataType:= ftFloat; end; //with //добавляем денежное поле: with AddFieldDef do begin Name:= 'MyCurr'; DataType:= ftCurrency; end; //with //добавляем поле Memo: with AddFieldDef do begin Name:= 'MyMemo'; DataType:= ftMemo; Size:= 20; end; //with end; //with {Создаем ключ и индексы:} with TMy.IndexDefs do begin Clear; //делаем первичный ключ: with AddIndexDef do begin Name:= ''; Fields:= 'Key'; Options:= [ixPrimary]; end; //делаем индекс в возрастающем порядке: with AddIndexDef do begin Name:= 'NameIndxASC'; Fields:= 'Name'; Options:= [ixCaseInsensitive]; end; //делаем индекс в убывающем порядке: with AddIndexDef do begin Name:= 'NameIndxDESC'; Fields:= 'Name'; Options:= [ixCaseInsensitive, ixDescending]; end; end; //with //создаем таблицу: TMy.CreateTable; //и открываем ее: TMy.Open;end;Разберем приведенный код. Первый блок выполняет проверку на наличие таблицы. Таблица ищется в папке, откуда была запущена программа. Если таблица найдена, то компоненту TMy присваиваются свойства DatabaseName (папка, где располагается таблица) и TableName (имя таблицы). В нашем случае таблица называется Proba.db, но вы можете усложнить программу, используя диалог OpenDialog, как в прошлом примере. В этом случае пользователь сможет выбрать не только имя таблицы, но и ее расположение. Далее таблица открывается, а оператор Exit досрочно завершает выполнение процедуры.
Если выполнение процедуры продолжается, значит, таблица не была найдена. В этом случае мы заполняем свойства компонента-таблицы DatabaseName, TableType и TableName необходимыми значениями.
Далее начинаем добавлять поля. Чтобы уменьшить код, мы используем оператор with. Напомню, что этот оператор создает блок кода, который относится к указанному в with объекту. Так, вместо
with TMy.FieldDefs do begin Clear;можно было бы написать
TMy.FieldDefs.Clear;В случае одиночного оператора это допустимо, но в случае множественных команд ссылаться в каждой строчке на объект будет утомительно. Свойство FieldDefs таблицы содержит описание полей этого набора данных. Таким образом, мы начинаем с того, что очищаем это описание.
Далее у нас идет метод AddFieldDef, предназначенный для добавления поля в описание. Опять же, чтобы не ссылаться каждый раз на этот метод, мы используем вложенный оператор with для каждого добавляемого поля.
В простейшем случае в блоке добавления нового поля требуется указать только два свойства объекта-поля: Name (имя поля) и DataType (тип поля). С именем все понятно, а что касается типа поля, то он определяется свойством DataType класса TField. Чтобы получить подробную справку по возможным типам полей, установите курсор в редакторе кода на слове DataType и нажмите <Ctrl+F1>, чтобы вызвать контекстную справку. В списке тем выберите ту тему, которая относится к классу TField, а в открывшейся справке щелкните по ссылке TFieldType (относится к Delphi 7, хотя возможно, имеется и в предыдущих версиях). Откроется страница с подробным описанием типов полей. При использовании этого метода следует сверяться, имеется ли выбранный тип поля в таблицах используемого формата.
Помимо этих двух свойств, при необходимости могут использоваться и другие:
Required - Логическое свойство. Если равно True, то значения поля должны быть уникальными (не могут повторяться). В нашем примере такое свойство имеется у поля, которое мы будем использовать как первичный ключ.
Size - Указывает размер поля. Используется в основном, со строковыми и Memo - полями.
После того, как в список полей были добавлены все необходимые поля, начинаем создание первичного ключа и индексов. Если за список полей отвечает свойство FieldDefs таблицы, то за список индексов отвечает свойство IndexDefs, а за добавление нового индекса - метод AddIndexDef. По аналогии с полями, используем оператор with для уменьшения кода. Для каждого индекса требуется указать по три свойства: Name (имя индекса), Fields (имя поля, по которому строится индекс) и Options (параметры индекса). Параметры индекса указаны в таблице:
Параметры типов индекса | |
Тип | Описание |
ixPrimary | Первичный индекс (ключ). Не применяется с таблицами типа dBase. |
ixUnique | Уникальный индекс. Значения этого поля не могут повторяться. |
ixDescending | Индекс в убывающем (обратном) порядке. |
ixExpression | Ключевой индекс для таблиц dBase. |
ixCaseInsensitive | Индекс, нечувствительный к регистру букв. |
ixNonMaintained | Этот тип используется редко. Он подразумевает, что при редактировании пользователем значения индексируемого поля, индексный файл автоматически не обновляется. |
Как видно из примера, свойству Options можно присвоить не один параметр, а список параметров:
Options:= [ixCaseInsensitive, ixDescending];Далее все просто: указав необходимые поля и индексы, методом CreateTable формируются физические файлы таблицы. Сама таблица имеет расширение *.db, файл с полем Memo - *.mb, остальные файлы содержат созданные индексы.
Для сортировки данных используем индексы. У нас их два -' NameIndxASC ' (в возрастающем порядке) и ' NameIndxDESC ' (в убывающем порядке). Чтобы сортировать данные, например, в убывающем порядке, нужно указать имя соответствующего индекса в свойстве IndexName компонента-таблицы:
TMy.IndexName:= 'NameIndxDESC';Если же мы хотим снять сортировку, то достаточно просто присвоить этому свойству пустую строку: TMy.IndexName:= '';