Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ПрИС / ПрИС_Лабораторная работа №8.doc
Скачиваний:
46
Добавлен:
11.03.2015
Размер:
137.73 Кб
Скачать

Лабораторная работа №8 Применение технологии доступа к данным Delphi – ibx при создании приложения для работы с бд. Команды языка sql для манипулирования данными

Цель работы: познакомиться с применением технологии доступа к данным Delphi – IBX при создании приложения для работы с БД, освоить основные команды языка SQL для манипулирования данными.

Основные теоретические сведения

На странице InterBase Палитры компонентов содержатся компоненты доступа к данным, адаптированные для работы с сервером InterBase и объединенные названием InterBase Express. Компоненты из набора InterBase Express предназначены для работы с сервером InterBase версии не ниже 5.5.

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

Технология IBX обеспечивается следующими компонентами:

IBDatabase – создает соединение с БД IB

IBTable – компонент, инкапсулирующий набор данных, обеспечивающий доступ к одной из таблиц

IBQuery - компонент, инкапсулирующий набор данных, позволяет на уровне приложения общаться с БД на языке SQL.

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

IBTransaction - улучшенное управление транзакциями

IBUpdateSQL – используется для изменений в таблицах только для чтения и для кеширования изменений

IBSQL – выполняет запросы SQL, минимизируя затраты буферизации и обмена данными с компонентами Delphi

IBDataSet – Обеспечивает выполнение команд Select и выполняет команды SQL по вставке, удалению и изменению записей.Также как и IBSQL обеспечивает эффективный доступ к данным

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

IBSQLMonitor - отслеживание состояния процессов выполнения запросов

IBEvents – обеспечивает асинхронную обработку событий сервера IB

IBExtract – извлекает метаданные с сервера

IBClientDataSet – клиентский набор данных

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

Механизм доступа к данным InterBase Express

Все компоненты InterBase Express, инкапсулирующие набор данных, должны обращаться к базе данных только через компонент соединения TIBDatabase. Поэтому, для построения приложения на основе компонентов IBX на модуле данных должны быть, прежде всего, размещены компоненты IBDatabase и IBTransaction.

Компонент TIBDatabase осуществляет соединение с сервером БД, а IBTransaction управляет транзакциями. К этим компонентам подключаются компоненты наборов данных: IBTable, IBQuery, IBStoredProc, IBSQL, IBDataset, - заданием в них значений свойств Database (имя компонента IBDatabase) и Transaction (имя компонента IBTransaction). А далее к компонентам наборов данных могут подключаться обычные компоненты источников данных DataSource, к которым, в свою очередь подключаются компоненты отображения данных.

Установка соединения с БД

Для создания соединения с БД используется свойство DatabaseName, в котором необходимо указать полный путь (включая имя сервера) к выбранному файлу БД с расширением gdb. Компонент IBDatabase имеет собственный редактор, который также позволяет задать значения основных свойств, обеспечивающих соединение с базой данных.

На панели Connection выбирается требуемый сервер InterBase (локальный или доступный удаленно), затем в списке Protocol определяется используемый сетевой протокол и при помощи кнопки Browse выбирается файл базы данных.

На панели Database Parameters задаются имя пользователя (SYSDBA) и его пароль (masterkey). Также здесь можно выбрать и набор шрифтов для языковой адаптации приложения WIN1251 (Character Set). Свойство LoginPrompt определяет появление диалога, запрашивающего имя пользователя и пароль при соединении с базой данных.

Соединение с БД включается и отключается свойством Connected, которое связано со свойством Active компонентов наборов данных, подключенных к данному.

Свойство компонента IBDatabase - DefaultTransaction указывает на компонент IBTransaction, используемый по умолчанию при соединении с БД. Свойство компонента IBTransaction - DefaultDatabase задает компонент IBDatabase, используемый по умолчанию при выполнении транзакции.

При работе с серверными БД компонент набора данных TTable становится мало эффективным, так как он создает на компьютере пользователя временную копию серверной БД и работает с этой копией. Естественно, что такая процедура требует больших ресурсов и существенно загружает сеть. Преодолеть этот недостаток позволяет использование в приложении компонента IBQuery вместо IBTable, так как запрос SQL сводится к просмотру таблицы и результат этого запроса (а не сама исходная таблица) помещается во временном файле на компьютере пользователя. Ограничением работы с компонентом IBQuery является то, что набор данных, передаваемый в приложение, доступен только для чтения, однако существуют возможности преодолеть и этот недостаток.

В целом проектирование приложения соответствует вышеприведенному в работе 3, но вместо TTable используется компонент TQuery. Компонент TQuery не содержит свойства TableName, т.к. имя таблицы (таблиц) указывается текстом SQL-строки (задается в свойстве SQL). Выполнение этой SQL-строки инициируется в DesignTime установкой Active=True; при RunTime следует выполнять методом Open в том случае, когда SQL-оператор не возвращает набора данных (таких как INSERT, UPDATE, DELETE, CREATE TABLE) а также операторы Select, которые возвращают в качестве результата одну строку. или методом ExecSQL в противном случае (содержит SELECT предписание языка SQL).

Команды SQL для манипулирования данными

Одной из особенностей языка запросов к БД является его представление в виде текста; формирование текста SQL-запроса может быть произведено любыми средствами (важно лишь соблюдение правил синтаксиса запроса). Существуют три формы SQL:

Интерактивный SQL применяется для непосредственной работы с БД - пользователь вводит SQL-оператор, он сразу же выполняется и пользователь видит результат выполнения (или код ошибки).

Статический SQL содержит SQL-операторы, жестко закодированные в теле исполняемого приложения. Наиболее распространен встроенный SQL (Embedded SQL), где SQL-код включается в исходный текст (базовой) программы, написанной на другом языке (например, С или Pascal); при использовании встроенного SQL результаты выполнения операторов SQL перенаправляются в переменные, которыми оперирует базовая программа.

Динамический SQL также является частью приложения, но конкретный SQL-код генерируется во время выполнения приложения (RunTime), а не вводится заранее. Наибольшей гибкостью обладает последний вариант формирования текста SQL-запроса.

С помощью динамического SQL программа-клиент выполняет программное формирование оператора SQL для его последующего исполнения, делая это в три этапа:

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

  • программа выполняет оператор Prepare, который обращается к серверу баз данных для анализа текста оператора и подготовки его к выполнению;

  • программа использует оператор Execute для выполнения подготовленного оператора.

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

Ручная настройка Query (статические запросы)

С помощью свойства DataBase указать имя компонента IBDatabase. Свойство SQL должно содержать запрос, содержащий имя таблицы, с которой вы хотите работать. Редактор для создания запроса может быть вызван с помощью свойства SQL Инспектора объектов или с помощью команды EditSQL контекстного меню компонента. После написания запроса необходимо установить свойство Active=True. Для управления отображением данных имеется Редактор полей. Вызов, назначение, функции и приемы работы с ним аналогичны Редактору полей компонента Table.

Свойства Query аналогичны свойствам Table. Программный доступ к полям осуществляется с помощью свойства Fields (Query1.Fields[1]), методом FieldByName (Query1.FieldByName(Name)) или по имени объекта (Query1Name). Также можно осуществлять доступ к значениям полей, используя свойства Value, AsString и др., осуществлять навигацию по набору данных, устанавливать фильтры, ограничивать вводимые значения и т.д.

Методы Query. Перед изменением каких-либо свойств, влияющих на выполнение запроса, необходимо закрыть соединение, связанное с прежним запросом методом Close. Для выполнения запроса используются методы Open или ExecSQL в зависимости от вида запроса (см. выше).

Получить список имен полей таблицы, связанной с Query позволяет метод GetFieldNames, например: Query1. GetFieldNames(ComboBox1.Items).

Работа с параметрами. Динамические запросы позволяют использовать параметры, которые можно применять вместо имен таблиц, имен полей и их значений. Значения этих параметров передаются извне и тем самым, не меняя текст самого запроса, позволяют изменять возвращаемый им результат. Параметры задаются в запросе с двоеточием, предшествующем имени параметра. После ввода запроса, содержащего параметры, свойство Инспектора объектов Params, позволит открыть окно со списком всех параметров, указанных в запросе. Каждый из параметров является объектом типа TParam, свойства которого могут быть изменены в Инспекторе объектов. Основные свойства параметров следующие:

DataType – тип данных параметра

ParamType – тип параметра (используется при обращении к процедурам, хранимым на сервере)

Value – значение по умолчанию

Type – тип значения по умолчанию.

Программный доступ к параметрам аналогичен доступу к полям набора данных Query1.ParamByName(‘xSt’).AsString:=Edit1.text;.

Отображение всех данных отдельной таблицы с помощью компонента IBQuery, используя динамический SQL:

IBQuery1.Close;

IBQuery1.SQL.Clear;

IBQuery1.SQL.Add('select * from <имя_табицы>');

IBQuery1.Open

Имя таблицы может быть выбрано из списка имен таблицы, связанных с IBDataBase, полученного с помощью метода GetTableNames, например: IBDatabase1.GetTableNames(form1.DBComboBox1.Items). Поскольку SQL-запрос представляет собой строку, то для формирования текста запроса может быть использована строковая переменная:

Str:=’ select * from’;

Str:=Str+’<имя_таблицы>’

IBQuery1.SQL.Add(str);

Отображение данных из нескольких связанных таблиц базы данных

Для демонстрации возможности отображения данных из нескольких связанных таблиц необходимо предварительно разместить на форме компоненты отображения данных (DBGrid) и навигации для этих таблиц. Для работы потребуется два компонента IBQuery, текст запроса компонента, соответствующего родительской таблице. В тексте запроса компонента IBQuery, соответствующего дочерней таблице, необходимо указать условие отбора: Select * from <имя_таблицы> Where (NM=:NM) (значение ключевого поля NM должно быть равно параметру :NM). В данном случае параметр не надо определять с помощью Редактора параметров, вызываемого из свойства Params компонента IBQuery. Вместо этого в свойстве DataSource компонента IBQuery дочерней таблицы необходимо сослаться на источник данных DataSource, связанный с родительской таблицей. Это сообщит приложению, что оно должно взять значения параметра из текущей записи этого источника данных (имя параметра должно совпадать с именем поля в источнике данных).

Модификация данных при работе с компонентом IBQuery.

  1. Компонент Query с запросом Select формирует таблицу только для чтения. Установка значения True в свойстве компонента RequestLive позволяет возвращать изменяемый набор данных, вместо таблицы только для чтения при соблюдении следующих условий:

    1. - набор данных формируется обращением только к одной таблице;

    2. - набор данных не упорядочен использованием Order By;

    3. - набор данных не использует агрегатных функций.

  2. Использование запросов Insert, Delete UpDate. Как уже упоминалось выше, запросы, не возвращающие наборов данных, выполняются методом ExecSQL:

  3. Использование компонента UpdateSQL. Компонент UpdateSQL позволяет модифицировать наборы данных, открытые в режиме только для чтения. В свойстве SQL компонента IBQuery написать текст запроса для отображения всех записей таблицы: Метод ExecSQL осуществляет немедленную модификацию данных таблицы: Select * from <имя_таблицы>. Установить свойство компонента CachedUpdates равным True. Разместить в приложении компонент IBUpdateSQL и связать его с приложением, установив в компоненте IBQuery свойство UpdateObject в имя этого компонента. В свойствах компонента UpdateSQL DeleteSQL, InsertSQL и ModifySQL записать тексты запросов, которые должны выполняться при удалении, вставке или модификации записи. Запросы могут быть созданы с помощью редактора компонентов, вызываемого с помощью контекстного меню. При работе в этом редакторе после указания всех необходимых сведений на вкладке Options, для просмотра сгенерированного редактором запроса необходимо нажать кнопку Generate SQL. Запросы автоматически переносятся в соответствующие свойства компонента после нажатия на ОК. Раздел Set сгенерированного запроса указывается установка всех полей в значения, задаваемые соответствующими параметрами с именами, тождественными именам полей. В разделе Where содержатся условия, по которым идентифицируется модифицируемая запись. Префикс OLD_ , используемый в именах полей, используется для указания значений, полученных компьютером до модификации записи. При создании запросов для модификации и добавления записи имена полей, значения в которые проставляется с помощью триггера, использующего генератор, включать не надо. После выполнения всех описанных действий редактирование, добавление и удаление записей таблицы может быть выполнено с помощью компонента DBNavigator.

  4. Использование хранимых на сервере исполняемых процедур. Для работы с хранимыми процедурами используется компонент IBStoredProc, который позволяет передавать информацию в процедуры и воспринимать возвращаемую информацию с помощью параметров. Для работы компонента необходимо установить его свойства: DatabaseName и StoredProcName. Свойства Params и ParamType заносятся автоматически после связывания с конкретной процедурой во время проектирования в соответствии с описанием процедуры.

    1. пример

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

Оператор select

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

Select [ distinct | all {* | <значение 1> [, <значение 2>… ]}

from <таблица1 > [, <таблица2 >… ]

[ where <условие_поиска>]

[ group by столбец [collate collation]

[, столбец 1 [collate collation]…]

[ having <условия_поиска>]

[ union <оператор_select>]

[ plan <план_выполнения_запроса>]

[ order by <список_столбцов>];

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

Select {* | <значение 1> [, <значение 2>…]}

from <таблица 1> [, <таблица 2>…];

<значение 1>, <значение 2>… – имя столбца возвращаемого оператором, * – все столбцы

<таблица 1>, <таблица 2>… – имя таблицы, из которой происходит выборка данных.

Например, создать набор данных, состоящий из всех столбцов:

Select * from prihod;

Такой же набор данных можно получить:

Select n_prihod, date_prihod, name_det, kolvo, from prihod;

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

Select { * | <значение 1> [, <значение 2>… ]}

from <таблица 1> [, <таблица 2>…]

where <условия поиска>;

Сравнение с константой

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

<имя_столбца> < оператор> < константа>

<оператор> – =, <, >, <= (!>), >=(!<), <>(!=).

Например, выбрать из таблицы prihod все операции приема товара объемом 20 единиц:

Select * from prihod

where kolvo=20;

Внутреннее соединение таблиц

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

<имя_столбца_табл1> <оператор> <имя_столбца_табл2>.

Например, выбрать все записи о приходе деталей из таблицы prihod и для каждой детали указать его цену из таблицы detal:

Select prihod.*, detal.zena_ed

from prihod, detal

where prihod.name_det = detal.name_det;

Для каждой записи из таблицы prihod ищется запись в таблице detal, у которой значение в поле name_det совпадает со значением name_det текущей записи таблицы prihod. Порядок перечисления в условии поиска значения не имеет:

Prihod.name-det = detal.name_det

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

Идентификация столбцов через имя таблицы неудобно из-за громоздкости обозначений. Лучше присвоить каждой таблице краткое имя. Такие имена называются псевдонимами таблиц. Они отделяются пробелом от фактического имени таблицы в списке from:

Select from <таблица_1 псевдоним_1> [, <таблица_2 псевдоним_2> …]

where … ;

Например:

Select prihod.*, detal.zena_ed

from prihod P, detal D

where P.name_det = D.name_det;

Определение сортировки order by

Результирующий HД можно упорядочить с помощью предложения:

Order by <список_столбцов>.

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

Select name_det, zena_ed

from detal

where zena-ed >= 35;

order by name_det;

Устранение повторяющихся записей

Ключевое слово Distinct. Повторяющимися считаются записи, содержащие идентичные значения во всех столбцах результирующего HД. Если в результирующем HД нужно вносить все записи, то указывают ключевое слово All (по умолчанию All). Например, получить наименование всех деталей, полученных на склад:

Select distinct name_det

from prihod;

Расчет вычисляемых столбцов

Для расчета вычисляемых столбцов результирующего HД используются арифметические выражения:

Select [distinct | аll ] { * | <столбец 1> [, <выражение 1>… ]}

from <таблица 1> [, <таблица 2>… ];

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

Например: рассчитать общую стоимость полученных деталей для каждого факта получения:

Select p.*, d.zena_ed, p.kolvo * d.zena_ed

as stoim

from prihod p, detal d

where p.name_det = d.name_det;

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

count (<выражение>) – подсчитывает число вхождений значения выражения во все записи результирующего HД;

sum (<выражение>) – суммирует значение выражения;

avg (<выражение>) – находит среднее значение;

max (<выражение>) – определяет максимальное значение;

min (<выражение>) – определяет минимальное значение.

а) количество наименований деталей, оприходованных на список:

select count (distinct name_det) as count_name

from prihod;

б) вычислить общую стоимость оприходованных деталей за 4.10.00:

select sum (p.kolvo * d.zena_ed) as itogo

from prihod p, detal d

where (p.name_det = d.name_det) and (p.date_prihod = ‘04.10.00’);

Группировка записей

Для группы записей столбца, характеризующие одинаковые значения можно получить агрегированные значения (min, max, avg). При этом один из столбцов представляется агрегирующей функцией, и предложение group by столбец [, столбец…] перед предложением where.

Например, получить общее количество прихода деталей по каждой из них:

Select p.name_det sum (p.kolvo) as priem

from prihod p

group by p.name_det;

Или, общая цена на каждую деталь на каждую дату:

Select p.name_det, p.date_prihod, sum (p.kolvo * d.zena_ed) as sum

from prihod p, detal d

where p.name_det = d.name_det

group by p.name_det, p.date_prihod;

Предложение having

Наложение ограничений на группировку записей. Агрегация выдается только по группам удовлетворяющим условию.

Формат: после group by столбец [, столбец…]

Having <агрег_функц> <отношение> <значение>, где

Агрег_функц – min, max, avg, sum;

Отношение – =, <>, <, <=, >=;

Значение – константа, результат вычисления или единичное значение, возвращенное select.

Найти минимальный приход товара (деталей) не меньше 100 единиц.

Select name_det, min (kolvo)

from prihod

group by name_det

having min (kolvo) >=100;

Можно задавать разные функции для столбца и условия having.

Отличие where и having

В условие where нельзя вносить агрегированную функцию.

Where – исключает значения, не удовлетворяющие условию.

Having – исключает группы с агрегированными значениями.

Использование подзапросов

Часто невозможно решить поставленную задачу путем использования единственного запроса. Например, в тех случаях, когда при использовании условия поиска в предложении where значение с которым надо сравнить, далее не определено, а вычисляется оператором select.

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

Оператор select имеет вид:

Select

from

where <сравниваемое значение> <оператор> (select);

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

быть записан так:

Select kolvo, date_prihod from prihod

where kolvo = ( select max

(kolvo) from prihod);

Оператор select возвращает не одно значение, а список. Поэтому может возникнуть ошибка. Чтобы ее избежать надо заменить оператор = на оператор выбора из нескольких возможных значений (in).

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

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

Select p0.* from prihod p0

where p0.post in

(select p1.post

from prihod p1

where kolvo in

(select max (p2.kolvo)

from prihod p2));

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

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

Определяются в предложении from согласно спецификации:

Select { * | <значение 1> [, <значение 2>… ]}

from <таблица 1> <вид соединения > join < таблица 2>

on <условие поиска>;

Внешнее соединение отличается от внутреннего тем, что в результирующий HД включаются записи ведущей таблицы соединения, которые объединяются с пустым множеством записей другой таблицы. Какая из таблиц будет ведущей, определяет вид соединения (left – левое внешнее соединение, ведущая таблица 1; right – правое внешнее соединение, ведущая таблица 2; full – полное внешнее соединение).

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

1) если для записи таблицы 1 имеются записи таблицы 2, удовлетворяющие условию соединения, то в результирующий HД включаются все комбинации записей таблиц 1 и 2;

2) иначе, в HД включается запись из таблицы 1, соединенная с пустой записью таблицы 2;

3) пункты 1 и 2 повторяются для таблицы 2 и таблицы 1.

Пример. Пусть имеем таблицы A и B.

P1

P2

P3

P1

P2

a

x

400

x

1

b

x

200

y

2

c

y

500

z

2

d

Тогда выполнение оператора

Select A.P1, A.P2, B.P2

from A left join B

on A.P2 = B.P1;

A.P1 A.P2 A.P2

a x 1

b x 1

c y 2

d – –

A.P1 A.P2 A.P2

a x 1

b x 1

с y 2

– – 2

Запрос внешнего правого соединения:

Select A.P1, A.P2, B.P2

from A right join B

on A.P2 = B.P1;

дает другой результат:

Выполнение оператора полного внешнего соединения таблиц A и B:

Select A.P1, A.P2, B.P2

from A full join B

on A.P2 = B.P1;

приведет к результату:

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

Select p.date_prihod, p.name_det, p.kolvo, p1.post, p1.gorod

from prihod p left join postаvshik p1

on p.name_det = p1.name_det;

или

Select p.date_prihod, p.name_det, p.kolvo, p1.post, p1.gorod

from prihod p right join postаvshik p1

on p.name_det = p1.name_det;

Объединение результатов нескольких операторов select.

Объединение производится оператором union. Результирующие НД должны иметь одинаковую структуру. Одинаковые записи не дублируются.

Произведем объединение трех результирующих наборов данных:

select p.*

from prihod p

where p.name_det containing ‘3’;

union

select p.*

from prihod p

where p.kolvo > 100;

union

select p.*

from prihod p

where p.post = ‘AMD’;

Операции реляционной алгебры