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

         

 Поля


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

property Fields[Index: Integer];

function FieldByName(const FieldName: string): TField;

property FieldCount;

          Свойство 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 метод который выглядит так:

procedure TForm1.FieldsClick(Sender: TObject);

var

  i: Integer;

begin

  ListBox1.Clear;

  for i := 0 to Table1.FieldCount - 1 do

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

end;

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

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

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

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

procedure TForm1.ValuesClick(Sender: TObject);

var

  i: Integer;

begin

  ListBox2.Clear;

  for i := 0 to Table1.FieldCount - 1 do

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

end;

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

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



property AsBoolean

property AsFloat

property AsInteger

property AsString

property AsDateTime

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

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

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

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


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


В предложении FROM перечисляются все объекты (один или несколько), из которых производится выборка данных (рис.2). Каждая таблица или представление, о которых упоминается в запросе, должны быть перечислены в предложении FROM.



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


В SQL имеется возможность преобразовать значение столбца или функции к другому типу для более гибкого использования операций сравнения. Для этого используется функция CAST.

Типы данных могут быть конвертированы в соответствии со следующей таблицей:

Из типа данных     В тип данных

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

NUMERIC            CHAR, VARCHAR, DATE

CHAR, VARCHAR      NUMERIC, DATE

DATE               CHAR, VARCHAR, DATE

SELECT first_name, last_name, dept_no

FROM employee

WHERE CAST(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    

Mary S.         MacDonald            100    

Oliver H.       Bender               000    

Kelly           Brown                600    

Michael         Yanowski             100



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


Итак, начнем с рассмотрения простейших конструкций языка SQL. После такого рассмотрения мы научимся:

* назначать поля, которые должны быть выбраны

*                                                                                 назначать к выборке “все поля”

*                                                                                 управлять “вертикальным” и “горизонтальным” порядком выбираемых полей

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

*                                                                                 производить вычисления в списке выбираемых элементов


*                                                                                 использовать литералы в списке выбираемых элементов

*                                                                                 ограничивать число возвращаемых строк

*                                                                                 формировать сложные условия поиска, используя реляционные и логические операторы

*                                                                                 устранять одинаковые строки из результата.

Список выбираемых элементов может содержать следующее:

Þ     имена полей

Þ     *

Þ     вычисления

Þ     литералы

Þ     функции

Þ     агрегирующие конструкции


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


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

procedure Append;

procedure Insert;

procedure Cancel;

procedure Delete;

procedure Edit;

procedure Post;

Все эти методы - часть 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 на индексированной таблице. Фактически, всегда можно использовать и Append, и Insert.

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



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

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

procedure TForm1.InsertClick(Sender: TObject);

begin

  Table1.Insert;

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

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

  Table1.Post;

end;

procedure TForm1.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.

         


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


Мы уже рассказывали о типах данных, имеющихся в различных СУБД, в том числе и в 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”    завтра

Дата может неявно конвертироваться в строку (из строки), если:

-     строка, представляющая дату, имеет один из вышеперечисленных форматов;

-     выражение не содержит неоднозначностей в толковании типов столбцов.

SELECT first_name, last_name, hire_date

FROM employee

WHERE hire_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

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

SELECT first_name, last_name, hire_date

FROM employee

WHERE 'today' - hire_date > 365 * 7 + 1
                          получить список служащих,
                                                                       проработавших на предприятии
                                                                       к настоящему времени
                                                                       более 7 лет

FIRST_NAME      LAST_NAME              HIRE_DATE

=============== ==================== ===========

Robert          Nelson               28-DEC-1988

Bruce           Young                28-DEC-1988



 Редактор 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. Вы можете получить доступ к нему так:

procedure TForm1.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".


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


Наиболее важной командой языка манипулирования данными является команда 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   

Hong Kong       HKDollar  

Netherlands     Guilder   

Belgium         BFranc    

Austria         Schilling 

Fiji            FDollar

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

SELECT * FROM country

WHERE currency = “Dollar”  Получить подмножество строк таблицы Country,                                                                             удовлетворяющее             условию Currency = “Dollar”

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



COUNTRY         CURRENCY  

=============== ==========

USA             Dollar

Операция проекции позволяет выделить подмножество столбцов таблицы. Например:

SELECT currency FROM country    Получить список
                                                                                       денежных единиц

CURRENCY  

==========

Dollar    

Pound     

CdnDlr    

SFranc    

Yen       

Lira      

FFranc    

D-Mark    

ADollar   

HKDollar  

Guilder   

BFranc    

Schilling 

FDollar

На практике очень часто требуется получить некое подмножество столбцов и строк таблицы, т.е. выполнить комбинацию Restriction и Projection. Для этого достаточно перечислить столбцы таблицы и наложить ограничения на строки.

SELECT currency FROM country  

WHERE country = “Japan”        Найти денежную
                                                                                     единицу Японии

CURRENCY  

==========

Yen

SELECT first_name, last_name

FROM employee

WHERE first_name = "Roger"      Получить фамилии
                                                                              работников,
                                                                              которых зовут “Roger”

FIRST_NAME      LAST_NAME           

=============== ====================

Roger           De Souza            

Roger           Reeves

Эти примеры иллюстрируют общую форму команды SELECT в языке SQL (для одной таблицы):

SELECT  (выбрать) специфицированные поля

FROM    (из) специфицированной таблицы

WHERE   (где) некоторое специфицированное условие является                                                 истинным

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

SELECT first_name, last_name, proj_name

FROM employee, project

WHERE emp_no = team_leader  Получить список


                                                                              руководителей проектов

FIRST_NAME     LAST_NAME         PROJ_NAME           

============== ================= ====================

Ashok          Ramanathan        Video Database      

Pete           Fisher            DigiPizza           

Chris          Papadopoulos      AutoMap             

Bruce          Young             MapBrowser port     

Mary S.        MacDonald         Marketing project 3

Операция объединения позволяет объединять результаты отдельных запросов по нескольким таблицам в единую результирующую таблицу. Таким образом, предложение UNION объединяет вывод двух или более SQL-запросов в единый набор строк и столбцов.

SELECT first_name, last_name, job_country

FROM employee

WHERE job_country = "France"

UNION

SELECT contact_first, contact_last, country

FROM customer

WHERE country = "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 one.last_name, two.last_name, 

       one.hire_date

FROM employee one, employee two

WHERE one.hire_date = two.hire_date

  AND one.emp_no < two.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

SELECT d1.department, d2.department, d1.budget

FROM department d1, department d2

WHERE d1.budget = d2.budget

  AND d1.dept_no < d2.dept_no
                                                             получить список пар отделов с
                                                             одинаковыми годовыми бюджетами

DEPARTMENT                DEPARTMENT                   BUDGET

========================  ========================= =========

Software Development      Finance                   400000.00

Field Office: East Coast  Field Office: Canada      500000.00

Field Office: Japan       Field Office: East Coast  500000.00

Field Office: Japan       Field Office: Canada      500000.00

Field Office: Japan       Field Office: Switzerland 500000.00

Field Office: Singapore   Quality Assurance         300000.00

Field Office: Switzerland Field Office: East Coast  500000.00



 Системная информация утилиты настройки 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 на другие компьютеры).



 События


Событие

Назначение

property AfterPreview : TQRAfterPreviewPrint;

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

property AfterPrint: TQRAfterPrintEvent;

Наступает после печати отчета или его подготовки к печати.

property BeforePrint: TQRBeforePrintEvent;

Наступает в момент начала генерации отчета (до выдачи окна предварительного просмотра отчета или до его печати).

property OnEndPage: procedure (Sender: TObject);

Возникает в момент подготовки к генерации последней страницы отчета.

property OnNeedData: procedure (Sender: TObject; var MoreData: boolean);

Используется при создании отчета по данным, которые берутся не из НД, а из текстового файла, списка строк, массива  и т.п. В параметре MoreData обработчик должен вернуть True, если источник данных еще не исчерпан.

property OnPreview: procedure (Sender: TObject);

Используется для связывания с отчетом нестандартного окна просмотра (см. ниже).

property OnStartPage: procedure (Sender: TObject);

Возникает в момент подготовки к генерации первой страницы отчета.

С помощью компонента QRPreview программист может создать нестандартное окно предварительного просмотра. Для связи с отчетом используется событие OnPreview по следующей схеме:

Procedure RepForm.MyREportOnPreviewEvent(Sender: TObject);

begin

  MyPrevForm.QRPreview1.QRPrinter := TQRPrinter(Sender);

  MyPreviewForm.Show;

end;

Чтобы явное приведение типа TQRPrinter(Sender) стало возможным, необходима ссылка на модуль QRPrntr в предложении Uses соответствующего модуля (в примере – модуля RepForm).



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


Класс TDataSet.......................................................................................................................................................................... 2

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

Поля............................................................................................................................................................................................ 10

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

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

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

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

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

Создание Связанных Курсоров (Linked cursors)............................................................................................................ 24

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

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

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



Обзор

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

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

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

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

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

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

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

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

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


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


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

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

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

Свойство SQL............................................................................................................................................................................ 4

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

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

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

Open или ExecSQL?................................................................................................................................................................ 14

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




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


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

Обзор............................................................................................................................................... 2

Редактор DataSet........................................................................................................................... 2

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

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



 Соединение (JOIN)


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

После изучения этого раздела мы будем способны:

·     соединять данные из нескольких таблиц в единую результирующую таблицу;

·     задавать имена столбцов двумя способами;

·     записывать внешние соединения;

·     создавать соединения таблицы с собой.

Операции соединения подразделяются на два вида - внутренние и внешние. Оба вида соединений задаются в предложении WHERE запроса SELECT с помощью специального условия соединения. Внешние соединения (о которых мы поговорим позднее) поддерживаются стандартом ANSI-92 и содержат зарезервированное слово “JOIN”, в то время как внутренние соединения (или просто соединения) могут задаваться как без использования такого слова (в стандарте ANSI-89), так и с использованием слова “JOIN” (в стандарте ANSI-92).

Связывание производится, как правило, по первичному ключу одной таблицы и внешнему ключу другой таблицы - для каждой пары таблиц. При этом очень важно учитывать все поля внешнего ключа, иначе результат будет искажен. Соединяемые поля могут (но не обязаны!) присутствовать в списке выбираемых элементов. Предложение WHERE может содержать множественные условия соединений. Условие соединения может также комбинироваться с другими предикатами в предложении WHERE.



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


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

Поэтому, в язык SQL в качестве составных частей входят:

* язык манипулирования данными (Data Manipulation Language, DML)

*     язык определения данных (Data Definition Language, DDL)

*     язык управления данными (Data Control Language, DCL).

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

Язык манипулирования данными используется, как это следует из его названия, для манипулирования данными в таблицах баз данных. Он состоит из 4 основных команд:

SELECT   (выбрать)

INSERT   (вставить)

UPDATE   (обновить)

DELETE   (удалить).

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

CREATE DATABASE (создать базу данных)

CREATE TABLE     (создать таблицу)

CREATE VIEW      (создать виртуальную таблицу)

CREATE INDEX     (создать индекс)

CREATE TRIGGER   (создать триггер)

CREATE PROCEDURE (создать сохраненную процедуру)

ALTER DATABASE   (модифицировать базу данных)

ALTER TABLE      (модифицировать таблицу)

ALTER VIEW       (модифицировать виртуальную таблицу)

ALTER INDEX      (модифицировать индекс)

ALTER TRIGGER    (модифицировать триггер)

ALTER PROCEDURE  (модифицировать сохраненную процедуру)

DROP DATABASE    (удалить базу данных)

DROP TABLE       (удалить таблицу)

DROP VIEW        (удалить виртуальную таблицу)

DROP INDEX       (удалить индекс)

DROP TRIGGER     (удалить триггер)

DROP PROCEDURE   (удалить сохраненную процедуру).

Язык управления данными используется для управления правами доступа к данным и выполнением процедур в многопользовательской среде. Более точно его можно назвать “язык управления доступом”. Он состоит из двух основных команд:


GRANT   (дать права)

REVOKE (забрать права).

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

*                     интерактивный SQL

*                     встроенный SQL.

Интерактивный SQL используется в специальных утилитах (типа WISQL или DBD), позволяющих в интерактивном режиме вводить запросы с использованием команд SQL, посылать их для выполнения на сервер и получать результаты в предназначенном для этого окне. Встроенный SQL используется в прикладных программах, позволяя им посылать запросы к серверу и обрабатывать полученные результаты, в том числе комбинируя set-ориентированный и record-ориентированный подходы.

Мы не будем приводить точный синтаксис команд SQL, вместо этого мы рассмотрим их на многочисленных примерах, что намного более важно для понимания SQL, чем точный синтаксис, который можно посмотреть в документации на Вашу СУБД.

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


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


          Связанные курсоры позволяют программистам определить отношение один ко многим (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.


 Создание таблиц с помощью компонента 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:

property Items[Index: Integer]: TFieldDef;

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

procedure Add(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:

property Items[Index: Integer]: TIndexDef;

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

procedure Add(const Name, Fields: string;
                                Options: TIndexOptions);

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

TIndexOptions = set of (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 FieldDefs do

    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 IndexDefs do

    begin

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

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

    end;

    CreateTable;

  end;



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

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

procedure AddIndex(const Name, Fields: string;
                                Options: TIndexOptions);

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


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


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

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

CREATE TABLE table

 (<col_def> [, <col_def> | <tconstraint> ...]);

где

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

<col_def> - описание поля,

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

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

<col_def> = col {datatype | COMPUTED BY (<expr>) | domain}

   [DEFAULT {literal | NULL | USER}]

   [NOT NULL] [<col_constraint>]

   [COLLATE collation]

Здесь

col - имя поля;

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

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


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

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

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

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

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

<tconstraint> = [CONSTRAINT constraint <tconstraint_def>]

  <tconstraint>

  

Здесь

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

   | CHECK (<search_condition>)}

  

<search_condition> =

{<val> <operator> {<val> | (<select_one>)}

   | <val> [NOT] BETWEEN <val> AND <val>

   | <val> [NOT] LIKE <val> [ESCAPE <val>]

   | <val> [NOT] IN (<val> [, <val> ...] |

<val> = {

col [<array_dim>] | <constant> | <expr> | <function>

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

<constant> = num | "string" | charsetname "string"

<function> = {

COUNT (* | [ALL] <val> | DISTINCT <val>)

   | SUM ([ALL] <val> | DISTINCT <val>)

   | AVG ([ALL] <val> | DISTINCT <val>)

   | MAX ([ALL] <val> | DISTINCT <val>)

   | MIN ([ALL] <val> | DISTINCT <val>)

   | CAST (<val> AS <datatype>)

   | UPPER (<val>)

   | GEN_ID (generator, <val>)

   }

<operator> = {= | < | > | <= | >= | !< | !> | <> | !=}


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


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

property UniDirectional: Boolean;

property Handle: HDBICur;

property StmtHandle: HDBIStmt;

property DBHandle: 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;



 Список полей


SELECT first_name, last_name, phone_no

FROM phone_list      получить список
                                                         имен, фамилий и служебных телефонов
                                                         всех работников предприятия

FIRST_NAME    LAST_NAME            PHONE_NO            

============= ==================== ====================

Terri         Lee                  (408) 555-1234      

Oliver H.     Bender               (408) 555-1234      

Mary S.       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, к ней можно обращаться так же, как и к “настоящей” таблице.



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


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

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

 

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

 

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

 



 Сущность BDE


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

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



 Свойства


Свойство

Назначение

property Bands: TQuickRepBands;

Объект Bands содержит логические свойства, которые после установки в них значений True включают в отчет: HasColumnHeader – заголовки столбцов; HasDetail – детальную информацию; HasPageFooter – подвал страницы; HasPageHeader – заголовок страницы; HasSummary – подвал отчета; HasTitle – заголовок отчета.

property Dataset: TDataSet;

Указывает набор данных на основе которого создается отчет. Если нужно вывести связанную информацию из нескольких таблиц БД, ее объединяют в одном НД при помощи компонента TQuery. Информацию из нескольких связанных НД можно включать в отчет, если эти НД связаны в приложении отношением главный-подчиненный. В этом случае в качестве НД отчета указывается главный набор, а ссылка на соответствующие подчиненные наборы осуществляется в компонентах TQRSubDetail. Если в отчет нужно включить информацию из несвязанных НД, применяется композитный отчет, то есть отчет, составленный из группы других отчетов.

property Frame: TQRFrame;

Определяет параметры рамки отчета: Color – цвет линий; DrawBottom – наличие линии снизу; DrawLeft – наличие линии слева; DrawRight – наличие линии справа; DrawTop – наличие линии сверху; Style – стиль линии (сплошная, пунктирная и т.п.); Width – толщина линии в пикселях.

property Options: TQuickReportOptions;

Содержит множество из следующих логических значений: HasFirstHeader – разрешает печатать заголовок первой страницы; HasLastFooter – разрешает печатать подвал последней страницы; Compression – разрешает сжимать отчет при выводе его в метафайл.

property Page: TQRPage;

Определяет параметры страницы отчета. Все подсвойства этого сложного свойства доступны в окне Report Setting (см. ниже группы Page size и Margin окна редактора свойств).

property PrintIfEmpty: boolean;

Разрешает/запрещает печатать отчет в том случае если он не содержит данных.

property ReportTitle: String;

Имя отчета (не его заголовок !). Используется для идентификации отчета в задании на сетевую печать, возвращается компонентом QRSysData при Data = ReportTitle и может использоваться для набора одного из нескольких доступных отчетов.

property ShowProgress: boolean;

Разрешает/запрещает показывать индикатор процесса печати отчета.

property SnapToGrid: boolean;

Если содержит True, размещаемые в отчете компоненты привязываются к сетке отчета.

type  TQRUnits = (Inches, MM, Pixels, Native, Characters);

property Units: TQRUnits;

Определяет единицы измерения расстояний в отчете:    Inches – дюймы; MM – миллиметры; Pixels – пиксели; Native – внутренние единицы TQuickRep (равны 0,1 мм); Characters – символы текста.

property Zoom: Integer;

Определяет масштаб отображения отчета (в процентах от его размеров на листе бумаги) на этапе разработки. Может иметь значение в диапазоне 1..300. Значение свойства не учитывается при печати отчета или в режиме его предварительного показа.


Многие свойства отчета можно установить на этапе конструирования с помощью редактора свойств – вызовите локальное меню компонента TQuickRep и выберите опцию Report Settings.



Рис. 3. Окно установки параметров отчета.

Группа Paper size задает характеристики страницы: ее формат (A4 210 x 270 mm), ширину (Width), длину (Length) и направление печати – вдоль короткой стороны листа (Portait) или вдоль длинной (Landscape).

Группа элементов Margin указывает поля отчета: сверху (Top), снизу (Bottom), слева (Left), справа (Right), а также количество колонок (Number of columns) и расстояние между ними (Column Space).

С помощью элементов группы Other можно задать шрифт (Font), его высоту (Size) и используемые единицы измерения длины (Units).

Группа Page frame определяет свойства рамки: наличие линии сверху (Top), снизу (Bottom), слева (Left), справа (Right), цвет линий (Color) и их толщину (Width).

Группа Bands определяет наличие полос заголовков и подвалов (Page header – заголовок страницы; Title – заголовок отчета; Column header – заголовок колонок; Detail band – полоса для детальной информации; Page footer – подвал страницы; Summary – подвал отчета), а также высоту соответствующей полосы (строка Length справа от переключателя выбора). После выбора типа и высоты полосы она появляется в отчете, если окно закрыто кнопкой OK или была нажата кнопка Applay. Элементы Print first page header и Print last page footer управляют соответственно печатью заголовка на первой странице и подвала на его последней странице.


 Свойство SQL


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

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

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

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

Query1.Close;

Query1.SQL.Clear;

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

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

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

Query1.SQL.Add(‘where Name = ’’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:

procedure TForm1.LoadClick(Sender: TObject);

begin

  if OpenDialog1.Execute then

    with Query1 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. Вот ключевые свойства для достижения этих целей:

property Params[Index: Word];

function ParamByName(const Value: string);

property DataSource;

          Если подставлять значение параметра в параметризованный запрос через свойство 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, кроме того, здесь подготавливается запрос:

procedure TForm1.FormCreate(Sender: TObject);

var

  i : Byte;

begin

  Query1.Prepare;

  for i:=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. Параметр будет передаваться в момент переключения закладок:

procedure TForm1.TabSet1Change(Sender: TObject;

 NewTab: Integer;

 var AllowChange: Boolean);

begin

  with Query1 do begin

    Close;

    Params[0].AsString:=

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

    Open;

  end;

end;



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


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


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

Итак:

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

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

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

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

 



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


SELECT first_name, last_name, dept_no,

       job_code, salary

FROM employee

ORDER BY last_name        получить список сотрудников,
                                                                       упорядоченный по фамилиям
                                                                       в алфавитном порядке

FIRST_NAME   LAST_NAME     DEPT_NO JOB_CODE      SALARY

============ ============= ======= ======== ===========

Janet        Baldwin       110     Sales       61637.81

Oliver H.    Bender        000     CEO        212850.00

Ann          Bennet        120     Admin       22935.00

Dana         Bishop        621     Eng         62550.00

Kelly        Brown         600     Admin       27000.00

Jennifer M.  Burbank       622     Eng         53167.50

Kevin        Cook          670     Dir        111262.50

Roger        De Souza      623     Eng         69482.62

Roberto      Ferrari       125     SRep     99000000.00

...

SELECT first_name, last_name, dept_no,

       job_code, salary

FROM employee

ORDER BY last_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

...

Столбец, определяющий порядок вывода строк, не обязательно дожен присутствовать в списке выбираемых элементов (столбцов):

SELECT first_name, last_name, dept_no,

       job_code

FROM employee

ORDER BY salary           получить список сотрудников,
                                                                       упорядоченный по их зарплате

FIRST_NAME      LAST_NAME       DEPT_NO JOB_CODE

=============== =============== ======= ========

Ann             Bennet          120     Admin   

Kelly           Brown           600     Admin   

Sue Anne        O'Brien         670     Admin   

Mark            Guckenheimer    622     Eng     

Roger           Reeves          120     Sales   

Bill            Parker          623     Eng



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


SELECT first_name, last_name, dept_no,

       job_code, salary * 1.1

FROM employee

ORDER BY 5                получить список сотрудников,
                                                                       упорядоченный по их зарплате
                                                                       с 10% надбавкой

FIRST_NAME   LAST_NAME     DEPT_NO JOB_CODE            

============ ============= ======= ======== ===========

Ann          Bennet        120     Admin        25228.5

Kelly        Brown         600     Admin          29700

Sue Anne     O'Brien       670     Admin        34402.5

Mark         Guckenheimer  622     Eng            35200

Roger        Reeves        120     Sales     36982.6875

Bill         Parker        623     Eng            38500

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

SELECT first_name, last_name, dept_no,

       job_code, salary * 1.1

FROM employee

ORDER BY dept_no, 5 DESC, last_name
                          получить список сотрудников,
                                                                       упорядоченный сначала по
                                                                       номерам отделов,
                                                                       в отделах - по убыванию их
                                                                       зарплаты (с 10%),
                                                                       а в пределах одной зарплаты -        по фамилиям

FIRST_NAME  LAST_NAME  DEPT_NO JOB_CODE            

=========== ========== ======= ======== ===============

Oliver H.   Bender     000     CEO               234135

Terri       Lee        000     Admin            59172.3

Mary S.     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




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


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

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

dgEditing

Установлен по-умолчанию в true, позволяет пользователю редактировать grid. Вы можете также установить свойство ReadOnly grid в 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, тогда следующий код позволяют включать и выключать параметр одной кнопкой:

procedure TForm1.Button3Click(Sender: TObject);


begin

  if ShowTitles 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.



procedure TForm1.FormCreate(Sender: TObject);

var

  i : Word;

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

begin

  {Fill ListBox}

  ListBox1.Clear;

  for i:=0 to Table1.FieldCount-1 do

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

  {Make CheckBoxes}

  for i:=0 to Table1.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. Физически данные на  диске не изменяются.

procedure TForm1.downButtonClick(Sender: TObject);

var

  i : Integer;

begin

  with ListBox1 do

  if (ItemIndex<Items.Count-1)and(ItemIndex<>-1) then begin

    i := ItemIndex;

    {move ListBox item}

    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

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

 

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

 

Обзор.......................................................................................................................................................................... 2

Класс TDataBase...................................................................................................................................................... 2

Объект Session......................................................................................................................................................... 7

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

 


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


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

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

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

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

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

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


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

*                     NOT SHARED -  транзакции разных типов работают через разные соединения с базой. Данное значение параметра подразумевает также 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-выражений, соответственно:

SET TRANSACTION READ WRITE WAIT ISOLATION LEVEL READ COMMITTED

и

SET TRANSACTION READ WRITE WAIT ISOLATION LEVEL SNAPSHOT

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

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

 Обзор.................................................................................................................................................

 Состав языка SQL.........................................................................................................................

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

 Команда SELECT.........................................................................................................................

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

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

 Все поля.................................................................................................................................

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

 Блобы.....................................................................................................................................

 Вычисления..........................................................................................................................

 Литералы...............................................................................................................................

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



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

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

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

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

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

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

 BETWEEN.............................................................................................................................

 IN...........................................................................................................................................

 LIKE......................................................................................................................................

 CONTAINING......................................................................................................................

 IS NULL................................................................................................................................

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

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

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

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

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

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

 Соединение (JOIN)....................................................................................................................

 Внутренние соединения......................................................................................................

 Самосоединения...................................................................................................................

 Внешние соединения...........................................................................................................


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


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

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

SELECT job_code

FROM employee       получить список должностей сотрудников

JOB_CODE

========

VP      

Eng      

Eng     

Mktg    

Mngr    

SRep    

Admin   

Finan   

Mngr    

Mngr    

Eng

...

Данный пример некорректно решает задачу “получения” списка должностей сотрудников предприятия, так как в нем имеются многочисленные повторы, затрудняющие восприятие информации. Тот же запрос, включающий модификатор DISTINCT, устраняющий дублирование, дает верный результат.

SELECT DISTINCT job_code

FROM employee                   получить список должностей сотрудников

JOB_CODE

========

Admin   

CEO     

CFO     

Dir     

Doc      

Eng     

Finan   

Mktg    

Mngr    

PRel    

SRep    

Sales   

VP      

Два следующих примера показывают, что модификатор DISTINCT действует на всю строку сразу.

SELECT first_name, last_name

FROM employee

WHERE first_name = "Roger" получить список служащих,
                                                                            имена которых - Roger

FIRST_NAME      LAST_NAME           

=============== ====================

Roger           De Souza            

Roger           Reeves

SELECT DISTINCT first_name, last_name

FROM employee

WHERE first_name = "Roger" получить список служащих,
                                                                            имена которых - Roger

FIRST_NAME      LAST_NAME           

=============== ====================

Roger           De Souza            

Roger           Reeves



 Утилита 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

Long Integer

числовое поле длиной 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

Formatted Memo

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

Graphic

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

OLE

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

Logical

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

+ (Autoincrement)

поле длиной 4 байта, содержащее нередактируемое (read-only) значение типа long integer. Значение этого поля автоматически увеличивается (начиная с 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 and Embedding) - образы, звук, видео, документы - которые для своей обработки вызывают создавшее их приложение. Может иметь любую длину. Это поле также сохраняется в отдельном файле. 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, но и создавать их

TEXT BLOB

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

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



Итак, мы изучили все типы полей, являющиеся “родными” для 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|Restructure Table (для открытой в данный момент таблицы) или Utilities|Restructure (с возможностью выбора таблицы). Однако, если Вы желаете изменить структуру или добавить новые свойства для таблицы, которая в данный момент уже используется другим приложением, Database Desktop откажет Вам в этом, поскольку данная операция требует монопольного доступа к таблице. Но зато все произведенные в структуре изменения сразу же начинают “работать” - например, если Вы определите ссылочную целостность для пары таблиц, то при попытке вставить в дочернюю таблицу данные, отсутствующие в родительской таблице, в Delphi возникнет исключительное состояние.

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

         


 Внешние соединения


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

Вспомним, запрос вида

SELECT first_name, last_name, department

FROM employee e, department d

WHERE e.dept_no = d.dept_no

возвращает только те строки, для которых условие соединения    (e.dept_no = d.dept_no)  принимает значение true.

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

Существует два вида внешнего соединения:  LEFT JOIN  и   RIGHT JOIN.  

В левом соединении (LEFT JOIN) запрос возвращает все строки из левой таблицы (т.е. таблицы, стоящей слева от зарезервированного словосочетания “LEFT JOIN”) и только те из правой таблицы, которые удовлетворяют условию соединения. Если же в правой таблице не найдется строк, удовлетворяющих заданному условию, то в результате они замещаются значениями null.

Для правого соединения - все наоборот.

SELECT first_name, last_name, department

FROM employee e LEFT JOIN department d

  ON e.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.

А вот пример правого соединения:

SELECT first_name, last_name, department

FROM employee e RIGHT JOIN department d

  ON e.dept_no = d.dept_no
                         получить список сотрудников
                                                                     и название их отделов,
                                                                     включая отделы, в которые еще
                                                                     не назначены сотрудники

FIRST_NAME      LAST_NAME     DEPARTMENT               

=============== ============= =========================

Terri           Lee           Corporate Headquarters   

Oliver H.       Bender        Corporate Headquarters   

Mary S.         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                

<null>          <null>        Software Products Div.   

Bruce           Young         Software Development

...

В результирующий набор входит и отдел “Software Products Div.” (а также отдел “Field Office: Singapore”, не представленный здесь), в котором еще нет ни одного сотрудника. 


 Внутренние соединения


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

SELECT first_name, last_name, department

FROM employee, department

WHERE job_code = "VP"     получить список сотрудников,
                                                                       состоящих в должности “вице-
                                                                       президент”, а также названия
                                                                       их отделов

FIRST_NAME      LAST_NAME        DEPARTMENT               

=============== ================ ======================   

Robert          Nelson           Corporate Headquarters   

Mary S.         MacDonald        Corporate Headquarters   

Robert          Nelson           Sales and Marketing      

Mary S.         MacDonald        Sales and Marketing      

Robert          Nelson           Engineering              

Mary S.         MacDonald        Engineering               

Robert          Nelson           Finance                  

Mary S.         MacDonald        Finance

...

Этот запрос (“без соединения”) возвращает неверный результат, так как имеющиеся между таблицами связи не задействованы. Отсюда и появляется дублирование информации в результирующей таблице. Правильный результат дает запрос с использованием операции соединения:

SELECT first_name, last_name, department

FROM employee, department

WHERE job_code = "VP"

  AND employee.dept_no = department.dept_no

 


                                                       имена таблиц

                                                                        получить список сотрудников,
                                                                                          состоящих в должности “вице-
                                                                                          президент”, а также названия
                                                                                          их отделов


FIRST_NAME      LAST_NAME        DEPARTMENT               

=============== ================ ======================   

Robert          Nelson           Engineering              

Mary S.         MacDonald        Sales and Marketing

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

Замечание 1: в одном запросе нельзя смешивать использование написания имен таблиц и их алиасов.

Замечание 2: алиасы таблиц могут совпадать с их именами.

SELECT first_name, last_name, department

FROM employee e, department d

WHERE job_code = "VP"

  AND e.dept_no = d.dept_no

 

                                    алиасы таблиц

                                                                        получить список сотрудников,
                                                                                          состоящих в должности “вице-
                                                                                          президент”, а также названия
                                                                                          их отделов

FIRST_NAME      LAST_NAME        DEPARTMENT               

=============== ================ ======================   

Robert          Nelson           Engineering              

Mary S.         MacDonald        Sales and Marketing

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

SELECT first_name, last_name, job_title, 

       department



FROM employee e, department d, job j

WHERE d.mngr_no = e.emp_no

  AND e.job_code = j.job_code

  AND e.job_grade = j.job_grade

  AND e.job_country = j.job_country
                           получить список сотрудников
                                                                         с названиями их должностей
                                                                         и названиями отделов

FIRST_NAME LAST_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.

SELECT first_name, last_name, department

FROM employee e JOIN department d

 ON e.dept_no = d.dept_no

AND department = "Customer Support"

WHERE last_name starting with "P"
                                                                  получить список  служащих
                                                                  (а заодно и название отдела),
                                                                  являющихся сотрудниками отдела
                                                                  “Customer Support”, фамилии кото-
                                                                  рых начинаются с буквы “P”

FIRST_NAME    LAST_NAME       DEPARTMENT

============= =============== ===================

Leslie        Phong           Customer Support         

Bill          Parker          Customer Support         


 Все поля


SELECT *

FROM phone_list      получить список служебных телефонов
                                                         всех работников предприятия
                                                         со всей необходимой информацией

EMP_NO FIRST_NAME LAST_NAME PHONE_EXT LOCATION      PHONE_NO

====== ========== ========= ========= ============= ==============

    12 Terri      Lee       256       Monterey      (408) 555-1234

   105 Oliver H.  Bender    255       Monterey      (408) 555-1234

    85 Mary S.    MacDonald 477       San Francisco (415) 555-1234

   127 Michael    Yanowski  492       San Francisco (415) 555-1234

     2 Robert     Nelson    250       Monterey      (408) 555-1234

   109 Kelly      Brown     202       Monterey      (408) 555-1234

    14 Stewart    Hall      227       Monterey      (408) 555-1234

 ...



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


SELECT first_name, last_name, phone_no,
       location, phone_ext, emp_no

FROM phone_list      получить список служебных телефонов
                                                         всех работников предприятия
                                                         со всей необходимой информацией,
                                                         расположив их в требуемом порядке

FIRST_NAME LAST_NAME PHONE_NO       LOCATION      PHONE_EXT EMP_NO

========== ========= ============== ============= ========= ======

Terri      Lee       (408) 555-1234 Monterey      256           12

Oliver H.  Bender    (408) 555-1234 Monterey      255          105

Mary S.    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

...



 Вычисления


SELECT emp_no, salary, salary * 1.15

FROM employee           получить список номеров
                                                                  служащих и их зарплату,
                                                                  в том числе увеличенную на 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.



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


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

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

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

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

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

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

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

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

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

·        Нажмите кнопку 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. Заполните созданный метод так:

procedure TForm2.Query2CalcFields(DataSet: TDataSet);

begin

  Query2NewTotalInvoice.Value := 23.0;

end;

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

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

procedure TForm1.Query2CalcFields(DataSet: TDataset);



begin

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

end;

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

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

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

procedure TForm1.Query1CalcFields(DataSet: TDataset);

var

  R : Double;

begin

  R:=0;

  with Query2 do begin

    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 для данного примера необязательно и его можно было бы удалить (однако, оно необходимо в других случаях).


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


          Вы видели что таблицы 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

  from Orders O, Items I

  where O.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.        


 Закладки (Bookmarks)


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

function GetBookmark: TBookmark;

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

procedure GotoBookmark(Bookmark: TBookmark);

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

procedure FreeBookmark(Bookmark: TBookmark);

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

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

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



 Заключение


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


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

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

 

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

 Обзор.............................................................................................................................................

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

 Заключение..................................................................................................................................





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

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




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

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



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


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

SET TRANSACTION [Access mode] [Lock Resolution]

[Isolation Level] [Table Reservation]

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

выражение

SET TRANSACTION

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

SET TRANSACTION READ WRITE WAIT ISOLATION LEVEL SNAPSHOT

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

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

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

Пример:

SET TRANSACTION READ WRITE

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

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

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

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

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

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

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


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

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

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

         

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

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

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

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

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


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


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

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

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