- Содержание
- Краткий
Обзор
- Основные
понятия TQuery
- Свойство
SQL
- TQuery и
Параметры
- Передача
параметров через TDataSource
- Выполнение
соединения нескольких таблиц
- Open или
ExecSQL?
- Специальные
свойства TQuery
-
- Краткий
Обзор
В этой главе
Вы узнаете некоторые основные
понятия о запросах (queries) и
транзакциях. Это достаточно
широкие понятия, поэтому
обсуждение разбито на следующие
основные части:
- Объект
TQuery.
-
- Использование
SQL с локальным и удаленным
серверами (Select, Update, Delete и Insert).
-
- Использование
SQL для создания объединения
(joins), связанных курсоров (linked
cursors) и программ, которые ведут
поиск заданных записей.
Сокращение SQL
означает Structured Query Language - Язык
Структурированных Запросов, и
обычно произноситься либо как
"Sequel" либо " Ess Qu El”. Однако,
как бы Вы его ни произносили, SQL - это
мощный язык БД, который легко
доступен из Delphi, но который
отличается от родного языка Delphi.
Delphi может использовать утверждения
SQL для просмотра таблиц, выполнять
объединение таблиц, создавать
отношения один-ко-многим, или
исполнить почти любое действие,
которое могут сделать ваши
основные инструменты БД. Delphi
поставляется с Local SQL, так что Вы
можете выполнять запросы SQL при
работе с локальными таблицами, без
доступа к SQL серверу.
Delphi
обеспечивает поддержку “pass through
SQL”, это означает то, что Вы можете
составлять предложения SQL и
посылать их непосредственно
серверам Oracle, Sybase, Inrterbase и другим.
“Pass through SQL” - это мощный механизм
по двум причинам:
-
- Большинство
серверов могут обрабатывать SQL
запросы очень быстро, а это
означает, что используя SQL для
удаленных данных, Вы получите
ответ очень быстро.
-
- Есть
возможность составлять SQL
запросы, которые заставят
сервер исполнить
специализированные задачи,
недоступные через родной язык
Delphi.
Перед
чтением этой статьи Вы должны
иметь, по крайней мере,
элементарное понятие о серверах и
различиях между локальными и
удаленными (remote) данными.
- Основные
понятия о TQuery
Предыдущий
Урок был, в основном, посвящен
объекту TTable, который служит для
доступа к данным. При использовании
TTable, возможен доступ ко всему
набору записей из одной таблицы. В
отличие от TTable, TQuery позволяет
произвольным образом (в рамках SQL)
выбрать набор данных для работы с
ним. Во многом, методика работы с
объектом TQuery похожа на методику
работы с TTable, однако есть свои
особенности.
Вы может
создать SQL запрос используя
компонент TQuery следующим способом:
- Назначите
Псевдоним (Alias) DatabaseName.
- Используйте
свойство SQL чтобы ввести SQL
запрос типа
“Select * from Country”.
- Установите
свойство Active в True
Если
обращение идет к локальным данным,
то вместо псевдонима можно указать
полный путь к каталогу, где
находятся таблицы.
Две основных
вещи, которые Вы должны понять
прежде, чем перейти дальше:
-
- Этот урок
не является учебником для
начинающих по SQL, а, скорее,
описанием объекта TQuery и
основных задач, которые Вы
можете решить с его помощью.
Если Вы не знаете ничто об SQL, Вы
все же сможете воспользоваться
этой статьей, и, в конце концов,
приобретете некоторое
понимание основ SQL. Однако, для
полного изучения языка, Вы
должны обратиться к любой из
большого количества книг и
документов, доступных по этому
предмету.
-
- Delphi
использует pass through SQL, поэтому
для разных SQL серверов
синтаксис может быть несколько
разным. Версия SQL для локальных
таблиц (Local SQL) очень сильно
урезан, по сравнению со
стандартом. Чтобы узнать о его
возможностях, Вы должны
прочитать не только эту статью,
но также файл LOCALSQL.HLP.
Вы увидите,
что объект TQuery один из наиболее
полезных и гибких компонентов,
доступных в Delphi. С ним Вы сможете
воспользоваться всей мощью,
предоставляемой лидерами среди
промышленных SQL серверов, вроде
InrterBase, Oracle или Sybase.
- Свойство
SQL
- Свойство
SQL - вероятно, самая
важная часть TQuery.
Доступ к этому
свойству происходит
либо через Инспектор
Объектов во время
конструирования
проекта (design time), или
программно во время
выполнения программы
(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, то обычно нужно
сделать четыре шага:
- Закрыть
TQuery
- Подготовить
объект TQuery, вызвав метод Prepare
- Присвоить
необходимые значения свойству
Params
- Открыть
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 во время выполнения.
- Передача
параметров через
TDataSource
- В
предыдущем Уроке Вы
видели способ
создания отношения
однин-ко-многим между
двумя таблицами.
Теперь речь пойдет о
выполнении того же
самого действия с
использованием
объекта TQuery. Этот
способ более гибок в
том отношении, что он
не требует индексации
по полям связи.
Объект
TQuery имеет свойство
DataSource, которое может
использоваться для
того, чтобы создать
связь с другим DataSet. Не
имеет значения,
является ли другой
DataSet объектом TTable, TQuery,
или некоторый другим
потомком TDataSet. Все что
нужно для
установления
соединения - это
удостовериться, что у
того DataSet есть
связанный с ним DataSource.
Предположим,
что Вы хотите создать
связь между таблицами
ORDERS и CUSTOMERS так, что
каждый раз, когда Вы
просматриваете
конкретную запись о
заказчике, будут видны
только заказы,
связанные с ним.
Рассмотрите
следующий
параметризованный
запрос:
select * from
Orders where CustNo = :CustNo
В
этом запросе :CustNo -
связывающая
переменная, которой
должно быть присвоено
значение из
некоторого источника.
Delphi позволяет
использовать поле
TQuery.DataSource чтобы
указать другой DataSet,
который предоставит
эту информацию
автоматически.
Другими словами,
вместо того, чтобы
использовать свойство
Params и “вручную”
присваивать значения
переменной, эти
значения переменной
могут быть просто
взяты автоматически
из другой таблицы.
Кроме того, Delphi всегда
сначала пытается
выполнить
параметризованный
запрос используя
свойство DataSource, и
только потом (если не
было найдено какое-то
значение параметра)
будет пытаться
получить значение
переменной из
свойства Params. При
получении данных из
DataSource считается, что
после двоеточия стоит
имя поля из DataSource. При
изменении текущей
записи в главном DataSet
запрос будет
автоматически
пересчитываться.
Давайте
переделаем пример из
прошлого урока (LINKTBL -
связывание двух
таблиц). Создайте
новый проект, положите
на форму один набор
TTable, TDataSource и TDBGrid.
Привяжите его к
таблице CUSTOMER. Положите
на форму второй набор -
TQuery, TDataSource и TDBGrid и
свяжите объекты между
собой. (см рис.4).
В
свойстве SQL наберите
текст запроса:
select * from
Orders where CustNo = :CustNo
В
свойстве DatabaseName для
Query1 укажите DBDEMOS.
В
свойстве DataSource для Query1
укажите DataSource1.
Поставьте
Active = True и запустите
программу.
Рис.4:
Программа LINKQRY -
связанные курсоры с
помощью SQL
- Выполнение
соединения нескольких
таблиц.
Вы видели что
таблицы CUSTOMERS и ORDERS связаны в
отношении один-ко-многим,
основанному на поле CustNo. Таблицы
ORDERS и ITEMS также связаны отношении
один-ко-многим, только через поле
OrderNo.
Более
конкретно, каждый заказ который
существует в таблице ORDERS будет
иметь несколько записей в таблице
ITEMS, связанных с этим заказом.
Записи из таблицы ITEMS определяют
тип и количество изделий, связанных
с этим заказом.
Пример.
Некто Иванов
Ф.П. 1 мая 1995г. заказал следующее:
- Гайка
4х-угольная - 50 штук
- Вентиль - 1
штука
А некто
Сидорчук Ю.Г. 8 декабря 1994г. заказал:
- М/схема
КР580 ИК80 - 10 штук
- Транзистор
КТ315 - 15 штук
- Моток
провода - 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
Этот запрос
состоит из четырех различных
частей:
-
- Выражение
Select определяет, что Вы хотите
получить - курсор, содержащий
некоторую форму DataSet.
-
- Затем идет
список полей которые Вы хотите
включить в dataset. Этот список
включает поля CustNo, OrderNo, SaleDate,
PartNo и Qty. Первые три поля из
таблицы ORDERS, а два других - из
таблицы ITEMS.
-
- Выражение from объявляет, что Вы
работаете с двумя таблицами,
одна называется ORDERS, а другая
ITEMS. Для краткости, в запросе
используется особенность SQL,
которая позволяет Вам
ссылаться на таблицу ORDERS
буквой O, а на таблицу ITEMS буквой
I.
-
- Выражение where жизненно важно
потому, что оно определяет поля
связи для двух таблиц.
Некоторые серверы могут
вернуть DataSet, даже если Вы не
включите выражение where в запрос, но
почти всегда результирующий
набор записей будет не тем, что
Вы хотели видеть. Чтобы
получить нужный результат,
убедитесь что Вы включили
выражение where.
- Open
или ExecSQL?
- После
того, как составлен SQL
запрос, есть два
различных способа
выполнить его. Если Вы
хотите получить
курсор, то нужно
вызывать Open. Если
выражение SQL не
подразумевает
возвращение курсора,
то нужно вызывать ExecSQL.
Например, если
происходит вставка,
удаление или
обновление данных (т.е.
SQL запросы INSERT, DELETE,
UPDATE), то нужно вызывать
ExecSQL. Тоже самое можно
сказать по-другому: Open
вызывается при
запросе типа SELECT, а
ExecSQL - во всех
остальных случаях.
Вот
типичный SQL запрос,
который используется
для удаления записи из
таблицы:
delete from
Country where Name = ‘Argentina’;
Этот
запрос удалил бы любую
запись из таблицы COUNTRY,
которая имеет
значение "Argentina" в
поле Имя.
Не
трудно заметить, что
это тот случай, когда
удобно использовать
параметризованный
запрос. Например,
неплохо было бы менять
имя страны, которую
требуется удалить:
delete from
Country where Name = :CountryName
В
этом случае
переменная :CountryName
может быть изменена во
время выполнения:
Query2.Prepare;
Query2.Params[0]
:= ‘Argentina’;
Query2.ExecSQL;
Код
сначала вызывает Prepare,
чтобы сообщить Delphi что
он должен разобрать SQL
запрос и подготовить
свойство Params.
Следующим шагом
присваивается
значение свойству Params
и затем выполняется
подготовленный SQL
запрос. Обратите
внимание, что он
выполняется через
ExecSQL, а не Open.
Программа
INSQUERY из примеров Delphi
демонстрирует эту
технику (проект C:\DELPHI\DEMOS\DB\INSQUERY.DPR)
- Специальные
свойства TQuery
Есть
несколько свойств, принадлежащих
TQuery, которые еще не упоминались:
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;
|