Рассылка статей | Программирование и готовые решения
Leadersoft.ru

Рассылка статей

Программирование и готовые решения

В этом разделе сайта дается информация от https://leadersoft.ru о статьях по программированию, ответах на вопросы и др. Статьи рассылаются подписчикам и публикуются на разных сервисах. Возможно некоторая информация устарела и ссылки не работают, но эти сведения могут быть полезны, если Вы серьезно занимаетесь разработкой баз данных

Выпуск 61. Защита приложений Access от несанкционированного доступа

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

Автор. Парусников Алексей

Защита приложений Access от несанкционированного доступа.

     Часто возникает необходимость закрыть доступ пользователям к окну проекта Access.

Причины могут быть разные:

  1. Любопытные пользователи стали «лазить» по базе и удалили макрос или «отредактировали» запрос (такое возможно ведь и в случае c .mde)
  2. Вы решили продать свою программу. Чем меньше покупатель будет видеть не нужных ему элементов, тем лучше (см. п. 1)
  3.  Скрытие окна проекта позволяет сделать приложение более удобным в работе.

     Как правило, одним из признаков, что разработчик «шагнул» от начинающего к «продвинутому» чаще всего является скрытие окна приложения.
Простейшим вариантом решения проблемы может быть скрытие окна базы данных через стандартные настройки: Сервис – Параметры запуска – убрать галочку «окно базы данных». Однако, как уже говорилось в предыдущей статье, такую защиту столь же просто убрать, удерживая при запуске приложения Shift. В той же статье говорилось о неких «модулях», способных блокировать такую возможность. Познакомимся с одним из них.
     Для построения интерфейса защиты создадим два макроса: AutoExec, AutoKeys. С первым Вы уже знакомы по статье «Автолинковка и резервное копирование». Второй выполняет похожую роль – перехватывает нажатие клавиш на клавиатуре. Чтобы он их перехватывал, он должен называться AutoKeys (зарезервированное имя в Access).
     В макросе AutoExec дадим команду на запуск формы FrmStart, в макросе AutoKeys – формы ВклОткШифт. Причем форма ВклОткШифт будет запускаться при нажатии комбинации клавиш Ctrl + Q. Для этого в конструкторе зададим имя макроса - ^Q.
Зачем так мудрено? Дело в том, что если мы просто зададим запуск формы в Сервис – Параметры запуска, то при включении защиты от Shift будет открываться пустое окно Access. Мы закроем базу вообще для всех, в том числе и от себя! Для всякого замка должен быть ключ: им и выступает форма ВклОткШифт - через нее устанавливается и снимается защита. А если ее сделать скрытой (см. ниже) и запускать через макрос (комбинацию клавиш) – мы еще больше запутаем любопытных юзеров.
     Само включение происходит через свойство базы
          DBS.Properties("AllowBypassKey") = True (или False)
     В зависимости от значения пароля, введенного в поле формы «ВклОткШифт» этому свойству присваивается  True или False. Затем база закрывается (чтобы изменения вступили в силу)
     DoCmd.Quit acPrompt
     Итак, проведем испытание:
     Запускаем наше приложение, удерживая Shift – открывается стартовая форма, окно проекта закрыто. Жмем Ctrl + Q, вводим 1234, жмем «Ввод». База закрывается. Снова щелкаем по базе, удерживая Shift – открывается стартовая форма, окно проекта открыто! И так делаем, пока не надоест.
     А теперь по поводу «скрытый объект». Есть еще одна возможность заморочить пользователя. Дело в том, что в Access есть три варианта отображения объектов:

  • Нормальный режим (в окне базы данных не отображаются скрытые и системные объекты) установлен по умолчанию
  • Режим отображения скрытых объектов (в окне базы дынных не отображаются системные объекты)
  • Режим отображения системных объектов (отображаются все объекты)

      Системный объект – это встроенный объект базы данных, определенный как системный, например таблица "MSysIndexes", или системные объекты, определенные пользователем. Для определения системного объекта необходимо, что бы его имя начиналось с символов USys. Например, добавь��е к названию формы, таблицы, отчета USys – и они тут же «исчезнут», станут скрытыми, но обращаться к ним из приложения можно так же, как обычно. Чтобы увидеть их, нужно сделать следующее: 
Сервис – Параметры – Вкладка вид. В группе Отображать выберите требуемый вариант – поставьте (уберите) галочку «Скрытые объекты», «Системные объекты» и т. д.
     Многих пользователей такая «защита» способна будет сбить с толку: даже сумев открыть приложение через Shift, они с удивлением обнаружат, что какой то формы или таблицы там нет, хотя при работе она видна. Но на профессионалов, конечно, такая уловка не подействует – разумеется, они знают о свойствах объектов базы данных. Но, если учесть, что большинство любопытных чаще всего бросают попытки «ковыряться», если у них не получилось с первого раза, то такая «защита» заслуживает внимания.
     Выводы:

  1. Защиту от Shift можно применять только при условии, что база преобразована в .mde. В противном случае ее легко обойти, сделав простой импорт всех объектов в новую базу. В .mde как известно без декомпиляции (взлома) невозможно сделать импорт общих модулей, форм и отчетов – самого ценного содержимого базы.
  2. Если вы разрабатываете приложения на своей работе, то обычно лучший способ защиты – поговорить «по душам» с пользователями (и с начальством). Защита от Shift будет служить подобно реечному замку на гараже – при желании легко вскрывается, но не будь его, гараж обокрали бы еще быстрее.
  3. Обычно защиту от Shift применяют в комплексе с другими методами: привязка к компьютеру, вход по паролю, шифрование данных  и т. д. Но об этом – в следующих статьях.

Как всегда, чтобы посмотреть, как все это работает, жмем на эту ссылку…


Ответы на вопросы


Вопрос 1. Добрый день! Подскажите, пожалуйста, необходимо экспортировать отчет как снимок, как прописать это в модуле без использования диалога с пользователем.
  Ответ. DoCmd.OutputTo acOutputReport, "Отчет1", acFormatSNP, "Отчет1.snp", True
Вопрос 2. Добрый день.
Подскажите пожалуста в форме в 1 поле текст при нажатие кнопки происходит процедура "Print" в файл значения поля 1. Создается определенный файл но сохранение в кодеровке WIN а надо DOS как быть ???
  Ответ. 1. Функция StrConv конвертирует данные строки в нужный формат.
                2. С другой стороны файл для печати должен открываться в бинарном виде, иначе будут потеряны "управляющие" сисмволы.
                3. Иногда самостоятельно создают таблицу для перекодировки символов.
Вопрос 3. Есть три таблицы Artikul, Prihod, Rashod. Нужен запрос вытаскивающий все артикулы из базы Artikul для которых нет записей в таблицах Prihod, Rashod. т.е. артикулы без проводок. Начал с простого запроса. Подскажите, почему не работает:
SELECT NAIM FROM ARTIKUL LEFT JOIN PRIHOD ON ARTIKUL![KOD] = PRIHOD![IND] WHERE PRIHOD![KOLVO] IS NULL
  Ответ. В таких запросах используется ключевое слово Exists для проверки записей в других таблицах. Изучите его применение.

Вопрос 4. Добрый день.
Подскажите как быть... В форме есть три поля 1 Числовое 2 Текстовое 3 результатирующие
мне необходимо сцепить эти два поля.. например если 1 = AG а второе 00001 то чтоб поле 3 = AG00001
Подскажите очень надо

  Ответ. В свойстве "Данные" третьего поля ввести =Поле1 & Поле2
Вопрос 5. Подскажите такую хитрость.
Есть форма с несколькими текстовыми полями, по нажатию кнопки значения из этих полей обрабатываются RUN SQL (запрос на добавление), после чего поля нужно очистить, чтобы дать возможность пользователю ввести новые значения.
Обычно пользуюсь вот этим:
For Each ctrl In Me
      If ctrl.ControlType = acTextBox Or ctrl.ControlType = acComboBox Or ctrl.ControlType = acCheckBox Then ctrl.Text = ""
End If

Но - в данном примере одно из полей таблицы носит свойство Requared, соответственно VBA ругается, что поле не может содержать NULL и вываливается в ошибку
Вопрос - можно ли программно очистить поле, имеющее свойство Requared?
  Ответ. Может так сделать ctrl.Text = Null
Вопрос 6. Напишите, пожалуйста, программу. Я заполняю листы прибытия иностранных граждан, около 50 человек в день, а бланк состоит из клеточек, в которые и нужно вписывать каждую букву, Лист формата А-4 с двух сторон. Можно сделать программу, которая бы автоматически расставляла эти буквы по клеточкам? Если это возможно, пишите на ... Ирина
 

Выпуск 60. Специальные эффекты в Access

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

Автор. Парусников Алексей

Специальные эффекты в Access

 

     В этой статье рассмотрим простой пример, как сделать свое приложение Access более привлекательным. Речь конечно же пойдет о спец. эффектах. В ранее выпущенной статье «Автолинковка (автоприсоединение) таблиц при открытии приложения» уже показывался спец. эффект дефрагментация формы при закрытии – форма «рассыпалась на части». Сейчас рассмотрим процесс открытия приложения, и попробуем его «перехватить» и запустить анимированную заставку. В качестве последней будет выступать презентация PowerPoint.
     Сначала создадим саму заставку. Открываем PowerPoint, и выбираем в меню «Общие шаблоны» понравившиеся. Я, например, выбрал «Океан». Выделяем «Заголовок слайда» и пишем свой текст. Аналогично «Подзаголовок слайда». Форматирование такое же, как и Word. Теперь сделаем анимацию. Для этого выделяем текст, к которому хотим применить анимацию, и выбираем Показ слайдов – Настройка анимации – Добавить эффект. Далее, думаю, нет смысла объяснять. Осталось теперь только сохранить все это как презентацию (.pps) – например «Заставка.pps». Для запуска из Access полученного «шедевра» воспользуемся технологией ActiveX.

     Технология ActiveX обеспечивает взаимодействие приложений – Автоматизацию (Automation), при которой одно приложение управляет работой другого. Чтобы начать работу с ActiveX – объектом, нужно объявить соответствующую переменную, создать сам объект и связать переменную с объектом. Одним из способов выполнения этой работы является использование спецификатора New в операторе объявления переменных:


      Dim <имя переменной> As New <имя приложения.имя класса>


     Однако более универсальным способом создания ActiveX – объектов является вызов специальных функций CreateObject и GetObject.
Как правило, вызов этих функций помещается в правую часть оператора Set. В результате его выполнения объектное выражение возвращает ссылку на созданный объект, которая становится значением переменной. Синтаксис функции CreateObject:


     CreateObject (“Имя_приложения.Имя_класса”)


     Функция GetObject имеет дополнительный параметр путь, который задает полный путь к файлу, содержащему ActiveX – объект.
Создадим пустую форму frmHide и создадим в ней процедуру открытие формы:

     Private Sub Form_Open(Cancel As Integer)
     Dim ppApp As Object
          Set ppApp = CreateObject("Powerpoint.Application")
          ppApp.Visible = True
          ppApp.Presentations.Open CurrentProject.Path & "\Заставка.pps"
     End Sub

     Как видно, полный путь к файлу презентации был получен при помощи CurrentProject.Path и имени файла. Если теперь запустить эту процедуру, то она в свою очередь запустит Powerpoint, а он покажет нам нашу заставку «Заставка.pps». Так как нам нужно, чтобы запуск происходил при открытии приложения, то запустим ее самым простым способом – через указания имени стартовой формы, в нашем случае это будет frmHide: Сервис – Параметры запуска – Вывод формы/страницы – frmHide. Заодно уберем все галочки, кроме «контекстные меню по умолчанию» - этим мы скроем почти все стандартные меню Access и само окно приложения, чтобы не мешало. Напомню, кто не знает: чтобы самому добраться до формы для редактирования (отобразить окно Access) нужно запускать приложение, удерживая Shift. Это так называемая «защита от дурака» в простейшем исполнении. Под «дураками» подразумеваются любопытные пользователи, которым интересно, что «внутри» программы, а так же наивные разработчики, думающие таким образом провести пользователей, и закрыть им доступ к окну Access. Shift уже давно не для кого не секрет, а вот чтобы нельзя было открыть при помощи Shift существует… «защита от Shift» – специальные программные модули, препятствующие этому, которые разумеется можно взломать другими программными модулями, которые чаще всего так и называют: «Взлом защиты от Shift». И тут уже все будет зависеть от уровня разработчиков: чей модуль "хитрее", тот и выиграл. Впрочем, мы отвлеклись, речь то ведь у нас была о запуске презентации Powerpoint.
     Итак, стартовую форму указали, окно Access свернули. Запускаем приложение. Появляется заставка, идет анимация и…все. Дело в том, что в здесь не хватает еще одного важного момента: так управление было передано Powerpoint, то теперь нужно из Powerpoint запустить Access! Можно создать аналогичную процедуру в Powerpoint, ведь там тоже, как и во всех офисных приложениях, поддерживается VBA. Но мы поступим по другому: пусть Access закроет Powerpoint и запустит вторую стартовую форму (теперь уже "настоящую стартовую"). Для этого воспользуемся свойством формы Таймер и создадим еще одну процедуру в форме frmHide:

     Private Sub Form_Timer()
     Dim ppApp As Object
          Set ppApp = CreateObject("Powerpoint.Application")
          If ppApp.Visible = True Then
               ppApp.Quit
               DoCmd.Close acForm, Me.Form.Name
               DoCmd.OpenForm "frmStart"
          End If
     End Sub

     Как видно, через нее закрывается приложение Powerpoint (ppApp.Quit), закрывается сама форма frmHide (потому я ее и назвал Hide) и открывается настоящая стартовая форма приложения – frmStart. Событие Таймер происходит с заданным интервалом (мс). Он задается в конструкторе формы frmHide – Интервал таймера. Я поставил 4500. Этого хватает для «прогона» анимации.

Ответы на вопросы


Вопрос 1. В процедуре VBA создается один временный перекрестный запрос с помощью Openrecordset. Можно ли создать другой временный запрос, отбирающий данные из первого (результатом должен быть запрос на объединение первого запроса с итоговой строкой). Решить проблему через хранимые запросы не удается - в первом запросе используется сложный динамический критерий отбора, реализуемый только в VBA.
  Ответ. Наверное, лучше всего сохранять результаты запроса во временной таблице. Потом при выходе из программы ее удалять.
Вопрос 2. Добрый день, Admin!
acbShutDown = acb_apiExitWindowsEx(EWX_SHUTDOWN, 0). С помощью этой функции компьютер должен выключаться. Возможно она работает на Windows 2000 и более ранних версия, но на Windows XP она не работает. Что необходимо дописать чтобы эта функция работала на Windows XP?
P.S. Но функция смены пользователя на Windows XP работает.
acbLogOff = acb_apiExitWindowsEx(EWX_LOGOFF, 0)
  Ответ. Вот пример, который работает. http://www.mvps.org/access/api/api0016.htm
Вопрос 3. Подскажите, пожалуйста. Ломается база. Пытаюсь её восстановить, но почему-то не работает следующая строка.
DBEngine.RepairDatabase "edudate.mdb" Появляется сообщение, что операция не поддерживается для объектов этого типа. Заранее благодарен.
  Ответ. В меню есть функция восстановления базы данных, непонятно зачем ее восстанавливать из VBA. Если она постоянно "разрушается", то надо искать причину этого, а не пытаться восстанавливать ее программно.

Вопрос 4. Добрый день. Подскажите пожалуста как програмным кодом форматировать поле... В форме есть три поля 1. Date 2.Time 3. DateTime Me.DateTime = date+Time Выходит: 04.07.200617:51:33
А мне необходимо :20060704175133

  Ответ. Debug.Print Format(Me.DateTime, "yyyy") & Format(Me.DateTime, "mm") ...
Вопрос 5. Добрый день, Admin! С помощью какого кода я могу скопировать значения из поля1 в буфер обмена для последующей вставки?
  Ответ. Можно использовать ключи для имитации действия клавиатуры SendKeys "{F2}", True - выделяет поле SendKeys "^X", True - копирует в буфер

Выпуск 59. Оптимизация приложений в Access. Часть 3

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

Автор. Парусников Алексей

Оптимизация приложений в Access. Часть 3.

 

Оптимизация аппаратных средств.

     Каждый модуль Access обладает множеством возможностей для настройки приложения, но нельзя рассчитывать на какой-либо успех в работе, если компьютер, на котором выполняется приложение, является устаревшим или имеет недостаточно оперативной памяти. Microsoft в аннотации к Access указывает минимальные системные требования для компьютера. Но практика показывает, что подобных ресурсов хватает лишь на то, чтобы программа что называется «задышала», а вот чтобы нормально работать, требования надо серьезно увеличивать.

  • Если выбирать между модернизацией процессора и установкой ОЗУ большего объема, следует выб­рать ОЗУ. Как известно, памяти много не бывает, а Access, как и любая другая серьезная база данных, для своей нормальной работы требует значительного объема памяти.
  • Необходимо регулярно очищать корзину и удалять временные файлы (особенно файлы Internet и электронной почты!). Базы данных требует значительного объема дискового пространства, а эти файлы могут поглотить массу места еще до того, как пользователь успеет это осознать.
  • Не лишним будет регулярное сжатие базы данных. Начиная с версии Access 2000 это можно настроить автоматически. Сервис – Параметры – Вкладка общие – Сжимать при закрытии. Дело в том, что в Access есть своя «корзина», и при удалении записи не удаляются, а помечаются как удаленные (знаком *), то есть «становятся в очередь на удаление». Так же в Access есть скрытые системные таблицы, в которых хранятся параметры форм. Форму удалили, а параметры остались. Потом они конечно удалятся, но это потом. А пока мы имеем «мусор» - ненужные данные. По этой причине часто начинающие разработчики удивляются, когда после удаления (стирания) записей из базы ее размер почему то не очень изменяется. Особенно это касается полей типа OLE, где хранятся рисунки формата .bmp. Забили базу рисунками, она «раздулась» до нескольких десятков мБ, удалили рисунки – а размер остался. Выход – сжатие базы (дефрагментация). Так же замечено, что при регулярном сжатии база становиться более устойчивой - реже «слетает».
  • Рекомендуется отключить Journal (Журнал) в Outlook. Журнал Outlook генерирует запись при каж­дом запуске и выходе из приложения. Этот журнал может стать очень большим и поглощать дис­ковое пространство и процессорное время, необходимые приложению.
  • Необходимо использовать освободившуюся оперативную память. Приложения очень хорошо погло­щают оперативную память, но неохотно ее освобождают. Не стоит запускать несколько приложений одновременно. Вообще, желательно, чтобы машина, на которой установлена база, была ориентирована в первую очередь на работу с базой – не стоит ставить на нее ненужные программы. Особенно это касается машин, которые используются в качестве «серверов» при файл-серверном построении сетевой базы. А то иной раз частенько на таком «сервере» работают как на клиенте, да еще запускают какое-нибудь «навороченное» приложение, да и не одно, а потом удивляются, почему вдруг сетевая база стала «тормозить».
  • У многих разработчиков компьютеры намного более быстродействующие, чем у пользователей. Это следует учитывать при разработке приложения, и протестировать его на более «слабой» машине. Так же, если Вы разрабатываете приложение, не зная точно, какой Office будет у пользователя, будет не лишним протестировать его на всех версиях, на которых оно предположительно может быть установлено. Лучше всего установить на машине несколько операционных систем и на каждой соответствующую версию Office – это даст наиболее «чистый» результат тестирования. Так же если Вы переустанавливаете Office, то следует помнить, что многие «серьезные приложения» приложения «мусорят», если не сказать больше, в системный реестр. И чем «серьезнее» приложение, тем больше. Поэтому после удаления программы, не лишним будет прочистить реестр, например, при помощи RegCleaner, а то и вручную через RegEdit.exe, и «добить» что осталось через Norton Utilities Integrator.

Оценка производительности

     Естественным является желание посмотреть, что же дала оптимизация, насколько быстрее стало работать приложение. API Windows предлагает таймер, который отслеживает время в миллисекундах. Функция timeGetTime() измеряет промежуток времени с момента запуска Windows. Поскольку она использует другой аппаратный счетчик, то возвращает время с точностью до миллисекунды. Используя timeGetTime(), можно вставить строку кода до и после выполнения любой критической опе­рации и получить очень точное измерение времени, которое понадобилось для завершения действия. Для использования вызова API необходимы две вещи: объявление функции и глобальная переменная для хранения времени запуска таймера. В разделе объявлений модуля необходимо ввести следующие три строки:

     Private Declare Function a2ku apigettime Lib "winmm.dll" Alias "timeGetTime" () As Long
     Dim Ingstartingtime As Long

Затем можно создать подпрограмму для запуска часов и функцию остановки часов.

     Sub a2kuStartClock()
          Ingstartingtime = a2ku_apigettime()
     End Sub
 

     Function a2kuEndClock()
          a2kuEndClock = a2ku_apigettime() - Ingstartingtime
     End Function

А вот пример использования этих функций:

' Определение времени выполнения запроса

     Sub QueryTimer(strQueryName As String)
     Dim db As Database
     Dim qry As QueryDef
     Dim rs as Recordset
          Set db = CurrentDb()
          Set qry = db.QueryDefs(strQueryName)

' Запуск часов. a2kuStartClock
          Set rs = qry.OpenRecordset()
' Остановка часов и вывод результата в окне отладки.
          Debug.Print strQueryName & " executed in:  " & a2kuEndClock & " milliseconds"
          rs.Close
     End Sub

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

     Хотя определение времени играет не последнюю роль, но только на основе этой информации невоз­можно судить о производительности приложения во время разработки. Как уже говорилось выше, обычно у разработчика более высокоскоростной компьютер, стало быть тестирование не совсем «чистое», ведь таймер не может сообщить, как будет выполняться приложение на другом компьютере с меньшим объемом памяти или более низкоскоростным диском. Для более точного наблюдения за приложением можно воспользо­ваться другой недокументированной функцией — ISAMStats. Функция ISAMStats не документирована и не поддерживается, поэтому информацию, которую она сообщает, можно использовать лишь в качестве общих указаний. При запуске функция производит измерение шести важных оказывающих влияние на про­изводительность операций. Она подсчитывает все обращения чтения и записи на диск, обращения чтения в кэше, опережающего чтения, размещения блокировок и освобождения блокировок. Данную функцию можно использовать в Access 2000 и даже в более ранних версиях Access. Синтаксис функции очень простой:

     DBEngine.ISAMStats(опция,[reset])

     Существует шесть возможных значений для аргумента опция:

Значение аргумента опция
Описание
0

Запись на диск

1

Чтение с диска

2

Чтение из КЭШа

3

Чтение из кэша (опережающее чтение)

4

Размещение блокировки

5 Освобождение блокировки

     Необязательный параметр переустановки позволяет переустановить отдельные счетчики на нулевое значение. Чтобы воспользоваться данной функцией для оценки производительности, необходимо либо вычесть одни показания из предыдущих, либо переустановить счетчик на нулевое значение и затем вы-поднять оценку. Рассмотрим один из способов использования функции ISAMStats для оценки производительности.

     Sub PrintStats()
     Dim i As Integer
          Debug.Print
          Debug.Print "Disk Reads:  " & ISAMStats (0, Falsa)
          Debug.Print "Disk Writes:  " & ISAMStats(1, False)
          Debug.Print "Cache Reads:  " & ISAMStats(2, False)
          Debug.Print "Read-Ahead Cache Reads:  " & ISAMStats(3, False)
          Debug.Print "Locks Placed:  " & ISAMStats(4, False)
          Debug.Print "Locks Released: " & ISAMStats(5, False)
          For i = 0 To 6
               ISAMStats i, True
          Next
     End Sub

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

Создание связей между таблицами в конструкторе (схема данных).

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

Повышение скорости выполнения запросов.

     Вот основные рекомендации:

  • Рекомендуется создавать индексы для всех полей, которые будут использованы для определения критерия отбора.
  • В результирующем наборе не следует отображать какие-либо лишние столбцы. Обработка и отобра­жение каждого столбца занимает дополнительное время.
  • Рекомендуется воздерживаться от употребления сложных выражений в запросах.
  • Следует избегать функции IIF() (немедленное IF). IIF() оценивает и истинное, и ложное значения перед тем, как выдать результат. Если выполнять данную операцию для каждой записи, это может сильно повлиять на производительность.
  • По возможности следует пользоваться оператором Between для уменьшения количества строк в ре­зультирующем наборе вместо операторов "больше чем" и "меньше чем".
  • Рекомендуется по возможности поэкспериментировать с подчиненными запросами вместо исполь­зования объединений или сложных условий OR. Оптимальный выбор зависит от многих дискретных факторов, и только эксперимент поможет решить, какой подход использовать.
  • Вместо SQL-операторов в коде рекомендуется использовать сохраненные запросы с параметрами. Jet уже скомпилировал запросы с параметрами и создал для них план выполнения. Использование скомпилированных и сохраненных запросов устраняет необходимость оценки и оптимизации SQL-строки. Access компилирует SQL-строки, использующиеся в качестве источника записей или источника строк для форм, отчетов или элементов управления, поэтому они остаются нетронутыми. Поэтому рекомендуется всегда использовать скомпилированные (сохраненные) запросы.

Ответы на вопросы

Вопрос 1. Добрый день. Подскажите, есть в форме путь к файлу xls. Какую процедуру навесить на кнопку для открытия ?
  Ответ. Application.FollowHyperlink strPath
Вопрос 2. Проконсультируйте, пожалуйста как в ACCESS создать с помощью SQL табличку в которой будет строковое поле которое допускает ввод пустых значений (т.е. свойство "Пустые строки" будет иметь значение "Да")
  Ответ. 1. ALTER TABLE Сотрудники ADD COLUMN Примечания TEXT(25)
                2. ALTER TABLE Сотрудники ADD COLUMN Примечания TEXT(25) NOT NULL
Вопрос 3. =Sum(IIf([Содержание заявок подчиненная форма].[Form]![Валюта цены]="EUR";[Курс EUR]*[Содержание заявок подчиненная форма].[Form]![Цена];0))
Пока было без Sum, то выбирало нормально.
Добавил Sum теперя пишет #Ошибка
Почему так?
  Ответ. Sum работает только в запросах на группировку.
Вопрос 4. Помогите по ACCess!
Есть две таблицы: Лицо(один), Перемещение(многие), отношение "один-ко-многому". Хочу создать форму на основе запроса по этим
таблицам для отображения (последнего места нахождения), ввода нового лица и корректировки. Проблема в запросе - не могу правильно
отобрать записи. Получается только в итоговом запросе, но он не позволяет сделать ввод и корректировку. В условие отбора прописываю
функцию DLast ( =DLast("поле";"таблица") ) Выдает "Ошибка синтаксиса(Пропущен оператор)". Помогите, нигде не могу найти пример этой функции, чтобы найти ошибку. Заранее спасибо.
  Ответ. Надо проверить в запросе поля, таблицы, знаки препинания. Таблицы и поля лучше держать в квадратных скобках.
Вопрос 5. Ребят, подскажите, как обновить подчиненную форму в открытой из другой формы?
  Ответ. Forms("Имя формы").Controls("Подчиненная форма").Form.Requery

 

Выпуск 58. Оптимизация приложений в Access

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

Автор. Парусников Алексей

Оптимизация приложений в Access. Часть 2.

Форма (модуль) … : добавьте инструкцию Option Explicit.

     Во многих языках программирования переменные должны быть объявлены обязательно, и эти объявления используются компилятором, чтобы зарезервировать память для переменных. В то же время в VBA объявление переменных не является обязательным, и допускается использование неописанных переменных.
     Когда-то среди программистов, использующих разные языки программирования, велись споры по поводу необходимости обязательного описания всех переменных. Действительно, кому охота занудно прописывать каждую переменную Dim i As Integer, j As Integer и т. д. Однако другой стороны, один из самых опасных источников труднообнаруживаемых ошибок в языках программирования, допускающих применение неописанных переменных, это опечатки в написании имен переменных. Такие опечатки истолковываются транслятором как появление еще одной, новой переменной, отличной от ранее использовавшейся, и не воспринимаются как ошибки. Порой для обнаружения такой опечатки требуется время, во много раз превосходящее то, которое потребовалось бы на явное описание всех используемых в программе переменных.
     В VBA решено было так — предоставить разрешение этой дилеммы самому программисту. В этом языке имеется оператор Option Explicit. Если Вы начнете свой модуль с этого оператора (он должен быть расположен в самом начале модуля, до того, как начнется первая процедура этого модуля), то VBA будет требовать обязательного объявления переменных в этом модуле и генерировать сообщения об ошибке всякий раз, как встретит необъявленную переменную. Кроме того, чтобы это требование стало обязательным для всех модулей без исключения, можно установить параметр Require Variable Declaration (Явное описание переменных) на вкладке Editor (Редактор) диалогового окна Options (Параметры) редактора VBA.
     Если же не использовать Option Explicit и переменные не объявлять, то переменной по умолчанию присвоится тип Variant. Вот на это и ругается мастер – на переменную общего типа.  Дело в том, что такие переменные занимают больше места в памяти по сравнению с данными других типов, а главное, они не могут быть обработаны немедленно. Когда компилятор обнаруживает вариантную переменную, то сначала должен выяснить ее настоящий тип, преобразовать в него данные, а уж потом делать с ними вычисления. На все это требуются время, и если в простых приложениях, где нет сложных вычислений,  можно применить переменные типа Variant, то в сложных, особенно где происходит много вычислений с двойной точностью, разница по скорости вычислений может отличаться в разы. Вообще, у профессиональных программистов VBA считается хорошим тоном прописывать в заголовке Option Explicit – тем самым вы избежите не только вышесказанные проблемы, но и многие другие, касающиеся не только производительности.

Форма…: используйте меньшее число элементов.

     Это пожалуй, самая часто встречающаяся рекомендация мастера. Действительно, многие начинающие разработчики стараются «навешивать» на одну форму множество элементов: полей, полей со списками, подчиненных форм, вкладок на которых опять поля, подчиненные формы и т. д. Все это делается с целью сделать приложение более «наглядным», чтоб «все сразу было видно». Чем это чревато? Потерей скорости загрузки такой формы.
     Дело в том, что обычно каждый элемент (контрол) имеет связанный с ним источник данных, который нужно «подцепить» при открытии. Не зря ведь существует столько событий формы, связанной с ее запуском: открытие, загрузка, включение, получение фокуса – как раз для того, чтобы управлять процессом «подцепления» данных, когда нет возможности использовать меньше элементов. Рассмотрим обычные ошибки начинающих разработчиков и способы из устранения.

1.     Число записей в форме

     Бывает так, что прежде нормально грузившаяся форма, вдруг начинает «тормозить». Одной из причин может быть то, что источник данных такой формы – вся таблица, которая, как известно, заполняется. Все нормально, пока данных там порядка нескольких тысяч, но стоит поработать с базой несколько лет - и количество записей (особенно в бухгалтерских программах) может перевалить за несколько сотен тысяч. И при работе такая форма заметно потяжелеет, особенно, если она сделана как подчиненная на основной. Как выход – использовать в качестве источника данных формы запрос, где количество за��исей можно ограничить, например, периодом дат (текущий год, месяц).

2.     Подчиненные формы.

     Старайтесь не использовать больше одной - двух подчиненных форм на основной. Помните, что при открытии основной формы последовательно происходит загрузка в саму форму и подчиненные, что требует времени. Особенно это касается форм с вкладками, на которых расположены подчиненные. Если отказаться от такого дизайна не получается, тогда применяйте динамическую привязку данных к подчиненным формам при переходе по вкладкам, используя свойство формы RecordSource:

Forms!frmGlawn!frmSub.Form.RecordSource = "Имя запроса"

     Еще один признак начинающего разработчика – наличие на форме множества скрытых полей и подчиненных форм, которые используются либо для вычислений, либо в случае с формами – как своеобразный фильтр для получения значения поля из таблицы по заданному параметру (значению в другом поле). Такой способ действительно прост: источником данных такой формы делается запрос, в котором есть ссылка на поле главной формы Forms!frmGlawn!Поле1. В нужный момент делается обновление формы (Requery) и считывается значение из подчиненной формы. Но если таких форм прицеплено несколько, и каждый раз их обновлять – это может заметно сказаться на скорости работы приложения. Лучше вместо этого воспользоваться стандартной функцией:

DLookup("Имя поля таблицы или запроса", "Имя таблицы или запроса", "[Имя поля таблицы по которому фильтруем] = значение фильтра") – см. Help

3.     Поля со списками.

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

Поле1.RowSource = "ИмяЗапроса"

     Мы рассмотрели только самые простые рекомендации, которые смог определить мастер. Можно еще добавить:

1.     если внимательно проанализировать интерфейс, то может оказаться, что некоторые подчиненные формы не несут важной в данный момент информации и их можно загружать уже после загрузки основной, например нажатием на кнопку или двойным щелчком по полю.
2.     если убрать галочку Сервис – Параметры – Вкладка общие – Отслеживать автозамену имен, то приложение будет работать быстрее

Ответы на вопросы

Вопрос 1. Как программно написать сжатие БД при выходе из нее?
  Ответ.1. Нельзя сжимать файл в котором работаешь. В настройках есть функции сжатия, которые срабатывают при закрытии программы.
              2. Из VBA можно сжать базу. В справке надо посмотреть фунцию CompactDatabase
Вопрос 2. База в открытой папке. Разделена мастером разделения (то бишь в одном месте таблицы, в другом - формы и запросы). Многопользовательский режим разрешен (режим открытия - общий). Когда пользователь хочет залезть в базу, он залезает в эту открытую папку и собственно пользуется ею. Но, как только пользователей становится 2, то пишет, что слишком большое количество юзеров. Все окна в формах заблокированы, доступа к ним нет, т.е. в принципе база открыта практически только на чтение. Хотелось бы, чтобы все таки можно было открывать большому количеству пользователей и чтобы они могли только читать, а я (как администратор) - исправлять.
Я понимаю, что надо сделать так:
- файл mdb с данными (таблицами) на компьютере "сервере" в папке с открытым общим доступом
- файлы mdb или mde с формами и запросами на компьютерах пользователей (естественно, они должны быть связаны с файлом данных) - у каждого пользователя свой.
:shock: :shock: Как связать эти файлы. Я пыталась воспользоваься F1, но что-то ничего не поняла. Там написано, что надо создать макрос, а я с макросами не дружу совсем. Не поможете?
  Ответ. Есть диспетчер связанных таблиц (Сервис-Служебные программы), он позволяет привязать интерфейс к таблицам на сервере. Есть также пример на сайте как это сделать из VBA.
Вопрос 3. Подскажите пожалуста при помощи какой фонкции проверить пустое поле или нет ?
  Ответ. if nz(fld,"") = "" then
Вопрос 4. Привет всем!
Может кто нибудь подскажет как заставить работать скроллинг в VBA редаторе Offica. При работе в нем это очень облегчило бы жизнь.
  Ответ. http://www.gasanov.net/VBScroll.htm тебе поможет и мышек не надо...
Вопрос 5. Как задать иконку какую-нибудь на форму? или вообще убрать стандарную аксесовскую, но чтобы остался крестик закрытия..скажите пожалуйста! И еще..создал кнопку удаления мастером, жму на нее, у меня аксесс просит подтверждение удаления, как избавиться от него?
  Ответ.  1. В меню Сервис-Параметры-Правка и Поиск

 

Выпуск 57. Оптимизация приложений в Access

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

Парусников Алексей

Оптимизация приложений в Access. Часть 1.

     Иногда при разработке приложений возникает ситуация, когда еще недавно работающая программа вдруг начинает «тормозить», запросы, ранее выполнявшиеся что называется «в полсекунды» начинают зависать и т. д. Попробуем разобраться, что может быть причиной этому и составить общие рекомендации по поводу оптимизации приложений. Как известно, в Ассеss есть мастер, позволяющий провести беглый анализ приложения. Жмем Сервис – Анализ – Быстродействие – Все типы объектов – Выделить все – ОК. Итак, что же обычно мы видим?

Приложение не сохранено в полностью откомпилированном виде.

     Это означает, что нужно преобразовать базу из формата .mdb в формат .mde. Делать это нужно закрыв приложение и запустив Access. Далее находим Сервис – Служебные программы – Создать mde файл. Причем, если приложение создавалось в другой версии Access, ее сначала следует преобразовать в текущую: Сервис – Служебные программы – Преобразовать базу данных, а уже потом делать из нее .mde. В результате получится преобразованная копия базы (.mde).
     Начиная с версии Access 2000 поддерживается совместимость версий «снизу вверх». Это означает, что .mde файл сделанный в Access 2000 будет работать в более поздних версиях, а вот наоборот – не будет. Дело в том, что от версии к версии меняется формат .mde файла, и то, что понятно для Access 2002 – китайская грамота для Access 2000.
     Преобразованная таким образом база будет работать быстрее, потому, что скомпилируются все модули приложения. Но в результате Вы уже не сможете редактировать формы и отчеты в режиме конструктора. Преобразовать .mde в .mdb обратно стандартными средствами Access так же будет невозможно. Об этом следует помнить и всегда сохранять исходную (.mdb) версию базы. Стандартными нельзя, но… Как известно, нет такого замка, которого невозможно открыть. Теоретически (да и практически) можно декомпилировать базу (и не такие программы «ломали»), но это уже относится к области «хакеров», и по понятным причинам в данной статье освещаться не будет. Остановимся лишь на одном интересном для начинающих разработчиков Access вопросе.
     Часто программисты, ранее разрабатывавшие приложения в других средах (Си, Delphi, FoxPro и др.) спрашивают: «Как сделать в Access .exe? (исполняемый файл, способный работать сам по себе, без Access)». Ответ всегда один: никак.  И причин тому несколько:

  1. де��о в том, что изначально язык VBA разрабатывался как вспомогательное средство создания офисных документов, его определение в переводе так и звучит: «Визуальный язык программирования для приложений (офисных)», и само собой подразумевалось, что созданные с его помощью приложения будут работать только под управлением родительского приложения, в данном случае Access. Поэтому при компиляции в Access создается не машинный (двоичный) код, а так называемый p-код. Р-код близок к машинному, но программа в Р-коде не может быть непосредственно выполнена процессором. Преобразование (трансляция) в двоичный код происходит во время выполнения программы. Это сделано для отладки приложений. Без p-кода невозможно было бы прерывать выполнение приложений, редактировать тексты программ и снова продолжать выполнение. Кстати, аналогично ведь происходит и в других средствах разработки. Везде присутствует «промежуточная» трансляция.
  2. хотя Microsoft постоянно совершенствует VBA, но «отдавать» его как средство для создания независимых коммерческих приложений пока не собирается. Очевидно, что в этом случае компания может потерять значительную часть прибыли от реализации новых версий Office, ведь для того, чтобы к примеру запустить приложение Access, созданное в более новой версии Access (с новыми возможностями), необходимо установить (купить) эту новую версию. Вообще, эта тема – коммерческое распространение приложений Access заслуживает отдельной статьи, и я постараюсь в одной из следующих статей подробно остановиться на этом. Пока скажу лишь, что проблема эта вполне решаема и многие успешно реализуют свои программы.

Таблица … : свяжите с другими таблицами базы данных

     Как следует из ответа мастера, в базе есть не связанная с другими таблица. В некоторых случаях это оправдано – речь идет о так называемых «служебных» таблицах, в которых заносятся какие либо параметры приложения. В этом случае ответ мастера можно проигнорировать. А вот если речь идет о справочной таблице, откуда подставляются значения (ключи) в основную таблицу, то к мастеру стоит прислушаться. Дело в том, что Access относится к самому распространенному сейчас классу реляционных СУБД, основным признаком которого является то, что данные распределены в базе, словно одежда в платяном шкафу: носки в ящике для носков, штаны в ящике штанов и т. д. Помимо этого они еще и связаны между собой логическими связями. Например: Комплект верхней одежды – это штаны + рубашка + носки. Потянув за такую «нитку» машина может вытащить весь комплект и определить его состав, а это значит, что если вы зададите ей искать головной убор (шапку) – в раздел для верхней одежды она уже не полезет, стало быть, шапка найдется быстрее. Вот это мастер и пытается нам втолковать. То есть такая «правильная» база, это как хороший шкаф у аккуратного хозяина, в котором есть отделы, в отделах секции, в секциях ячейки. В свою очередь «плохая» база – это сундук, где все свалено в одну кучу – попробуй, найди там чего. Вообще, лично я, когда хочу определить уровень разработчика Access, первым делом смотрю, как связаны у него таблицы, как распределены данные. Причем иногда оказывается, что вообще никак. Дескать, пусть сам Access разбирается, что и с чем там связано.
     Установка связей между таблицами делается в специальной вкладке Схема данных. Жмем сервис – схема данных или просто правой кнопкой в окне проекта и в контекстном меню выбираем схема данных. В открывшемся диалоговом окне снова жмем правой кнопкой и выбираем Добавить таблицу. В появившемся диалоговом окне раскрываем вкладку таблицы и выбираем нужные: щелкаем дважды по названиям и таблицы появляются в окне конструктора. Закрываем окно со списком таблиц.
     Для установки связи между таблицами нужно выбрать в одной из таблиц (главной) таблице поле для связи, нажать левую кнопку мыши и перетащить поле во вторую таблицу. Отпустить левую кнопку мыши над тем полем подчиненной таблицы, с которым устанавливается связь. После этого появится диалоговое окно Изменение связей. Здесь можно поставить автоматическую проверку ссылочной целостности – флажок обеспечение целостности данных, и если требуется, каскадное обновление/удаление связанных полей. Я практически всегда включаю флажок обеспечение целостности данных, потому, что если этого не сделать, то в скором времени Ваша база забьется «мусором» - появятся ни с чем не связанные записи.

Таблица … : добавьте индекс для поля…

     Индекс - это упорядоченный список значений и ссылок на те записи, в которых хранятся эти значения. Чтобы найти нужные записи, СУБД сначала ищет требуемое значение в индексе, а затем по ссылкам быстро отбирает соответствующие записи.
     В каждой таблице должен быть так называемый ключ – идентификатор записи. Исключение составляют лишь специальные служебные таблицы, обычно с параметрами приложения, в которых, как правило, обычно не много записей. Чаще всего ключами делают поля типа «счетчик» - числовое поле с уникальными значениями. Но мастер просит проиндексировать еще одно поле в таблице, хотя в нем уже есть ключ-счетчик. Дело в том, что видимо в данном случае в каких либо запросах часто используется это поле, и по нему вытягиваются другие записи.  Если поле проиндексировать, то поиск (выполнение запроса) пойдет быстрее.
     Однако индексирование может привести и к обратному эффекту. Дело в том, что при добавлении и удалении записей или при обновлении значений в индексном поле требуется обновлять индекс, что при большом количестве индексов в таблице может замедлять работу. Поэтому индексы обычно рекомендуется создавать только для тех полей таблицы, по которым наиболее часто выполняется поиск записей. Индексировать можно любые поля, кроме МЕМО-полей, полей типа Гиперссылка и объектов OLE.
     Итак, открываем таблицу в режиме Конструктора, выбираем поле, для которого требуется создать индекс, далее вкладка Общие и выбираем для свойства Индексированное поле значение Да (Допускаются совпадения) или Нет (Совпадение не допускаются).
     Индекс может быть так же и составным. Обычно такую индексацию применяют, когда нужно задать уникальные значения для группы полей. Например, в одном поле номер договора, в другом идентификатор. Нужно, что в таблице разрешалось сохранение данных типа 12П, 12Р, 12Н, а вот два раза 12П не разрешалось – сразу должно появиться сообщение о повторяющихся записях. Делается это просто: снова открываем таблицу в режиме Конструктора, на панели инструментов Конструктор таблиц жмем кнопку Индексы. В первой пустой строке поля Индекс вводим имя индекса, затем в поле Имя поля жмем на стрелку и выбираем первое поле, для которого необходимо создать индекс. В следующей строке поля Имя поля указываем второе индексируемое поле, причем для данной строки поле Индекс должно оставаться пустым. Далее повторяем это для всех полей, которые необходимо включить в индекс (разрешается включать в индекс до 10 полей).

продолжение в следующем выпуске...


Ответы на вопросы


Вопрос 1. Добрый день.
Подскажите пожалуйста такая проблема
в отчете есть три выводимых поля
1 Сума
2 Валюта
3 Еквивалент

Необходимо чтоб если поле валюта было например UAH то поле еквивалент равнялось [SUMA]\5.05, Если USD то поле еквивалент равнялось [SUMA] ?
  Ответ. Можно использовать IIF([Валюта]='USD';[SUMA];[SUMA]\5) в запросе отчета.
Вопрос 2. Перед репликацией забыл дать доступ одному пользователю к одной таблице. Вроде все просто: так как дать доступ в реплике нельзя (так "оно" сообщает), открывается основная реплика и там дается доступ. НО! В основной реплике доступ есть (можно "поставить-снять"- нет проблем) НО в реплике он (доступ) не появляется. И основная реплика и реплика "пользуются" одним и тем же Secured.mdw. Синхронизация не приводит к "появлению" доступа. Я- администратор (начинающий) БД и владелец всех объектов БД. Win2000, MSAccess-2003 Может есть идея- в "чем собака порылась"? Заранее благодарен. С уважением, Владимир
  Ответ. При репликации в каждую таблицу mdb добавляются 4 ключевых поля, сам же файл mdw не реплицируется. Туда ничего не добавляется. Возможный выход - создаете новую базу данных mdb и из главной реплики туда загружаете все таблицы. Далее удаляете все импортированные оля репликации. Далее нужно повторить все действия по репликации и защите базы данных.
Вопрос 3. Acсess, Офис 2000.
1) Как в программ�� сделать КРАСИВО разворачивание окон?
Команда RunCmd.Maximize поставленная на событие Открытия формы делает это не очень: появляется зауженная форма, потом она разворачивается. Эта дерготня утомляет...

2) И как забороть такой негативный эффект: при переходе кнопками на панели задач по окнам открытых форм все они склонны сократиться в размерах (из сосотояния Максимайз :-)), если среди них есть хотя-бы одно маленькое (не всплывающее и не модальное).

Как закрепить "развернутое" состояние нужных форм?
  Ответ. Надо отключить вывод изображения и дать программе вычислить новые размеры окна "математически"
Me.Painting = False
Вопрос 4. Добрый день подскажите пожалуйста каким образом програмно открвать файлы xls, либо doc без прав редактирования ???
  Ответ. У каждого файла есть защита на уровне ввода пароля. Там и определяется доступ к объектам. Например, у Excel делается так:
ActiveSheet.Protect DrawingObjects:=True, Contents:=True, Scenarios:=True
Вопрос 5. Добрый день подскажите как быть
Есть общая форма где в поле типа DATE пользователь меняет дату и в подчененной форме фильтруются записи по выбраной дате... Использую запрос в подчененной форме с фильтром [Forms]![Zagalna]![Me_date]- отказывается фильтровать...
  Ответ. Дату в запросах переводят
1. Для Access в формат: #mm.dd.yyyy#
2. Для SQL Server в формат: ddmmyyyy

Выпуск 55. Материалы для начинающих

Данная статья ориентирована на разработчиков Access, уже имеющих опыт программирования в Access

Парусников Алексей

Создание пользовательских классов в Access.

Общие определения

Сначала определимся: что же такое объект в понимании объектно-ориентированного программирования. В самом простом (житейском) понимании объектэто какая-то вещь, и так же как и в окружающем мире, объект имеет свойства ("камень твердый") и операции, которые выполняются над этими свойствами (камень можно разбить). Почти так же обстоит дело и с объектами в мире программ. Например, в качестве объекта можно рассматривать файл, в качестве свойства — его размер или имя, а в качестве операций — чтение или запись. Итак, есть объект и есть свойства — что же является операцией? Любая пользовательская процедура или функция, изменяющая или просто работающая со свойствами объекта. Например, процедура инициализации полей записи или процедура вывода на экран значения полей. Возникает естественное желание объединить данные и способы их обработки в одно целое, так чтобы было ясно, какие процедуры предназначены для обработки определенных данных. Таким образом, мы вплотную подошли к центральным понятиям объектно-ориентированного программирования — понятиям инкапсуляции и класса.

Инкапсуляция и классы

Объектно-ориентированное программирование (далее ООП) - наиболее популярная в настоящее время методология программирования, являющаяся развитием структурного программирования. Центральной идеей ООП является инкапсуляция, т. е. структурирование программы на модули особого вида, объединяющие данные и процедуры их обработки,  причем внутренние данные модуля могут быть обработаны только предусмотренными для этого процедурами. В VBA принято название модуль класса или просто класс. Каждый класс имеет внутреннюю часть, называемую реализацией, и внешнюю часть, называемую интерфейсом.
Класс таким образом, представляет собой новый тип данных, позволяющий создавать новые переменные этого типа - объекты (иногда их еще называют экземплярами класса). Объект состоит из элементов, которые могут быть как собственно данными, т. е. значениями определенного типа данных, так и  функциями,  реализующими   операции  над  элементами - данными.  Элементы - данные   называются  свойствами  класса,  элементы - методами класса. Такое описание служит шаблоном для создания в программе конкретных экземпляров (объектов) данного класса, свои конкретные имена.

Встраивание

Помимо инкапсуляции, ООП на VBA характеризуется еще одним немаловажным свойством - встраиванием. Встраивание - это механизм порождения новых классов с использованием существующих. Пусть построен класс А, тогда при объявлении нового класса В его свойст­вами могут быть объекты класса А. В этом случае говорят, что класс А встроен в класс В, и класс А является родителем, а класс В - потомком. Встраивание - транзитивное отношение, т. е. можно создать произвольно длинную цепочку вложенных объектов, образующих иерархию родителей и потомков. Так, в новый класс С можно встроить объект класса В, который является объектом класса А. Для приложений Office XP характерна ситуация, когда необходимо указать 6 - 8 уровней вложенности, чтобы добраться до нужного объекта. Например, чтобы скрыть панель инструментов Стандартная в документе Word, необходимо выполнить следующее присваивание:

Word.Application.ActiveDocument.CommandBars(2).Visible = False

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

Наследование

Наряду со встраиванием, есть еще один способ использовать существующие классы при создании новых, он называется наследованием. При наследовании указывается, что вновь создаваемый на основе класса А класс В содержит все (или некоторые) методы и свойства класса А, а также свои собственные методы и свойства. Различие между встраиванием и наследованием только синтаксическое. При встраивании, используемом в VBA для ссылки на метод или свойство встроенного класса, мы используем имя поля этого класса. При наследовании (характерном для С и C++) имя свойства или метода родительского класса можно использовать непосредственно.

Создание класса

Синтаксически классы в VBA оформляются в виде специальных модулей классов (имя класса - это имя модуля), где в разделе Declarations помещается описание свойств (переменных) класса, а дальше идет описание методов (процедур) класса. Синтаксически описания свойств и методов практически не отличаются от описания обыкновенных переменных и процедур. Случаи отличия или особенностей употребления будут оговорены отдельно. Для создания модуля класса необходимо в окне базы данных выполнить следующие действия:

Insert - Class Module - "Имя класса"

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

Процедуры класса

В VBA все процедуры класса делятся на три группы:

  1. процедуры - методы
  2. процедуры - свойства
  3. процедуры - репликации на события

Процедуры - методы

Синтаксис объявления таких процедур не отличается от стандартного, за исключением использования ключевого слова Friend. Ключевое слово Friend, как и ключевые слова Private и Public, предназначено для ограничения области видимости метода. Если Private делает метод видимым только внутри модуля, a Public - для всех модулей всех проектов, то Friend занимает промежуточную позицию между ними: он делает видимым метод только в том проекте, где был описан класс.

Процедуры - свойства

Как известно, определение класса в VBA состоит из двух разделов:реализации и интерфейса. Одна из наиболее трудных задач для программистов - новичков в ООП заключается в определении того, какие члены класса делать закрытыми (включать в раздел реализации), а какие, наоборот, открытыми (включать в раздел интерфейса). Общим правилом можно считать то, что чем меньше программе известно о реализации класса, тем лучше, т. е. желательно скрыть посредством ключевого слова Private как можно большее количество свойств класса в раздел реализации, а доступ к свойствам осуществлять через специальные Public - методы, организующие интерфейс класса.
Классы и объекты Сокрытие информации (инкапсуляция) - это сокрытие деталей реализации функций, класса или даже программы. В условиях сокрытия информации программист работает с функциями и классами как с черными ящиками. Другими словами, передавая функции некоторое значение в форме входного параметра, программист знает лишь результат, который будет получен на выходе этой функции. Сокрытие информации в реализационную часть класса и доступ к ней через функциональный интерфейс повышает надежность программы. Имеется два способа создания свойств:

  1. Более простой из них заключается в создании в разделе описаний модуля класса переменной, объявленной с помощью ключевого слова Public. Таким образом, пользователи объекта получат возможность устанавливать и получать значение, сохраняемое в этой переменной. (Иными словами, свойство доступно как для чтения, так и для записи). Имя переменной определяется именем свойства, которое используется в вашей программе, поэтому, как и в случае имен классов, следует применять имена, отражающие содержимое переменных. Хотя использование общих переменных для определения свойств является самым простым способом, у него имеется несколько недостатков:
    • У созданного класса нет никакой возможности определить, когда внешний процесс изменил значение свойства. Это может сыграть свою негативную роль в том случае, когда вам нужно ограничить значения определенным интервалом или выполнить какое - либо действие при изменении значения.
    • Невозможно наложить ограничения на значения свойства или выполнить другую проверку данных. К примеру, вы можете захотеть ограничить значение свойства, представляющего возраст человека, положительными действительными числами.
    • Невозможно создавать свойства, предназначенные только для чтения. В программах часто возникают ситуации, когда нужно возвратить значение свойства, не присваивая его, особенно в тех случаях, когда такие значения основаны на других данных.
  2. Для того чтобы справиться с этими недостатками, следует использовать второй способ создания свойства, а именно, создание свойства при помощи процедуры Property, их всего три: Property Let, Property Set, Property Get. Рассмотрим их подробнее:

    Property Let - процедура записи, которая позволяет присваивать свойству новые значения

    [{Public|Private|Friend}] [Static] Property Let имяПроцедурыСвойства ([списокПараметров] значение)
    Классы и объекты[блокОператоров1]
    [Exit Property]
    Классы и объекты[блокОператоров2]
    End Property

    Синтаксис прост и понятен. Вначале идут операторы объявления видимости процедуры - свойства и необязательное ключевое слово Static. Кстати, хотя использование ключевого слова Private допустимо, его присутствие сводит на нет смысл процедуры - свойства.

    имяПроцедурыСвойства - это имя процедуры, изменяющей свойство. Кстати,   желательно,   чтобы   имя   повторялось   во   всех   трех   процедурах-свойствах, если они имеют дело с одним и тем же свойством.

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

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

    Далее идет блокОператоров, в котором следует произвести присваивание значению свойства значения фактического параметра значение.

    Property Set - процедура записи, которая позволяет присваивать свойству и свойству - ссылке новые значения. Отдельная процедура нужна, поскольку свойство - ссылка указывает на объект, а присваивание объектам отличается от присваивания переменным простых типов.

    [{Public I Private|Friend}] [Static] Property Set имяПроцедурыСвойства ([списокПараметров]   ссылкаНаОбъект)
       [блокОператоров1]
    [Exit Property]
       [блокОператоров2]
    End Property

    Здесь только одно отличие от предыдущей процедуры: вместо параметра значение, стоит параметр ссылкаНаОбъект, который представляет имя ссылки на объект, присваиваемый свойству - ссылке.

    Property Get - процедура чтения (а точнее, функция), которая позволяет считывать значение свойства. Эта процедура применима как к простым свойствам, так и к свойствам-ссылкам.

    [{Public I Private|Friend}] [Static] Property Get имяПроцедурыСвойства
    ( [списокПараметров] )   As   ТипДанных
    [ блокОператоров 1 ]
    [имяПроцедурыСвойства = выражение]
    [Exit Property]
    [ блокОператоров 2 ]
    End Property

    Как видно, Property Get - это функция, и для нее действуют все синтак­сические правила функций. Напомним лишь одно: тип значения, возвращаемого функцией, должен совпадать с типом значения выражение.

Создание экземпляра класса

Определив класс, задав ему несколько свойств и методов, можно использовать его в программе VBA. Но для этого сначала необходимо создать новый экземпляр класса. Невозможно просто ссылаться на переменные или вызывать процедуры модуля класса таким образом, как это делается в стандартном модуле: VBA выдаст ошибку компиляции "Подпроцедура или функция не найдена", так как возможности найти процедуру в глобальном пространстве имен не существует. Процедура останется "скрытой" до тех пор, пока вы не создадите новый экземпляр класса, и тогда можно вызвать ее только как метод созданного экземпляра класса. Для создания нового экземпляра класса нужно объявить объектную переменную, которая используется для хранения ссылки на созданный экземпляр класса. Для переменных, применяемых для ссылки на пользовательские классы, действуют те же правила, что и для переменных, ссылающихся на объекты VBA или приложения. Их можно описать, используя зарезервированные слова Dim, Private, Public и Global. Следующим шагом является создание нового экземпляра объекта и сохранения ссылки на него в переменной. Для этого используется оператор Set в сочетание с ключевым словом New.

Создание экземпляра класса. Имя класса User, имя экземпляра класса tmpuser.

Dim tmpuser As User
Set tmpuser = New User

Заметьте, что типом данных в этом примере является имя класса, которое было определено раньше. Хотя такой синтаксис может показаться избыточным, для создания нового экземпляра объекта обязательно нужно использовать ключевое слово New в операторе Set. Если этого не сделать, то при попытке применить любое свойство или метод этого класса, VBA выдаст сообщение об ошибке выполнения 91 - "Не задана объектная переменная или переменная блока with". Для создания нового экземпляра объекта простого описания объектной переменной при помощи оператора Dim недостаточно.

Неявное создание экземпляра класса

Объявление переменной и создание нового экземпляра класса можно объединить в одном операторе. Вот как это будет выглядеть:

Dim tmpuser As New User

После такого объявления переменную можно сразу использовать, обращаясь к свойствам и методам объекта, на который она указывает. При этом сам объект создается не оператором Dim, а позднее, при первом обращении к переменной. Хотя описанный способ неявного создания объектов и удобен, поскольку позволяет сэкономить одну строку кода, он имеет один недостаток: из-за того что в сложном приложении вы не знаете точно, когда будет создан объект, отладка такого приложения может быть затруднена. Поэтому рекомендуется всегда создавать объект явно, с помощью отдельного оператора Set New.

Выпуск 54. Автолинковка таблиц при открытии приложения.

Данная статья ориентирована на начинающих разработчиков Access, желающих более углубленно изучить возможност�� программирования в Access и сделать свои приложения более профессиональными.

Автор. Парусников Алексей

Разделенная база данных.

Создавая свои первые базы данных в Access, начинающие разработчики обычно строят приложения, которые состоят из одного файла базы данных, то есть таблицы и формы расположены в одном и том же файле mdb. Такая компоновка может быть удобна лишь в том случае, когда база делается что называется «для себя». Но Access позволяет помимо локальных приложений создавать и сетевые. Простейшим случаем такого приложения является разделенная база данных, включающая в себя два файла mdb: первый — файл объектов данных (в нем хранятся таблицы), второй — файл объектов приложения (в нем хранятся все остальные объекты — формы, запросы, отчеты, страницы доступа к данным, макросы и модули VBA). При этом в файле объектов приложения устанавливаются связи с таблицами, хранящимися в файле объектов данных.
Разделение базы данных дает следующие преимущества:

  1. В однопользовательской среде можно обновлять объекты приложения, не оказывая влияния на существующие данные. При этом приложение обновляется простой заменой файла объектов приложения. Альтернативой этому может служить такой способ: представьте, что Вы установили неразделенную базу данных, пользователи начали работать с ней, причем постоянно и тут Вам дают задание, что либо изменить: сделать новый отчет, запрос и т. д. Придется выгонять пользователей, садиться за машину, делать работу. И все это время база будет не рабочей. Конечно, такая ситуация не реальна. Реальнее, что скорее выгонят Вас с работы, чем остановят производство. Поэтому, даже однопользовательские базы лучше делать разделенными.
  2. В многопользовательской среде с одними и теми же данными могут совместно работать все пользователи приложения, поскольку файл объектов данных размещается на файловом сервере. В качестве файлового сервера может выступать общая папка (с открытым для всех доступом) в которой помещают файл объектов данных (база с таблицами).

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

  1. делаем новую пустую базу, жмем в окне базы правой кнопкой, выбираем в контекстном меню «Импорт» или «Файл – Внешние данные – импорт»,  и далее по диалогу. В результате в базу будут импортированы (скопированы) таблицы. Удаляем таблицы из базы, откуда делался импорт, и подключаем ее к таблицам новой базы.
  2. делаем разделение базы при помощи мастера: «Сервис – служебные программы – разделение баз данных» и далее по диалогу.

Подключение же к «серверу» (базы с таблицами) делается очень просто – жмем правой кнопкой в окне базы объектов приложения (там, где формы и все остальное), выбираем в контекстном меню «Связь с таблицами» или «Файл – Внешние данные – Связь с таблицами» и далее по диалогу. В результате в нашей базе появятся ярлыки таблиц, причем со значком стрелки слева, который означает, что таблицы внешние. Ограничение при работе с такими таблицами – нельзя менять их структуру (добавлять, изменять поля и т. д.) в этой базе. Можно только в той, где они созданы (находятся).
Но лучше, если уж решили делать базу разделенной, при проектировании сразу же создавать две базы mdb: в одну помещаем таблицы, в другую все остальное.
Итак, база разделена. Напомню, основное преимущество разделения базы – возможность создания сетевого приложения. Представим: на одном из сетевых компьютеров создаем папку «База», открываем к ней общий доступ (нужно так же открыть доступ и к диску, на котором установлен Access, обычно «С»), и помещаем в нее файл объектов данных (базу с таблицами). На других компьютерах размещаем копии файлов объектов приложения (базу с формами, отчетами и т. д.) и подключаем их к нашему «серверу». Получилось сетевое приложение – много пользователей заносят данные в одну базу. Такое приложение называется «Файл – серверным», так как в качестве «сервера» выступает файл объектов данных.
Но допустим, по каким то причинам месторасположения «сервера» изменилось – папку «База» переместили. Как только пользователи запустят свои приложения, у них появится сообщение о том, что таблицы не найдены. В этом случае, жмем правой кнопкой по ярлыку таблицы в приложении пользователей, выбираем в контекстном меню «Диспетчер связанных таблиц», помечаем те таблицы, путь к которым нужно обновить, или жмем «Выделить все», затем «ОК» и далее по диалогу.
Все вышеуказанные действия делаются вручную. Но можно ли все это сделать программно?

Автоприсоединение (автолинковка) таблиц

   Сначала подумаем, в каких еще случаях, кроме как нежелании вручную нажимать на кнопки, может понадобится автолинковка? Казалось бы, поместил базу на компьютер – «сервер», подключил к ней базы (приложения) пользователей, и не трогай ее (не перемещай). Зачем каждый раз при запуске цеплять таблицы? Дело тут вот в чем:

  • Иногда приходится менять месторасположение «файлового сервера». Представьте, 50 пользователей, 50 раз придется бегать к каждому и настраивать новое подключение.
  • Предположим, Вы сделали установочный дистрибутив вашего приложения Access – Setup.exe. Откуда вам знать, куда пользователь захочет установить Вашу программу: на «С», «D» или куда то еще? Можно конечно в настройках программы – упаковщика указать, что программа будет ставиться только в «С:/Program Files…». Но такой подход не серьезен, и лишь покажет Ваш непрофессионализм.

Стало быть, деваться некуда, придется цепляться. Что для этого понадобится?

Самое главное – это CurrentProject.Path (текущий проект - путь): оказывается, Access сам знает, куда его установили. Далее понадобятся программные методы работы с таблицами (чтение, запись, удаление данных) – ведь нужно не просто определить, куда установили приложение, но и сохранить его путь в таблице, и затем «подцепить» их по этому пути. Делается это при помощи объектной модели доступа к данным DAO - Data Access Objects. Объекты доступа к данным создавались, как объектно-ориентированный интерфейс для ядра баз данных Jet фирмы Microsoft как раз для того, чтобы можно было программно вносить, изменять, удалять данные в таблицах.

Общая схема работы подключения:

при запуске приложения запускается макрос AutoExec - чтобы он запускался именно при запуске, он должен называться AutoExec – это зарезервированное имя в Access. Макрос в свою очередь запускает функцию SetReferences () из модуля AutoPatch, которая собственно и производит автолинковку таблиц. Далее макрос запускает форму frmStart – стартовую форму приложения.

Путь базы сохраняется в таблице tSystemPath – Pname. Имена таблиц, которые нужно присоединить – в таблице tSystemTables – Tname. Если вы будете применять этот пример в своих проектах, Вам нужно будет записать туда имена своих таблиц.

В данном примере при запуске приложения помимо автолинковки делается еще одно важное дело: записывается путь к папке с резервными копиями базы (об этом чуть ниже). Это так называемые данные настройки приложения. Их может быть много: к примеру, каталог выгрузки снимков отчетов, каталог шаблонов .dot и т. д. Чтобы после запуска, приложение было работоспособным, нужно все эти настройки определить и сохранить в соответствующих таблицах, в данном случае tAdminCop.

Резервное копирование базы данных.

Хотя Access и считается достаточно надежной системой, но и у нее бывают сбои и базы «рушатся» – нет в мире совершенства. А потому считается хорошим тоном сделать сервис – сохранение резервных копий базы. Действительно, зачем рисковать?

Для этого понадобятся методы работы с файловой системой Windows – модуль FileSystem и функции работы с файлами: fDeleteFile, fCopyFile. Названия говорят сами за себя. Чтобы пользователь мог указывать/создавать каталог для сохранения резервных копий понадобится GetDirFolder – функция открытия диалогового окна выбора каталога (папки).

Непосредственно копирование производится при помощи процедуры sCopBase в модуле формы frmStart. Причем, сначала проверяется наличие каталога для сохранения копии базы, наличие самой базы – функции IsFolderName, IsFileName.
Имена копий базы задаются с указанием даты и времени копирования.

Выпуск 53. Сравнение баз данных Microsoft SQL Server

Введение в тему  
    Задача сравнения баз данных Microsoft SQL Server является достаточно актуальной. Предположим Вы разрабатываете adp проект на Access, используя SQL Server на своем компьютере, и Вам надо перенести базу данных на другой компьютер. Первый раз это легко сделать (используя backup файла), но что делать, если кто-то уже работает с базой данных. Например, вводит данные в базу данных, тогда этот вариант "отпадает"
    Выход только один. Надо провести сравнение баз данных и добавить отсутствующие таблицы, поля, индексы в базу данных. Конечно есть много серьезных разработок на эту тему. Например, у
Microsoft SQL Server Enterprise Manager есть функции репликации, т.е. путем создания публикаций и подписчиков синхронизовать базы данных. Но может быть и так, что Вы не будете иметь полный доступ к удаленному серверу и такой вариант тоже будет "отброшен" (этот вариант добавляет поля репликации в базу данных). Неплохой вариант - это создание sql скрипта и его выполнение на SQL сервере для конкретной базы. Но и у такого способа есть недостатки - это отдельная программа, потребует дополнительного изучения, финансовых затрат, и самое главное, что ее невозможно вставить в VBA программу (кроме sql скрипта).
   Что делать, если нужно добавить в базу данных всего несколько таблиц, полей и индексов. Попробуем решить эту задачу средствами
VBA. Ниже дается описание УПРОЩЕННОГО класса на VBA, который создает необходимые таблицы и поля. Адрес загрузки: CompareSQL.zip
   И так и Вы можете просто прочитать этот материал и положить "в корзину" или попытаться понять проблему более детально и найти более полное решение. Для того, чтобы было интересно выполнять задачу, она разделена на пункты и оплачивается через webmoney.ru. И так успехов Вам в этом решении.

Задача для удаленного выполнения. Бюджет 600 рублей
    Необходимо доработать данные класс для сравнения баз данных по 3 направлениям.
а) Нужно сравнить 2 базы данных, записать различия в таблицу и создать отчет для печати (300 рублей)
б) Добавить
/изменить в указанной базе данных недостающие таблицы, процедуры и представления (300 рублей)

Описание класса для сравнения
     В примере используются в 2 базы данных: test1 и test2. Их надо создать вручную и разместить на SQL сервере. Базы данных могут быть созданы и в различных версия Microsoft SQL Server: MSDE, 2000, 2005, Express 2005. Это непринципиально. Класс получается достаточно простой. Открываем два каталога баз данных и начинаем выполнять сравнение и модификацию.

'--- Начало --
' Переменные 1 - шаблон, 2 - модифицированная база данных
Private adoxCat1 As ADOX.Catalog ' Первый каталог
Private adoxCat2 As ADOX.Catalog ' Второй каталог
Private adoxTbl1 As ADOX.Table
Private adoxTbl2 As ADOX.Table
Private adoxCol1 As ADOX.Column
Private adoxCol2 As ADOX.Column
Private adoxIdx1 As ADOX.Index
Private adoxIdx2 As ADOX.Index

' Функция создания каталога
Public Function fcCreateCatalog(strConnection1 As String, strConnection2 As String) As Long
Set adoxCat1 = New ADOX.Catalog
Set adoxCat2 = New ADOX.Catalog
adoxCat1.ActiveConnection = strConnection1
adoxCat2.ActiveConnection = strConnection2
End Function

' Сравниваем объекты
Public Function fcCompareObjects(Optional strType)
fсAdoxCreateObjects ' Сравниваем все объекты
End Function

' Закрываем каталоги
Public Function fcCloseCatalog() As Boolean
On Error GoTo 999
' Освобождаем память
adoxCat1.ActiveConnection.Close
adoxCat2.ActiveConnection.Close
Set adoxCat1 = Nothing
Set adoxCat2 = Nothing
fcCloseCatalog = True
Exit Function
999:
fcCloseCatalog = False
Err.Clear
End Function

' Закрываем класс
Private Sub Class_Terminate()
fcCloseCatalog
End Sub

Public Sub fсAdoxCreateObjects()
fсAdoxCreateTables ' Ссоздание таблиц
End Sub

' Перебираем все таблицы
Public Function fсAdoxCreateTables()
On Error GoTo 999
For Each adoxTbl1 In adoxCat1.Tables
If adoxTbl1.TYPE = "TABLE" Then fсAdoxCreateTable
Next
Exit Function
999:
MsgBox Err.Description
Err.Clear
Exit Function
End Function

' Создание таблицы
Public Function fсAdoxCreateTable() As Boolean
Dim adoxCol As ADOX.Column
On Error GoTo 999
If fсAdoxFindTable(adoxTbl1.Name) = True Then
' Для тестирования новых таблиц снимите комментарий ниже
' adoxCat2.Tables.Delete adoxTbl1.Name ' Удаляем таблицу
Else
Set adoxTbl2 = New ADOX.Table
With adoxTbl2
.Name = adoxTbl1.Name 'имя создаваемой таблицы
.ParentCatalog = adoxCat2
fсAdoxCreateTableColumns
fсAdoxCreateTableIndexes
End With
adoxCat2.Tables.Append adoxTbl2 'добавляем таблицу в БД
End If
Set adoxTbl2 = Nothing
Exit Function
999:
MsgBox Err.Description
Err.Clear
Exit Function
End Function

' Создание полей таблицы
Public Function fсAdoxCreateTableColumns() As Boolean
On Error GoTo 999
For Each adoxCol1 In adoxTbl1.Columns
adoxTbl2.Columns.Append adoxCol1.Name, adoxCol1.TYPE, adoxCol1.DefinedSize
Next
Exit Function
999:
MsgBox Err.Description
Err.Clear
Exit Function
End Function

' Создание индексов таблицы
Public Function fсAdoxCreateTableIndexes() As Boolean
Dim Col As ADOX.Column
On Error GoTo 999
For Each adoxIdx1 In adoxTbl1.Indexes
Set adoxIdx2 = New ADOX.Index
adoxIdx2.Name = adoxIdx1.Name
For Each Col In adoxIdx1.Columns
adoxIdx2.Columns.Append Col.Name
Next
adoxIdx2.PrimaryKey = adoxIdx1.PrimaryKey
adoxIdx2.UNIQUE = adoxIdx1.UNIQUE
adoxTbl2.Indexes.Append adoxIdx2
Next
Exit Function
999:
MsgBox Err.Description
Err.Clear
Exit Function
End Function


' Находим в каталоге нужную таблицу
Public Function fсAdoxFindTable(strName As String) As Boolean
Dim adoxTbl As ADOX.Table
On Error GoTo 999
fсAdoxFindTable = False
For Each adoxTbl In adoxCat2.Tables
If adoxTbl.Name = strName Then
fсAdoxFindTable = True
Exit Function
End If
Next
Exit Function
999:
MsgBox Err.Description
Err.Clear
Exit Function
End Function
'--- Конец класса ---


Выпуск 52. Репликация базы данных

Новости сайта
    Есть профессиональное решение по репликации удаленных баз данных. База данных автоматически делается реплицируемой, архивируется и защищается паролем. Информация отсылается на сайт в Интернете. Любой офис имея пароль и доступ к сайту и архиву может скачать данные и синхронизовать их с офисной базой. Таким образом, такое решение значительно упрощает взаимодействие разных офисов.

Введение в тему  
        По репликации базы данных дано не так уж и много информации. Что это такое. Репликация - это синхронизация записей (и структуры) базы данных. Например, у Вас есть 2 офиса, 2 удаленных склада необходимо синхронизовать эти данные таким образом, чтобы заказы и складские остатки были одинаковыми в офисах и на складах. Один из вариантов - это разместить базу в интернете. Сделать на сайте систему заказов, но возникает проблема. Нужно постоянно защищать базу данных от взлома, да и интерфейс и отчетность сайта будут иметь ограничения по сравнению с решением выполненном на Access. В этом приложении больше возможностей по поиску и работе с данными и отчетами типа Excel и Word.

Варианты решения
   Для работы с базами данных Access (.mdb) была разработана модель, которая получила название Microsoft® Jet and Replication Objects (JRO). В этой статье мы рассмотрим, как с помощью JRO сделать базу данных реплицируемой, научимся создавать различные типы реплик, а также синхронизировать их между собой.

Первы�� шаг - сделаем базу реплицируемой. Функция для этого может выглядеть так:

Private Function MakeRecordLevelDesignMaster(strDBPath As String)
Dim repMaster As New JRO.Replica 'определяем переменную с которой в дальнейшем будем производить все действия
   repMaster.MakeReplicable strDBPath, False 'делаем базу данных реплицируемой
   Set repMaster = Nothing 'удаляем переменную из памяти
End Function
Метод MakeReplicable([ConnectString] [, ColumnTracking])имеет два параметра. Первый - это путь к базе данных, которую мы делаем реплицируемой. Второй параметр рассмотрим подробнее. Он может принимать два значения True (по-умолчанию) или False . В нашем примере установлено значение False. Это значит, что при синхронизации реплик конфликты будут отслеживаться на уровне записи. Если установлено значение True, то конфликты будут отслеживаться на уровне столбца. Например, два пользователя в разных репликах изменяют различные поля одной записи. Если  значение параметра установлено False (уровень записи), то при синхронизации возникнет конфликт. Хотя на самом деле конфликта нет, ведь данные различных столбцов не связаны между собой. Избежать такого конфликта можно установив значение параметра True (уровень столбца). Тогда конфликт при синхронизации возникнет только в том случае, если оба пользователя изменят один и тот же столбец одной и той же записи. Таким образом, использование отслеживания конфликтов на уровне столбца позволяет произвести синхронизацию с наименьшим количеством ошибок, хотя и прибавит системе немного работы. Возможность  отслеживания конфликтов на уровне столбца есть только в JRO, в DAO ее нет. Это надо учесть при проектировании базы данных

По умолчанию, все объекты базы делаются реплицируемыми, т.е. при синхронизации будут обновляться данные во всех таблицах. Если вы хотите чтобы объект базы данных был локальным (при синхронизации данные не обновляются),  надо задать ему это свойство до объявления базы реплицируемой. Это можно сделать так:

Private Function KeepObjectLocal(strDBPath As String, strObjectName As String, strObjectType As String)
Dim repMaster As New JRO.Replica 'определяем переменную с которой в дальнейшем будем производить все действия

   repMaster.ActiveConnection = strDBPath 'Связываем переменную с базой данных
   repMaster.SetObjectReplicability strObjectName, strObjectType, False 'делаем объект  strObjectName, который имеет тип strObjectType локальным (False) или реплицируемым (True)

Set repMaster = Nothing 'удаляем переменную из памяти
End Function

Пример вызова функции Call KeepObjectLocal("C:\<Путь к базе данных>\<Имя файла>", "Клиенты", "Tables")

Второй шаг - создание реплики. Для создания реплики используем метод CreateReplica.
Function MakeNewReplica(strReplicableDBPath As String, strDescription As String) As Integer
Dim repReplicableDB As New JRO.Replica 'определяем переменную с которой в дальнейшем будем производить все действия
Dim strNewReplicaDBPath As String 'определяем переменную - имя новой реплики

   strNewReplicaDBPath = "C:\<Путь к базе данных>\<Имя файла>"  'получаем имя новой реплики
   repReplicableDB.ActiveConnection = strReplicableDBPath 'Связываем переменную с базой данных
   repReplicableDB.CreateReplica strNewReplicaDBPath, strDescription, jrRepTypeFull, jrRepVisibilityGlobal 'Создаем реплику
   Set repReplicableDB = Nothing 'Удаляем переменную с памяти
End Function

Метод CreateReplica (ReplicaName, Description [, ReplicaType] [, Visibility]  [, Priority] [, Updatability]) имеет такие параметры:

1. ReplicaName - путь и имя новой реплики

2. Description - описание реплики

3. ReplicaType - тип реплики. В нашем примере jrRepTypeFull - полная реплика. (не обязательный параметр)

4. Visibility - видимость реплики. В нашем примере jrRepVisibilityGlobal  - глобальная реплика, т.е. в реплике "видны" все данные из главной реплики (не обязательный параметр)

5. Priority - приоритет реплики. В нашем примере этот параметр опущен. Это значит, что приоритет присваивается автоматически. (не обязательный параметр)

6. Updatability - показывает разрешается ли пользователям менять схему и записи реплицируемых объектов реплики. В нашем примере параметр опущен. Это значит, что               разрешаются изменения. (не обязательный параметр).

 

Давайте рассмотрим какие еще значения могут принимать параметры метода CreateReplica.

ReplicaType может принимать значения

jrRepTypeNotReplicable По умолчанию. База данных не реплицируемая.
jrRepTypeDesignMaster Реплика - владелец проекта.
jrRepTypeFull Полная реплика.
jrRepTypePartial Частичная реплика.

 

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

Function CreatePartial(strReplicableDBPath As String, strDescription As String)

Dim repFull As New JRO.Replica
Dim repPartial As New JRO.Replica
Dim strPartialDBPath As String

  strPartialDBPath = "C:\<Путь к базе данных>\<Имя файла>"  'получаем имя новой реплики
 

  'Создаем пустую реплику
  repFull.ActiveConnection = strReplicableDBPath
  repFull.CreateReplica strPartialDBPath, strDescription, jrRepTypePartial, jrRepVisibilityGlobal

  Set repFull = Nothing 'Удаляем переменную с памяти

  'Соединяемся с созданной репликой в эксклюзивном режиме
  repPartial.ActiveConnection = "Data Source=" & strPartialDBPath & ";Mode=Share Exclusive"

  'Создаем фильтр на основе таблицы (параметр jrFilterTypeTable)

  repPartial.Filters.Append "Клиенты", jrFilterTypeTable, "[Страна] = 'США'"
  repPartial.Filters.Append "Товары", jrFilterTypeTable, ""

  'Создаем фильтр на основе связей между таблицами (параметр jrFilterTypeRelationship)

  repPartial.Filters.Append "Заказы", jrFilterTypeRelationship, "КлиентыЗаказы"

  'Импоритруем данные согласно фильтра в реплику

  repPartial.PopulatePartial strReplicableDBPath

  Set repPartial = Nothing 'Удаляем переменную с памяти  
End Function
 

 Visibility - может принимать значения

JrRepVisibilityGlobal Глобальная реплика.
JrRepVisibilityLocal Локальная реплика.
JrRepVisibilityAnon Анонимная реплика.

 

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

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

 

Priority - это параметр который позволяет установить приоритет реплики. Приоритет реплики - еще одно новшество JRO. Этот параметр введен для того, чтобы показать относительное превосходств�� одной из реплик во время синхронизации: всегда принимаются изменения, сделанные в реплике с большим приоритетом. Почему относительное превосходство? Потому что, хотя изменения и принимаются, конфликт фиксируется и если он не обработан программно, при последующем обращении к  реплике вызывается мастер решения конфликтов. Он позволяет пользователю принять сделанные изменения или отменить их.

Приоритет реплики может принимать значение от 0 до 100 и устанавливается при создании реплики. Владелец проекта имеет приоритет 100. Далее, если при создании глобальной реплики приоритет не указан явно (как в нашем примере) то он принимается равным 90% от приоритета родительской. Как говорилось выше, приоритет локальной и анонимной реплики всегда равен 0. При синхронизации реплик с одинаковым приоритетом принимаются измения, сделанные в той реплике, у которой самый низкий ReplicaID - свойство, которое присваивается реплике автоматически при создании.

 

Updatability - может принимать значения

jrRepUpdFull Реплика может быть обновлена.
jrRepUpdReadOnly Реплика только для чтения.

 

По умолчанию принимается значение jrRepUpdFull. Если установлено значение jrRepUpdReadOnly то пользователю не разрешается изменять данные в реплике. Однако при синхронизации с другими репликами изменения данных принимаются.

Третий шаг - синхронизация двух реплик.

 

Private Function TwoWayDirectSync(strReplica1 As String, strReplica2 As String)
Dim repReplica As New JRO.Replica  'определяем переменную с которой в дальнейшем будем производить все действия

repReplica.ActiveConnection = "Data Source=" & strReplica1 & ";Mode=Share Exclusive"   'связываем переменную с репликой strReplica1

repReplica.Synchronize strReplica2, jrSyncTypeImpExp, jrSyncModeDirect  'синхронизируем реплику strReplica1 с репликой strReplica2

Set repReplica = Nothing   'Удаляем переменную с памяти  
End Function
 

Метод Synchronize(Target [, SyncType] [, SyncMode]) имеет такие параметры:

 

1.  Target - путь к реплике с которой проводим синхронизацию.

2. SyncType - тип синхронизации (не обязательный параметр).

3. SyncMode - режим синхронизации (не обязательный параметр).

 

Рассмотрим второй и третий параметры подробнее.

SyncType - может принимать значения

jrSyncTypeExport

Отправлять изменения сделанные в текущей реплике.

Изменения, сделанные в реплике

Target игнорируются.

jrSyncTypeImport Принимать изменения сделанные в реплике Target.

Изменения, сделанные в текущей реплике игнорируются

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

SyncMode - может принимать значения

jrSyncModeIndirect По умолчанию. Косвенная синхронизация.
jrSyncModeDirect Прямая синхронизация.
jrSyncModeInternet Косвенная синхронизация через интернет.

Во время прямой синхронизации двух реплик они открываются одновременно и данные передаются непосредственно из одной реплики в другую. При косвенной синхронизации открывается одна реплика, изменения собираются и помещаются в отдельный файл. Затем открывается вторая реплика и читает эти изменения. Прямую синхронизацию используют когда обе реплики находятся в одной локальной сети. При дозвоне и других ненадежных соединениях, когда связь может внезапно оборваться необходимо использовать косвенную реплику. Также, косвенную синхронизацию применяют через интернет (значение jrSyncModeInternet). При этом параметр Target имеет вид к примеру такой: "www.mycompany.myserver.com/files/Orders.mdb "

Таким образом, средства репликации широко представлены в Access и дают большие возможности создателям баз данных для использования репликации в своих проектах.


Выпуск 51. Аутсорсинг баз данных ( 2 часть )

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

Общие требования

1. Программное обеспечение разрабатывается, используя Microsoft Office 2003 или 2000, XP2.
2. Оцените примерное время на разработку утилиты. Срок ее разработки не должен превышать разумное время. Обычно это 2-3 дня.
3. Если Вы в состоянии решить задачу, то выберите номер технического задания и вышлите заявку в произвольном виде по email. Обязательно укажите контактную информацию и время для разработки утилиты.
4. Названия таблиц полей или функций желательно давать на английском языке.
5. Если заявка свободная, то к Вам придет подтверждение об участии в работе. После этого сообщения, можно разрабатывать утилиту.
6. Виды оплаты за работу. Webmoney, Безналичная, Наличная формы


Утилита для сохранения точек

1. Необходимо разработать разработать утилиту, которая запоминала в таблице координаты положения указателя мыши на форме.  Вы периодически нажимаете кнопку мыши (удерживая shift) ее координаты x и y сохраняются как последовательность координат.  Требования к разработчику. Знание основ программирования и событий формы.
2. Бюджет: 300 руб.,
3. Бонус: участие в разработке специальной формы

Разработка описания базы данных

1. Необходимо разработать предварительное описание новой базы данных. Требования к разработчику. Знание Microsoft Office, умение добавлять в описание рисунки, иметь свободное время для переговоров. В качестве помощи могут быть предоставлены шаблоны технических заданий баз данных.
2. Помощь. Могут быть предоставлены примеры технических заданий.
3. Бюджет: 300 руб.,
4. Бонус: участие в написании технических заданий по базам данных.

Разработка класса Access - Excel

1. Необходимо создать функцию (класс), который будет автоматически импортировать файлы Excel в таблицы Access.
2.
Метод решения. На 1 этапе необходимо встроенными функциями Access импортировать лист Excel в промежуточный файл Access mdb. Далее запросом добавить или обновить записи в таблице Access. Названия полей в Access и Excel могут отличаться.
3. Помощь. Могут быть предоставлены предварительные решения ��о импорту данных из Excel.
4. Бюджет: 300 руб.
5. Бонус: участие в разработке класса по этой теме.

Разработка класса Access - 1C

1. Необходимо создать функцию (класс), которая будет автоматически привязывать файлы базы данных 1С предприятия к текущему файлу Access. Формат файлов 1С-dbf.
2.
Метод решения. Для этого Вы создаете форму, добавляете в нее поле для указания пути к базе данных 1С. Дополнительно нужно добавить кнопку "Привязать". При нажатии на эту кнопку происходит автоматически привязка таблиц dbf. На следующем этапе, название таблиц и их поля переименовываются в соответствии с назначением, указанном в файле конфигурации 1С. Требования к разработчику. Необходимо знать структуру dbf файлов 1C, файл конфигурации таблиц и полей, а также функции VBA, позволяющие это сделать в автоматическом режиме.
3. Бюджет: 300 руб.
4. Бонус: участие в разработке класса по этой теме.