В этой главе Вы узнаете некоторые
основные понятия о запросах (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, а другая
ITEMиспользуется особенность SQL,
S. Для краткости, в запросе
которая позволяет Вам
ссылаться на таблицу 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;
[ Предыдущий урок |
Содержание | Следующий урок ]
|