    48 Обзор 

    Глава 3.   Работа с данными через интерфейс ОАО 

    Объекты доступа к данным (ОАО) — набор объектов, таких как Database, Tablf 
Recordset и QueryDef, которые позволяют обращаться, организовывать и 
манипулирс программным способом данными в локальных или удаленных базах 
данных, вкш объекты структуры базы данных. 

    3.1. Обзор ОАО 

    Порядок иерархии объектов ОАО образует его объектную модель. Объектная 
модель позволяет формировать программный интерфейс, предоставляющий обширный 
дос! объектам этой иерархии и к функциональным возможностям базы данных. 

    | DBEngine	""]———————— Ядро базы данных 
    -\ Errors (Error)	|—————— Ошибки 
    —| Workspaces (Workspace)  j—————— Рабочие области 
    -\ Groups (Group)	|———— Группы пользователей 
    -| Users (User)	~|———— Пользователи Ц Databases (Database)     ~|———— Базы данных 
    -j Recordsets (Recordset)   "|—— Наборы записей 
    -| QueryDefs (QueryDef)     ~]—— Запросы 
    -\ TableDefs (TableDef)	|—— Таблицы 
    -| Relations (Relation)         ~|—— Отношения 
    -| Containers (Container)     ~]—— Контейнеры 

    Рис. 3.1. Объектная иерархия ОАО. 

    Вся объектная иерархия ОАО подчинена ддву базы данных Jet (объект 
DBEngine). При созда или обращении к определенным объектам производится 
обращение к этой иерархии. 
    Например, следующий код открывает базу данных: 

    Set   dbDbl   =  DBEngine.Workspaces(0).OpenDatabase("Dbl.mdb") 

    В следующей таблице представлены некоторые из объектов доступа к данным и 
их испо зование. 

    Объект	Описание 

    Workspace	Содержит открытые базы данных._____________________ 
    Database	Открытая база данных._________________________ 


    Глава 3. Работа с данными через интерфейс РАО______________49 

    Объект	Описание 

Recordset       
Записи в таблице или записи, которые получены при выполнении запроса. 

QueryDef        
Сохраненное определение запроса.

TableDef
Сохраненное определение таблицы.

    Примечание: В этой главе описывается работа с данными готовой базы данных. 
Здесь не показывается, как использовать Visual Basic для создания новой базы 
данных или изменения структуры существующей базы данных. В большинстве случаев 
для определения или изменения структуры базы данных формата .mdb можно 
использовать такой инструмент, как Microsoft Access. 

    3.1.1. Объекты DBEngine и Workspace 

    Чтобы с помощью ОАО можно было обратиться к данным программным способом, 
сначала необходимо с помощью объекта DBEngine открыть рабочую область (см. 
ниже "Объект Workspace"). 

    Объект DBEngine 

    Объект DBEngine — объект высшего уровня в объектной модели ОАО. Он 
содержит и управляет всеми остальными объектами иерархии ОАО. 
    Чтобы определить номер версии ОАО, можно прочитать свойство Version 
объекта DBEngine. Для манипулирования базой данных можно также использовать 
методы RepairDatabase или CompactDatabase объекта DBEngine. В следующем 
фрагменте показан пример использования свойства Version и метода RepairDatabase. 

    MsgBox   "Номер  версии   ОАО   -   "   &  _ 
    DBEngine.Version     '   Сделать   попытку  восстановления  базы  данных 
	DBEngine.RepairDatabase   "С:\DB1.MDB" 


    Объект Workspace 

    Объект Workspace определяет сеанс пользователя (рабочую область) и 
устанавливает, как приложение будет взаимодействовать с данными. Если база 
данных открывается без специфицирования объекта Workspace, по умолчанию Hcnonb3yeTcg_.DBEngine.Workspaces(0). 

    Примечание: Сеанс очерчивает последовательность операций, выполняемых 
процессором базы данных Microsoft Jet. Сеанс начинается, когда пользователь 
регистрируется, и заканчивается, когда пользователь выходит из приложения. Все 
операции, выполняемые в течение сеанса, формируют одну область определения 
транзакции, и подчинены разрешениям, определенным именем пользователя в 
системе и паролем. В ОАО сеансы реализованы как объекты Workspace. 

    В следующем фрагменте кода метод CreateWorkspace объекта DBEngine 
используется для создания новой рабочей области, в которой процессор базы 
данных Microsoft Jet используется для взаимодействия с данными: 

    Dim wspNew as Workspace 
    Set wspNew = DBEngine.CreateWorkspace _ 
    ("NewJetWorkspace", "Admin", "", dbUseJet) 

    По мере необходимости можно открывать дополнительные объекты Workspace. 
Каждая рабочая область имеет ассоциированные с ней идентификатор пользователя 
и пароль. 

    Примечание: Преимущество использования нескольких рабочих областей в том, 
что каждая рабочая область определяет отдельный сеанс и, следовательно, 
отдельный поток обработки. В каждом сеансе может быть установлен свой уровень 
защиты, и каждый сеанс может управлять транзакциями независимо от других 
сеансов. Если организовывать несколько уровней защиты или несколько областей 
определения транзакций не требуется, к объектам DBEngine 


    50 Обзор L 

или Workspace можно не обращаться. Visual Basic будет использовать 
умолчательный объек DBEngine.Wbfkspaces (0). 

    3.1.2. Открытие и закрытие базы данных 

    Первый шаг в создании приложения базы данных — открыть базу данных. При э 
объявляется объектная переменная как объект Database, и затем для открытия б 
данных используется метод OpenDatabase. 

    Объявление базы данных 

    Объектная переменная Database описывает открываемую базу данных. 
Ассоциирсв переменную с базой данных можно оператором Set. 
    В следующем фрагменте база данных открывается в умолчательной рабочей области: 

    Dim  dbDbl   As   Database Set   dbDbl   =  OpenDatabase  _ ("C:\VB\Nwind.mdb") 

    Метод OpenDatabase 

    Синтаксис метода OpenDatabase: 

    Set база_данных - рабочая_область.OpenDatabase (имя_базы_данных, опции, 
только_для_чтения, строка_соединения) 

    Элементы метода OpenDatabase перечислены в следующей таблице: 

    Элемент	Назначение 

    база данных	Объектная переменная, представляющая объект Database, коте рый 
требуется открыть. 
    рабочая_область	Необязательный. Объектная переменная, представляющая суще 
ствующий объект Workspace, который будет содержать объек Database. Если 
значение для рабочая_область не указываете? используется умолчательная рабочая область. 
    имя_ базы_ данных	Строка, содержащая имя существующей базы данных. 
    опции	Для рабочей области базы данных Microsoft Jet — True или False Если 
база данных открывается с опции = True, другой пользовател! не сможет открыть 
эту же базу данных. Открывая базу данных длв исключительного использования, 
можно повысить производитель ность работы. Значение по умолчанию — False. 
    только_для_чтения	True или False. Если True, модификации не допускаются. 
Значение по умолчанию — False. 

    строка_соединения 

    Строка, содержащая различную информацию соединения, включая пароли. 

    Примечание: Если сеть это поддерживает, в имени базы данных можно 
использовать сетево путь, например: \\server1\dir1\Db1.mdb. 

    Метод Close 

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

    dbDbl.Close 


    Глава 3. Работа с данными через интерфейс ОАО	51 


    3.2. Работа с наборами записей 

    Набор записей — совокупность записей из базы данных. Для отбора информацию 
из базы данных программным способом используется объект Recordset. 
    После открытия базы данных манипулировать данными, сохраненными в базе 
данных, можно, создавая набор записей. Для создания набора записей вначале 
объявляется пере-MeHHafl^i^^beTcrRecordset1, и затем используется метод OpenRecordset. 

    3.2.1. Объявление объекта Recordset 

    Объектная переменная Recordset ассоциируется с набором записей. Для 
привязки переменной к набору записей используется оператор Set. 
    В следующем фрагменте кода открывается база данных и создается набор записей: 

    Dim dbDbl   As   Database 
    Dim recEmployees  As   Recordset 
    Set dbDbl = OpenDatabase("C:\VB\Nwind.mdb") 
    Set recEmployees = dbDbl.OpenRecordset("Employees") 

    Синтаксис метода OpenRecordset: 

    Set o6beKm_Recordset = объект.OpenRecordset (источник, тип, опции, блокировки) 

    В следующей таблице перечислены элементы метода OpenRecordset: 

    Элемент	Описание__ __ 

    o6b6Km_Recordset   Объектная переменная, представляющая объект Recordset, 
который _________требуется открыть.____ _ ____ __________ ____ _ 
    объект	Объектная  переменная,   представляющая  существующий объект (база 
данных, таблица, запрос, другой набор записей), на базе кото-_________рого 
создается новый Recordset.____________________ источник                  
Определяет источник для набора записей. Источник — строка, 
со-___________держащая имя таблицы, имя запроса или оператор SQL.______ тип    
                      Тип   создаваемого   объекта   набора   записей   —   
dbOpenTable, __________ dbOpenDynaset или dbOpenSnapshot.___________ ______ 
опции                        Определяет, как будет открыт набор записей. 
Например, можно открыть набор записей и позволить листать его только вперед 
(опция ___________dbForwardOnly).__ __________ ______ __ _      ____ 
    блокировки	Определяет блокировку для набора записей. Например, если для 
    параметра блокировки специфицируется dbPessimistic, набор запи
    I	сей будет на время редактирования блокирован. Дополнительную 
    информацию см. в разделе "Блокировка ядра базы данных Microsoft 
    Jet" главы 4 "Расширенные возможности программирования доступа 
    _________       к данным".__________________________________ 
    Например, следующий код создает набор записей только для чтения: 
    Dim  recorders  As   Recordset 
    Set   recorders   =   dbDbl.OpenRecordset  _ 
    ("Orders",   dbOpenDynaset,   dbForwardOnly) 
    В следующем фрагменте кода для определения типа набора записей проверяется 
свойство Туре объекта Recordset: 


    52	Работа с наборами запи 

    Select Case recWhatType.Type Case dbOpenTable 
    Msgbox "Это - набор записей типа table" Case dbOpenDynaset 
    Msgbox "Это - набор записей типа dynaset" Case dbOpenSnapshot 
    Msgbox "Это - набор записей типа snapshot" End Select 


    3.2.2. Типы объектов Recordset 

    Для рабочей области базы данных Microsoft Jet имеется четыре типа наборов 
записей: tal dynaset, snapshot и forward-only. 
    Table. Набор записей типа table соответствует единственной таблице и может 
изменять В память загружается только текущая запись. Порядок записей в наборе 
определяет ст дартный индекс. Нельзя создавать набор записей типа table на 
базе присоединен таблицы. Присоединенная таблица — таблица в другой базе 
данных, которая связан; базой данных MicrosofJ"Jetr|JaHHbie присоединенной 
таблицы остаются во внешней б; данных. Информацию относительно присоединенных 
таблиц см. в главе 4'"Расширенн возможности программирования доступа к данным". 
    Dynaset. Набор записей типа dynaset — динамический набор записей, которые 
MOI содержать поля из одной или больше таблиц базы данных. Dynaset также 
позвол; изменять данные. Для каждой з"аписи набора dynaset вместо фактических 
данных хран первичный ключ. Фактические данные отбираются только при обращении 
к конкретн записи. Dynaset отражает мрд_иф_икации в существующих записях 
другими пользователя* но не отражает записи, которые добавлены другими 
пользователями. Если другой полы ватель удаляет запись после того, как dynaset 
полностью заполнен, набор будет содержа указатель на удаленную запись. При 
попытке обращения к удаленной записи будет возвр щена ошибка выполнения. 
    Snapshot. Набор записей типа snapshot может содержать поля из одной или 
больше табш базы данных. Он представляет фиксированную, или статическую, копию 
данных в состо нии на момент создания набора snapshot. Snapshot не может быть 
обновлен и не отража изменения, проведенные в данных другими пользователями. 
Snapshot полностью не запо няется, пока пользователь не переместится в конец 
набора записей. 
   Forward-Only. Набор записей типа forward-only подобен набору записей типа 
snapshot,; исключением того, что записи можно листать только вперед. 

    Сравнение типов объектов Recordset 

    Тип используемого набора записей зависит от того, что требуется выполнять 
с данными наборе записей. 
    Если для упорядочения записей будут использоваться индексы, следует 
создать набор тиг table. Поиск в таблице выполняется'ТШГНСго быстрее, чем в 
наборах типа dynaset ил snapshot. Обычно table — самый быстрый тип наборов 
записей, в то время как dynaset -наиболее гибок в работе. 
    Если используется dynaset, процессор базы данных Microsoft Jet отбирает 
только перви' ный ключ каждой записи в наборе. Если используется набор записей 
типа snapshot, проце< сор Jet отбирает всю запись. В обоих случаях 
результирующие наборы размещаются памяти (выгружаясь, в случае необходимости, 
на диск), что позволяет прокручивать набо в любом направлении. 


    Глава 3. Работа с данными через интерфейс ОАО	53 

    Когда необходимо обработать запись, в snapshot^данные доступны локально. В 
случае с dynaset, с другой стороны, чтобы отобрать полную записьТпотребуется 
выполнить отдельный запрос. С целью сокращения времени запроса процессор 
Microsoft Jet запрашивает на :    сервере кластеры строк, специфицированных их 
закладками, вместо отбора по одной записи7~~~"""~"""'~""""~ 
    Если предполагается сделать единственный проход через набор записей, лучше 
использовать набор forward-only. 

    3.2.3. Вывод записей в форме 

    При работе через интерфейс ОАО элементы управления не связаны с набором 
записей, как в случае с элементом управления Data. Необходимо явно копировать 
данные из набора записей в элементы управления в форме. 
    В следующем фрагменте иллюстрируются два способа копирования данных из 
поля базы данных в текстовое поле формы: 

    txtEmpID.Text  =   recEmployees("EmployeelD") '  Или  альтернативный  синтаксис 
    txtEmpID.Text  =  recEmployees![EmployeelD] 

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

    '   Печать   первого  поля   текущей  строки  набора   записей 
    Print recEmployees.Fields (0) 
    '   Цикл  через   все   поля   текущей  записи 
    For Each fldCurrent In recEmployees.Fields 
    PrTntT fldCurrent.Value Next 


    Управление данными Null 

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

    If IsNull(recEmployees("Notes")) Then 
    txtNotes.Text = "Примечаний по служащему нет" Else 
    txtNotes . Text = recEmpl'oyees ("Notes") End If 


    Перемещение по набору записей 

    Навигация по набору записей основана на понятии текущей записи. Текущую 
запись можно представить себе как запись списка, подсвеченную невидимым курсором. 
    Для осуществления навигации по записям набора используются методы группы Move. 
    Методы MoveFirst и MoveLast перемещают указатель текущей записи на первую 
и последнюю запись в наборе записей. 
    Методы MoveNext и MovePrevious перемещают указатель на одну запись 
относительно текущей записи или закладки. 
    Метод Move перемещает укаэатель^арпгзеделенное количество строк вперед или назад. 


    54	Работа с наборами запис 

    В следующем фрагменте показаны примеры использования метода Move: 

    recEmployees.Move   +5   '   На   5   строк  вперед 
    recEmployees.Move  -6    '   На   б  строк назад 

    3.2.4. Навигационные свойства 

    Объект Recordset обеспечивает ряд свойств, которые можно использовать при 
навигаал наборе записей. 
    Например, свойство ВОР можно использовать для недопущения ошибок 
выполнения п| перемещении выше первой записи. 

    Свойства BOF and EOF 

    Свойства BOF и EOF указывают начало или конец набора записей. Если и BOF, 
и EOF-True, в наборе не имеется никаких записей. 
    При использовании методов MoveNext или MovePrevious следует контролировать 
свойств BOF и EOF, чтобы быть уверенным, что указатель не перемещается вне 
набора записей Свойства BOF или EOF устанавливаются в True при перемещении на 
одну запись до первси или после последней записи. При попытке перейти на 
запись выше BOF или после EOI возвращается ошибка выполнения (см. также рис. 
2.4). Ниже приводится пример проверю свойства EOF: 

    recEmployees.MoveNext If  recEmployees.EOF Then 
    recEmployees.MoveLast 
    Beep End   If 


    Свойство RecordCount 

    Свойство RecordCount набора записей типа table точно отражает количество 
записей в таблице и немедленно обновляется, когда пользователь добавляет или 
удаляет запись 
    Свойство RecordCount набора записей типа dynaset или snapshot возвращает 
количество записей, к которым имело место фактическое обращение. В этом случае 
свойство Record-Count не показывает, сколько записей содержится в dynaset или 
snapshot, пока набоз записей не будет полностью заполнен. 
    Чтобы заполнить набор записей типа dynaset или snapshot, следует 
воспользоваться мете дом MoveLast, имитируя отбор последней записи. Однако 
использование метода MoveLa •• этим способом может отрицательно воздействовать 
на производительность приложения 
    Перед чтением свойства RecordCount лучше подождать, пока набор записей не 
буде заполнен по мере выполнения других разделов кода, за исключением случаев, 
когда треб\ ется знать точное количество записей сразу при открытии набора. 
    В следующем фрагменте кода в окне сообщения на экран выводится количество 
записей = наборе: 

    MsgBox   "В  наборе  записей  -   "   &  _ 
    recEmployees.RecordCount   &   "записей" 

    Примечание: Для определения количества записей в наборе можно также 
использовать функцию SQL Count. Например, строка SELECT Count(') FROM 
Employees возвращает количество записей в таблице Employees. 


    Глава 3. Работа с данными через интерфейс ОАО	___                   55 


    Свойство AbsolutePosition 

    Свойство AbsolutePosition возвращает или устанавливает относительный номер 
текущей записи в наборе записей. AbsolutePosition отсчитывается от нуля (то 
есть, первая запись в наборе — запись 0). Если для свойства AbsolutePosition 
устанавливается значение 3, текущей становится четвертая запись. Следующий код 
выведет в надписи абсолютную позицию текущей записи: 

    IblStatus.Caption  =   "Указатель   находится   на   записи  №   "   &  _ 
    rs.AbsolutePosition   +   1

    Свойство Bookmark

    Свойство AbsolutePosition записи может изменяться, по мере добавления и 
удаления записей из набора. Для возвращения к определённой записи следует 
использовать свойство Bookmark (закладка). 
    В следующем фрагменте закладка используется для сохранения позиции и 
возвращения к ней: 

    Dim varBookmark As  Variant 
    varBookmark  =   recEmployees.Bookmark   '   Сохранить   позицию  в   наборе 
    '   [Здесь   -   некоторый  программный  код] 
    recEmployees.Bookmark = varBookmark   '   Вернуться  в  сохраненную позицию 

    3.2.5. Пример создания и перемещения по набору записей 

    В этом примере механизмы ОАО используются для открытия базы данных и 
создания набора записей. 

    Создание формы Группы товаров 

    1. Создать новый проект Standard EXE. 
    2. Назвать умолчательную форму frmCategories. 
    3. Добавить к форме элементы управления, как показано на следующей иллюстрации: 

    Группа товара	|                                                       , 
    Наименование	I 
    Описание	I 
    < Назад	Вперед > 

    Рис. 3.2. Форма Группы товаров. 

    Открытие базы данных и создание набора записей 

    1. В модуле формы объявить две переменные уровня модуля, как показано в 
следующем коде: 

    Dim dbCurrent  As   Database 
    Dim  recCategories  As   Recordset 

    2. Установить ссылку на библиотеку объектных модулей Microsoft ОАО 3.5 
(команда меню Project | References). 


    56	Работа с наборами записи 


    Примечание: Библиотека объектных модулей — данные, сохраненные в файле 
.olb или вну , исполняемого модуля (.exe, .dll или .осх). Библиотека 
обеспечивает информацию, используем^ кднтроллерами Автоматики (такими, как 
Visual Basic), относительно доступных объектов Авт матики. Для исследования 
содержимого библиотеки объектных модулей и получения информащ относительно 
обеспечиваемых объектов можно использовать окно Object Browser (см. рис. 1. Т). 

    3. В процедуре обработки события Load формы Группы товаров проделать 
следующие шаг 
    а)  Открыть базу данных \VB\Nwind.mdb и сохранить результирующий объект 
базы да ных в переменной dbCurrent. 
    б) Создать набор записей, используя таблицу, и фиксировать результирующий 
объе Recordset в переменной recCategories. 
    в) Перейти к первой записи набора записей. 
    г)   Прочитать значения CategorylD, CategoryName и Description в 
соответствующие эле менты управления. Программный код этих шагов будет 
выглядеть следующим обра зом:	I 

    Private   Sub  Form_Load() 
    Set  dbCurrent  =  OpenDatabase("C:\VB\Nwind.mdb") 
    Set   recCategories   =  dbCurrent.OpenRecordset("Categories") 
    recCategories.MoveFirst 
    FillFields 
    End  Sub 

    Sub  FillFields() 
    '   Эта  процедура   заполняет   поля  формы '   и  будет  вызываться  из  
нескольких  процедур 
    txtCategorylD.Text = recCategories.Fields("CategorylD") 
    txtCategoryName.Text = recCategories.Fields("CategoryName") 
    txtDescription.Text  =   recCategories.Fields("Description") 
    End  Sub 

    4. В процедуре обработки события Unload формы закрыть базу данных: dbCurrent.Close 
    5. Протестировать правильность отображения первой записи набора. 

    Добавление кода для кнопок навигации 

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

    Private   Sub   cmdPrevious_Click()    '   Кнопка   "Назад" 
    recCategories.MovePrevious 
    If   recCategories.BOF  Then Beep 
     recCategories.MoveFirst 
    Else 
      FillFields End   
    If End  
    Sub 

    Private   Sub   cmdNext_Click()    '   Кнопка   "Вперед" 
    recCategories.MoveNext 


    Глава 3. Работа с данными через интерфейс ОАО	57 

    If   recCategories.EOF  Then 
    Beep 
    recCategories.MoveLast Else 
    FillFields 
    End If 
    End Sub 


    3.3. Работа с данными 

    После создания набора записей можно использовать методы объекта Recordset 
для модификации, добавления или удаления записей. 

    3.3.1. Изменение записей 

    Для модификации записи нужно вызвать метод Edit, заполнить поля записи 
соответствующими данными и затем вызвать метод Update: 

    Private   Sub   cmdUpdate_Click   () 
    recEmployees.Edit ' Текущая {запис.ьДзаносится в буфер редактирования 
    recEmployees("LastName")   =   txtLName.Text 
    recEmployees.Update 
    End Sub 

    Если метод Update вызывается без предварительного вызова метода Edit, 
возвращается ошШсгГвы пол нёнйя7 

    Свойство EditMode 

    Для определения, вызывались ли методы Edit или AddNew, можно 
контролировать свойство EditMode. Это может быть полезно при выполнении 
команды Save (Сохранить), когда метод Update вызывается немедленно после 
метода Add или выполняется метод Edit, сопровождаемый методом Update, если 
пользователь редактирует текущую запись. 
    В следующей таблице представлены константы, которые указывают состояние 
редактирования текущей записи: 

    Константа_____Описание___________________________________ 

dbEditNone____
Текущей операции редактирования нет._________________ 

dbEditlnProgress 
Был вызван метод Edit и текущая запись находится в буфере редактирования.

dbEditAdd	
Был вызван метод AddNew, и текущая запись в буфере редактирования — новая 
запись, которая еще не была сохранена в базе данных. 

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

    Private  Sub  cmdUpdate_Click() 
    If  recEmployees.EditMode  =  dbEditNone   Then 
      recEmployees.Edit 
    End  If 
    recEmployees("LastName") =  txtLastName.Text 
    recEmployees("FirstName") = txtFirstName.Text recEmployees.Update 
    End  Sub 


    58	Работа с данный 

    3.3.2. Добавление записей 

    Для добавления новой записи к набору применяется метод AddNew. Запись 
физически ^ будет сохранена в базе данных, пока не будет вызван метод Update. 
    Следующий код можно применить к командной кнопке "Новая запись", которая 
очища* форму и вызывает метод AddNew. 

    Sub cmdAdd_Click () 
    ClearFields 
    recEmployees.AddNew End Sub 
    Sub ClearFields () ' Очистка полей формы txtNamel.Text = "" 
    End Sub 

    Sub  cmdUpdate_Click()  ' Кнопка   "Сохранить" 
    recEmployees("LastName") = txtLName.Text 
    recEmployees.Update 
    End  Sub 

    Пользователь может сформировать новую запись и затем щелкнуть кнопку 
"Сохранить". Ко; для этой кнопки копирует значения из полей формы в набор 
записей и сохраняет запись i базе данных. 

    Отмена модификаций 

    Методом CancelUpdate можно отменить действия методов AddNew или Edit, 
чтобы изменения не были перенесены в базу данных. 
    Следующий код отменяет модификацию и регенерирует содержимое двух 
текстовых поле,

    Sub cmdCancel_Click () 
    recEmployees.CancelUpdate 
    txtLastName.Text = recEmployees("LastName") 
    txtFirstName.Text = recEmployees("FirstName") End Sub 


    3.3.3. Удаление записей 

    Для удаления текущей записи из набора используется метод Delete. Важно! 
При удален, записи этим методом никаких предупреждений или подсказок не 
выводится. Предупрежу ющее сообщение в случае необходимости можно добавить в 
подпрограмме удаления. 
    Удаление записи не обусловливает автоматического перехода указателя 
текущей записи « следующую запись. Чтобы сделать следующую запись текущей, 
нужно вызвать мещ MoveNext. В следующей процедуре текущая запись удаляется из 
набора, и указател перемещается на следующую запись: 

    Private  Sub  cmdDelete_Click() 
    recEmployees.Delete 
    recEmployees.MoveNext 
    If   recEmployees.EOF  Then	
      recEmployees'. Move Last 
    End If 
    FillFields ' Пользовательская процедура 
    End Sub 


    Глава 3. Работа с данными через интерфейс ОАО	59 

    Чтобы определить, удалены_ли все записи в наборе, можно проверить свойство 
RecordCo-unt. После удаления последней записи значением свойства RecordCount 
становится ноль. 

    3.4. Совместное использование ОАО и элемента управления Data 

    В одном приложении могут "соседствовать" ОАО и элемент управления Data. 
Это позволяет применять в приложении связанные элементы управления при 
одновременном использовании гибкости механизмов ОАО. 
    Свойство Recordset элемента управления Data возвращает объект Recordset. 
    Свойство Recordset элемента управления Data можно ассоциировать с 
созданным объектом Recordset, используя метод Open Recordset, как показано в 
следующем примере: 

    Dim  recEmployees   As   Recordset 
    Dim  dbDbl   As   Database 
    Set   recEmployees   =  dbDbl.OpenRecordset   ("Employees") 
    Set   datEmployees.Recordset   =   recEmployees 

    Следует удостовериться, что свойство DataField связанных элементов 
управления соответствует именам полей в объекте Recordset. 

    3.5. Поиск записей 

    Ниже показано, как использовать один из методов группы Find или метод Seek 
для поиска данных в наборе записей. 

    3.5.1. Поиск записей в наборах типа dynaset или snapshot 

    В наборах записей типа dynaset или snapshot найти запись можно с 
использованием методов FindFirst, FindLast, FindNext и FindPrevious. 
    Специфицируемые критерии поиска должны быть эквивалентны оператору SQL 
WHERE без ключевого слова WHERE. 
    Следующий оператор находит первую запись, которая содержит указанный номер служащего: 

    recEmployees.FindFirst   "[EmployeeelD]    =   5" 

    Если вызывается метод FindFirst или FindLast и никаких записей не найдено, 
указатель текущей записи перемещается, соответственно, на первую или последнюю 
запись в наборе. Чтобы сохранить текущую позицию записи перед вызовом метода 
Find и вернуться к этой позиции, если записей по критериям не найдено, можно 
использовать свойство Bookmark. 
    В следующем примере кода выполняется поиск определенной записи и, если 
записей при поиске не найдено, указатель возвращается к оригинальной позиции: 

    Private Sub cmdFindFirst_Click() 
    Dim varBookmark as Variant 
    varBookmark = recEmployees.BookMark   ' Сохранить текущую позицию 
    recEmployees.FindFirst "[EmployeelD] = 5" 
    If recEmployees.NoMatch Then 
    recEmployees.BookMark = varBookmark ' Вернуться 
    ' к сохраненной позиции 
    End If 
    FillFields ' Пользовательская процедура заполнения полей End Sub 


    60 Поиск записей 

    Здесь используется свойство NoMatch, позволяющее определить, нашел ли 
метод FindFirs запись. 
    В следующем примере для нахождения первого служащего с фамилией, 
начинающейся ( "А", используется символ подстановки (*). 

    recEmployees.FindFirst   "[LastName]  .Like   'A*'" 

    В качестве альтернативы использованию методов FindFirst, FindLast, 
FindNext и РТпЭРгеуТоТГз для нахождения записей в наборах dynaset или snapshot 
можно применять оператор SQL WHERE. 
    В этом синтаксисе строковую переменную следует заключать в литеральные кавычки: 

    strSQL = "SELECT * FROM Employees " &     
             "WHERE [LastName] = " & _ 
             "'" & 
             txtLastName.text & 
             "'" 
    Set recEmployees = dbDbl.OpenRecordset(strSQL, dbOpenDynaset) 

    В большинстве случаев при использовании методов Find создание набора 
записей выполняется медленнее, чем с помощью запроса SQL с предложением WHERE. 
Однако^если набор записей небольшой, метод Find может работать быстрее, чем 
обновление набора "записей запросом, так как метод Find в данном случае 
выполняется на меньшем количестве записей. 
    Например, нахождение записи в существующем наборе, который содержит только 
10 записей, будет быстрее, чем обновление набора запросом с предложением 
WHERE, при котором придется искать среди, например, 20000 записей. 

    3.5.2. Поиск в наборах типа table 

    Для поиска записи д^наборе THnaJa^Jg,.используется метод Seek. Набор 
записей в этом случае должен иметь индексГйначе использовать метод Seek нельзя. 
    В следующей таблице приведены возможные операции сравнения, которые можно 
использовать с методом Seek: 

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

    И_||	Равно специфицированному ключевому значению    I 
    ">"	Больше специфицированного ключевого значения 
    ">="	Больше или равно специфицированному ключевому значению 
    "<="	Меньше или равно специфицированному ключевому значению 
    "<"	Меньше специфицированного ключевого значения 

    Для составных индексов специфицируется несколько значений. Например, если 
имеется индекс Name, который включает поля LastName и FirstName, можно было бы 
использовать метод Seek следующим образом: 

    rs.Index = "Name" 
    rs.Seek "^"/ "Степанков", "Сергей" 

    Метод Seek позволяет находить записи в таблице быстрее, чем при 
использовании методов Find с наборами dynaset или snapshot. Однако методом 
Seek можно выполнять поиск только при наличии индексов. " --•.-.--_ 


    Глава 3. Работа с данными через интерфейс ОАО	61 


    3.5.3. Пример поиска записей в наборе 

    В этом примере пользователю будет предоставлена возможность искать 
определенные записи в таблице Categories по критериям, вводимым в поле 
Описание формы Группы товаров. Для реализации примера нужно в форму добавить 
кнопку "Поиск". 
    1. Используя функцию InputBox, запросить у пользователя строку, которая 
будет представлять часть поля Описание. 
    2. Используя значение, возвращенное из InputBox, создать строку SQL, чтобы 
выбрать подходящие записи из таблицы Categories, как в следующем примере: 
    "SELECT   *   FROM  Categories   WHERE   [Description]   LIKE   '*введенный_текст*'" 
    3. Создать набор записей, основанный „на_ 3anpoce_SQL, и присвоить 
результат переменной recCategories. Это позволит кнопкам "Вперед" и "Назад" 
работать с новым набором записей. 
    4. Если записей не найдено (recCategories.RecordCount = 0), вывести на 
экран сообщение об этом. Иначе — отобразить в форме первую запись нового 
набора записей. 
    Текст кода кнопки "Поиск" будет выглядеть следующим образом: 

    Private  Sub  cmdFind_Click() Dim  strSQL  As   String Dim  strAnswer  As  
 String 
    strAnswer  =   InputBox("Введите  любую  часть   описания",    "Поиск  
записей") '   Сформировать   строку  SQL strSQL   =   "SELECT   *   FROM  
Categories   WHERE    [Description]    LIKE   "   &  _ 
    "'*"   &   strAnswer   &   "*'" 
    Set recCategories = dbCurrent.OpenRecordset(strSQL) If 
recCategories.RecordCount = 0 Then ' Записей не найдено 
    MsgBox "Подходящих записей не найдено. Выводятся все записи" 
    Set recCategories = dbCurrent.OpenRecordset("Categories") Else      ' 

Найдена, по крайней мере, одна запись 

    recCategories.MoveFirst 
    FillFields	' Заполнить поля формы End If End Sub 

    3.6. Использование запросов и операторов SQL 

    В этом разделе обсуждается использование сохраненных запросов и операторов 
SQL для просмотра или изменения данных в базе данных. 

    3.6.1. Запрос на выборку 

    Использовать сохраненный запрос на выборку (SELECT) можно, указав имя 
запроса при вызове метода OpenRecordset объекта Database. В следующей строке 
кода создается набор записей, основанный на сохраненном запросе типа SELECT Products'!: 

    Set  recProducts  =  dbDbl.OpenRecordset("Productsl") 

    Для отбора результатов запроса можно использовать также метод 
OpenRecordset объекта QueryTJef^ В следующем примере создается набор записей с 
помощью объекта QueryDef, осмрваннрго на сохраненном запросе SELECT: 

    Dim qryProducts As QueryDef 
    Set qryProducts = dbDbl.QueryDefs("Productsl") 
    Set recProducts = qryProducts.OpenRecordset 


    62	Использование запросов и операторов S 


    Установка параметров запроса 

    Если сохраненный запрос требует параметров, установить эти параметры можно 
че| семейство Parameters объекта QueryDef: 

    Set   qryProducts.Parameters("Начальная  дата")   = _ CDate (txtBeginDate.text) 


    Создание нового запроса 

    Для создания в период выполнения нового запроса и сохранения его в базе 
данных испо. зуется метод CreateQueryDef объекта Database: 

    Set   qryNewQuery  =  dbDbl.CreateQueryDef  _ ("Queryl",    "SELECT   *   
FROM  Employees") 

    Здесь Queryl — имя сохраняемого запроса. 

    3.6.2. Запросы действия 

    Запрос действия — оператор SQL, который модифицирует, удаляет или 
вставляет запи в базу данных. Запррс.действия не возвращает каких-либо записей. 
    Для выполнения запроса действия используется метод Execute объекта Database. 
    В следующем фрагменте выполняется запрос действия и на экран выводится 
количест обработанных записей. 

    dbDbl.Execute   "Запрос_действия",   dbFailOnError Msgbox   "Этот   запрос 
 изменил   "   &  _ 
    dbDbl.RecordsAffected  & _ 
    "   записей" 

    Здесь Запрос_действия — имя сохраненного запроса. 
    Опция dbFailOnError указывает методу Execute возвращать ошибку выполнения, 
если кака либо из обрабатываемых записей блокирована или не может быть 
обновлена или удален 
    Свойство RecordsAffected возвращает количество записей, которые были 
изменены, доба лены или удалены. 
    Для выполнения запроса действия можно также использовать объект QueryDef. 
Если запрс требует параметров, установить их можно через семейство Parameters 
объекта QueryDe как показано в следующем примере: 

    Dim  qryUpdate  As   QueryDef 
    Set   qryUpdate   =  db.QueryDefs("Updatel") 
    qryUpdate.Parameters    ("Введите  увеличение  цены")   =   .10 
    qryUpdate.Execute   dbFailOnerror 
    Msgbox  qryUpdate.RecordsAffected 

    3.6.3. Компоненты языка SQL 

    Структурированный язык запросов (SQL) можно использовать как для получения 
подмноже ства строк, так и для отбора информации одновременно из двух и более 
таблиц. SQ позволяет точно специфицировать, какие записи отобрать и в каком порядке. 
    Если в операторе SQL имеется ошибка — например, если указано недостоверное 
имя поля -будет возвращено сообщение ошибки выполнения, подобное "Too few 
parameters. Expected (Слишком мало параметров. Ожидалось 2)". Если получена 
эта ошибка, следует проверит строку SQL и также проверить правильность 
перечисленных в ней имен таблиц и полей. 

    Глава 3. Работа с данными через интерфейс ОАО	63 


    Оператор SELECT 

    Оператор SQL SELECT используется для формирования запроса на выборку 
записей. Синтаксис SELECT: 

    SELECT список_полей FROM список_таблиц WHERE критерии ORDER BY список_полей 

    В следующем примере в таблице Employees отбираются поля LastName (фамилия) 
и EmployeelD (номер служащего) только тех записей, где EmployeelD — больше 5. 
Записи отбираются в порядке убывания значений EmployeelD: 

    SELECT [LastName], [EmployeelD] FROM Employees WHERE [EmployeelD] > 5 
ORDER BY [EmployeelD] DESC 


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

    Предложение WHERE используется для ограничения выбора записей. Знак номера 
(#) указывает значения литералов даты. Значения литералов даты, введенные в 
оператор SQL, должны быть в американском формате дат (например, 10 мая 1997 
года записывается как 5/10/97). 
    Ниже показаны различные примеры операторов SQL: 

    ' Базовый синтаксис WHERE 
    strSQL = "SELECT * FROM Employees WHERE [LastName] = " & "'" & 
txtLName.text & "'" 
    ' Синтаксис WHERE IN 
    StrSQL = "SELECT Employees.[LastName] FROM Employees " & "WHERE 
Employees.Sity IN ('Иваново,'Тверь')" 
    ' Синтаксис WHERE BETWEEN 
    strSQL = "SELECT [OrderID] FROM Orders WHERE " & _ 
    " ( [OrderDate] BETWEEN #01/01/96# AND #12/31/96#)" 
    StrSQL = "SELECT [OrderlD] FROM Orders WHERE " & _ 
    "([OrderDate] BETWEEN #" & CDate(txtStartDate.TEXT) & _ "# AND #" & 
CDate(txtEndDate.TEXT) & "#)" 


    Предложение ORDER BY 

    Предложение ORDER BY используется для создания набора записей в 
специфицированном порядке. Опция ASC указывает порядок возрастания, a DESC — 
порядок убывания. 
    В следующем примере из таблицы Employees возвращаются все поля; записи 
набора сортируются по полю LastName (фамилия): 

    SELECT   *   FROM  Employees   ORDER  BY    [LastName]    DESC 


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

    Для объединения данных из нескольких таблиц можно использовать операцию 
соединения JOIN. Например, если требуется вывести на экран наименования групп 
товаров и названия товаров из различных таблиц, можно соединить записи из 
таблиц Categories и Products. 


    64 Использование запросов и операторов SQL 

    Предложение INNER JOIN определяет, что, например, требуется получить 
записи, для которых номер группы (CategorylD) из таблицы Categories 
соответствует номеру группы из таблицы Products. 
    В следующем примере соединяется информация из таблицы Categories и таблицы Products: 

    strSQL  =   "SELECT  Categories.[CategoryName],    "   &   _ 
"Products.[ProductName]   "   &  _ "FROM  Categories   "   &  _ "INNER..JOIN   
Products   ON   "   & "Products.[CategorylD]   =   Categories.[CategorylD]" 

    На следующей иллюстрации показан примерный результат этой операции соединения: 

    Таблица Categories 
    CategorylD    CategoryName 
    Мясные Рыбные Концентраты 
    \ 
    Таблица Products 
    ProduetID    ProductName    CategorylO 
    1	Бекон (Югосл.)	1 
    2	Куриные куб.	7 
    3	Сосиски копченые	1 
    / 
    Inner Join 
    CategoryName      ProductName 
    Мясные	Бекон (Югосл.) 
    Мясные	Сосиски копченые 
    Концентраты	Куриные куб. 

    Рис. 3.3. Формирование и результаты операции соединения. 


    3.6.4. Использование операторов SQL в коде Visual Basic 

    Для отбора или изменения информации в базе данных операторы SQL можно 
вводить в текст кода Visual Basic. 

    Примечание: Обычно более эффективно использовать сохраненный запрос, 
нежели создавать оператор SQL в период выполнения. 

    Создание оператора SELECT 

    В период выполнения можно динамически формировать строку SQL и 
использовать метод OpenRecordset для создания набора записей, основанного на 
операторе SQL. Например: 

    Dim stSQL As String 
    strSQL - "SELECT * From Employees" 
    Set recEmployees = dbDbl.OpenRecordset (strSQL) 


    Создание оператора UPDATE 

    Оператор SQL UPDATE изменяет существующие записи. Опция dbFailOnError 
обусловливает аннулирование модификации, если в течение модификации 
возвращается ошибка Кроме того, будет вызвана процедура обработки ошибки. 
    В следующем фрагменте показано использование оператора SQL UPDATE: 

    strSQL = "UPDATE Products SET [Discount] = 1 " & 
    "WHERE [Discount] = 0" dbDbl.Execute strSQL, dbFailOnError 


    Глава 3. Работа с данными через интерфейс ОАО 65 


    Создание оператора INSERT 

    С помощью оператора INSERT можно вставить новую запись. В следующем 
примере формируется и вставляется запись в таблицу Employees: 

    strSQL  =   "INSERT   INTO  Employees   "   &  _ 
    "([First  Name],    [Last  Name])    "   &   _ 
    "VALUES    ('Сергей',    'Степанков')" dbDbl.Execute   strSQL,   dbFailOnError 


    3.6.5. Пример использования запросов SQL 

    В этом примере для получения из базы данных Nwind.mdb информации по десяти 
наиболее дорогим товарам используется сохраненный запрос. 
    1. Создать новую форму. 
    2. Сохраненный запрос Ten Most Expensive Products в базе данных Nwind.mdb 
возвращает десять самых дорогих товаров. В процедуре обработки события 
Form_Load новой формы нужно создать набор записей, основанный на этом запросе, 
и вывести на экран результаты в элементе управления DBGrid, как показано на 
следующей иллюстрации: 

    ii Десять самым дорогих товаров               РИМ! IB I 
    
    Т enM ostE xpensiveProducts	UnitPricel 
    >	Cole de Blaye	263.5 123.79' 97 
    Thuringer Rostbratwurst Mishi Kobe Niku 
    
    Sir Rodney's Marmalade	81 
    Carnarvon Tigers	62. 5 1 
    Raclette Courdavault	55: 
    Manjimup Dried Apples	53, 
    Tarte au sucre	49 3 
    Ipoh Coffee	46 
    Rbssle Sauerkraut	456 
    
    :л.-,...л.  С!   .1 

    Рис. 3.4. Запрос десяти самых дорогих товаров. 


    Для справки: Оператор (свойство SQL) сохраненного запроса Ten Most 
Expensive Products: 

    SELECT   DISTINCTROW  TOP   10/Products . ProductName  AS	.      /• 
TenMostExpe'h'siveProducts,   Products .UnitPricej                  "у^ТЛ FROM 
 Products ORDER  BY   Products.UnitPrice   DESC; 
    fl-Ъ' ^-UHi. 

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

    Private   Sub   Form_Load() 
    Dim dbCurrent As Database Dim recProducts As Recordset 
    Set dbCurrent = DBEngine.Workspaces(0).OpenDatabase("C:\VB\Nwind.mdb") Set 
recProducts = dbCurrent.OpenRecordset("Ten Most Expensive _ Products") Set 
datProducts.Recordset = recProducts 
    End Sub 

    3 Зак. 136 


    66 Использование запросов и операторов SQ1 

    Или эквивалентный код (лишние и измененные строки закомментированы): 

    Private   Sub   Form_Load() 
    Dim dbCurrent As Database 'Dim recProducts As Recordset 
    Set dbCurrent = DBEngine.Workspaces(0).OpenDatabase("C:\VB\Nwind.mdi" 'Set 
recProducts = dbCurrent.OpenRecordset("Ten Most Expensive _ Products") 
    Set datProducts.Recordset = dbCurrent.OpenRecordset("Ten Most _ Expensive 
Products") 'Set datProducts.Recordset = recProducts 
    End Sub 


    Примечание: В форму необходимо поместить скрытый элемент управления Data 
(свойство Visible ~ False) и ассоциировать свойство DataSource элемента 
управления DBGrid с элементом управления Data. Затем можно установить свойство 
Recordset элемента управления Data равный набору записей, возвращенным в 
соответствии с сохраненным запросом. 

    Тестирование приложения 

    1. В меню Project выбрать Project Properties. 
    2. Изменить Startup Object (объект запуска) на новую форму. 
    3. Запустить приложение. 
    В форме будут выведены десять наиболее дорогих товаров, как на рис. 3.4. 

    3.6.6. Пример использования параметрических запросов 

    В этом примере для получения коммерческой информации из базы данных 
Nwind.mdb используется параметрический запрос. 
    1. Создать новую форму, в которой для получения данных по продажам по 
странам в интервале дат используется сохраненный в базе данных Nwind.mdb 
параметрический запрос Employee Sales by Country, как показано на следующей иллюстрации: 
    ii. Объемы продаж по служащим и регионам 
    
    Начальная дата 11/1/95 
    Конечная дата    j 12/31/35 
    | Выполнить запрос 
    Country    LastName    [FirstName      ShippedDate    QrderlD 

    Рис. 3.5. Форма со вводом параметров запроса. 


    Глава 3. Работа с данными через интерфейс ОАО	67 

    Имена параметров для запроса — Beginning Date и Ending Date. 

    Для справки: Оператор SQL параметрического запроса Employee Sales by Country: 

    PARAMETERS   [Beginning   Date]   DateTime,    [Ending  Date]   DateTirae; 
    SELECT   DISTINCTROW  Employees.Country,   Employees.LastName, 
    Employees.FirstName,   Orders.ShippedDate, 
    Orders.OrderlD,    [Order   Subtotals].Subtotal  AS   SaleAmount 
    FROM  Employees   INNER  JOIN    (Orders   INNER   JOIN    [Order 
    Subtotals]   ON  Orders.OrderlD  =   [Order 
    Subtotals].OrderlD)   ON  Employees.EmployeelD  =  Orders.EmployeelD 
    WHERE   (((Orders.ShippedDate)   Between   [Beginning   Date]   And   [Ending 
    
    Date]) ) ; 

    2. Код кнопки "Выполнить запрос" должен выглядеть так: 

    Private Sub cmdExecute_Click () 
    Dim strSQL As String 
    Dim dbCurrent As Database 
    Dim recSales As Recordset 
    Dim qrySales As QueryDef 
    Set dbCurrent'= DBEngine.Workspaces(0).OpenDatabase _ ("C:\VB\Nwind.mdb") 
    Set qrySales = dbCurrent.QueryDefs("Employee Sales by Country") 
    qrySales.Parameters("Beginning Date") = CDate(txtBegin) 
    qrySales.Parameters("Ending Date") = CDate(txtEnd) 
    Set recSales = qrySales.OpenRecordset() 
    Set datSales.Recordset = recSales End Sub"" 


