Переход с Paradox на InterBase за 30 дней

Скип Роуланд (Skip Rowland), президент r3 Software, Inc.

Настоящий документ представляет собой техническую статью, подготовленную на основе выступления Скипа Роуланда (Skip Rowland), президента фирмы r3 Software Inc., на 10-ой ежегодной конференции Inprise & Borland.

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

Обзор

Рассмотрим ситуацию на примере многофункциональной морской компании, занимающейся перевозками пассажиров, грузов и буксировкой по всему миру и являющейся в течение уже долгого времени моим клиентом. В 1993 году у них была установлена электронная система выписки счетов и определения загрузки на базе Paradox для Dos. В январе 1997 года они обратились ко мне с просьбой предложить что-нибудь, более полно отвечающее их требованиям к базе данных. Они чувствовали как внутреннее давление, вызываемое возросшими потребностями в управлении, так и внешнее, связанноес возросшими требованиями со стороны промышленности. Им требовалась система, которая могла бы объединить деятельность всех отделов (продаж, отправки, расчетного, операционного, отдела кадров, HR, и отдела управления рисками) в одну центральную базу данных.

В это время они имели сеть на базе Novell 3.11, включающую около 30 рабочих станций и использовали Paradox для Dos, Professional File, Excel, Word Perfect, а также ряд других инструментов. Тогда же были определены пять основных приложений:

  • Распределение/расчеты
  • Операционный
  • Работа с кадрами
  • HR
  • управление рисками

Позднее было выявлено еще три приложения:

  • управление продажами
  • управление операциями
  • общее администрирование

И я уверен, что по мере продвижения вперед будут всплывать все новые приложения.

Первоначально был предложен переход на Windows95, что, в свою очередь, потребовало обновления почти всего аппаратного и программного обеспечения компании. И хотя это было абсолютно неизбежно, в компании пытались максимально отодвинуть сроки модернизации. Мы предложили архитектуру клиент/сервер, но предложение было отвергнуто как нечто преждевременное; в результате в качестве хранилища данных был выбран Paradox v7.

Разработка началась в феврале 1997 года и шла обычным чередом: мы создавали базу данных, строили прототип, а затем снова возвращались к чертежам. И на каждом витке мы все ближе подходили к тому, что заказчики хотели получить, но не могли четко сформулировать.

По мере разработки в нее включались все новые направления, каждое из которых вызывало появление все новых и новых таблиц соответствий. База данных, включавшая изначально 25 таблиц, выросла до 400 таблиц.

Сначала все шло гладко. Приложение по распределению/расчетам было самой простой задачей, так как оно было полностью определено и работало уже в течение 4 лет. К несчастью, дела пошли значительно хуже в июне 1998 года, когда мы приступили к работе над HR-приложением.

Проблемы

Каждое приложение могло выполняться автономно и работало вполне приемлемо. Однако, как только приложения начинали работать параллельно, возникали противоречия, и все начинало разваливаться. Проблема усугублялась, когда пользователи с 16 Мб- ОЗУ пытались одновременно запускать Word, Excel, электронную почту и ряд других приложений. В результате иногда блокировались только приложения с базами данных, а иногда вся система. Иногда приложения с базами данных загружались полностью, а иногда загрузка не удавалась вообще. А сколько мы получили этих ужасных сообщений "insufficient memory to complete operation" (недостаточно памяти для завершения операции)! В общем, мы были свидетелями самых различных случаев несообразного, непонятного и абсолютно неприемлемого поведения.

Band-Aids

Как я уже упоминал, каждое приложение могло выполняться самостоятельно в качестве решения на уровне отдела. Но так как это разрушало целостное представление решения на уровне предприятия, ни о каком разбиении и слиянии данных на хоть сколько-нибудь регулярной основе не могло быть и речи. Первоначально я сам выполнял всю работу, используя Delphi вместо Paradox. Я проработал каждое приложение вдоль и поперек, исключая все внешние накладные расходы и урезая все, что было возможно. Базовую архитектуру я взял без изменения из KnowledgeBase:

  • исполняемые программы и данные размещались в разных каталогах
  • в исполняемой программе устанавливались сетевые и локальные директории
  • значение dbiSetLockRetry было установлено на 20

Но этого было не достаточно. Сначала мы обновили BDE, с 3.51 на 4.01, а затем изменили установки BDE-конфигурации.

MAXBUFSIZE 8192
MAXFILEHANDLES 500
SHAREDMEMSIZE 8192

В течение некоторого времени это работало. К сожалению, из-за циклов тестирования и создания прототипов, полностью использовалось только приложение по распределению/расчетам. Однако к сентябрю 1998 начали оперативно функционировать и использовать приложения HR- и операционный отделы. И вот наступило "Черное воскресенье". 21 сентября, около 10 часов утра, произошел сбой во всех четырех приложениях одновременно. Очевидно, что меры предосторожности были явно недостаточными.

Решения

Единственным приемлемым решением был бы переход на архитектуру клиент/сервер. InterBase была при этом логичным выбором, исходя из ее стоимости, доступности, сопровождения, а также моей предрасположенности к ней. Так как многие люди зависели от работы приложений, все необходимо было сделать в максимально короткие сроки. Фактически, сначала мне было дано на это 30 дней.

Кроме того, были некоторые дополнительные соображения, касающиеся клиента, а именно, обновление их сетевой операционной системы. Они использовали NetWare 3.11 и знали, что придется перейти на новую ОС. Рассматривались варианты NT4, ожидаемая NT5, NW4.11, или NW5. В конце концов, единственным жизнеспособным вариантом оказалась NW4.11. Так как у фирмы не было собственного сетевого администратора, вариант NT4 казался слишком рискованным; что касается NT5, то они опасались, что не успеют развернуть ее до 2000. На тот момент не было сертифицированной версии InterBase для NW5, следовательно, оставалась только NW4.11.

Некоторые дополнительные Band-Aids

Прежде чем начать перенос, от меня, как минимум, требовалось сделать текущие приложения доступными. Самая большая проблема заключалась в том, что размер блокировочных файлов увеличился до такой степени, что их стало просто невозможно переносить с помощью BDE (более 2.5 Мбайт). Рассматривались следующие промежуточные решения:

  • Разделение данных на несколько директорий и использование нескольких псевдонимов
  • Написание "диспетчера блокировочных файлов", который отслеживал бы размеры больших блокировочных файлов. При достижении любым из них размера в 700, ему предлагалось бы покинуть приложение с базой данных. Обычно блокировочные файлы приходилось бы удалять вручную - BDE не выполняла обычные служебные действия.
  • Мы снова обновили BDE на 4.51, и еще более изменили конфигурацию.

MAXBUFSIZE 8192
MAXFILEHANDLES 500
SHAREDMEMSIZE 8192

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

Фактический процесс переноса

Можно выделить четыре фазы переноса Delphi-приложения с Paradox на InterBase:

  • Установка и настройка
  • Перенос базы данных
  • Перенос кода базы
  • Оптимизация сервера

Установка и настройка включает инсталляцию InterBase, ознакомление и использование WISQL, а также конфигурирование BDE.

Для "чистого" запуска я демонтировал InterBase, установленную при инсталляции Delphi. Я выполнил полную инсталляцию IBLocal с установками по умолчанию. За тем я создал каталог под именем PDox2IB, в котором разместил "горячие" клавиши для InterBase Server Manger, WISQL, Database Desktop и BDEAdministrator.

В первую очередь я хотел изменить пароль входа в InterBase на более короткий. Для этого я:

  • запустил Server Manager
  • вошел в систему как User SYSDBA с универсальным паролем
  • выбрал User Security и изменил пароль SYSDBA на "bo"

Примечание: для просмотра изменений паролей необходимо выйти из системы, а затем снова войти в нее

Затем, чтобы создать новую базу данных в InterBase, в которую затем будет осуществлен перенос, я запустил WISQL.

  • Из меню, File/Create Database
  • DatabaseName: C:Program FilesBorlandIntrBaseVMXVM.gdb
  • Пароль: bo

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

Затем, чтобы создать новые псевдонимы для исходной и целевой баз данных, я запустил BDEAdmin.

  • В закладке Database, щелкнул правой кнопкой мыши и выбрал New. Оставил тип STANDARD
  • Изменил имя на VMS
  • Изменил PATH на C:Apps3EESBigData
  • Снова в закладке Database, щелкнул правой кнопкой мыши и выбрал New. На этот раз выбрал INTRBASE
  • Изменил имя на VMX
  • Изменил имя сервера на C:Program FilesBorlandIntrBaseVMXVM.gdb
  • Закрыл BDEAdmin

Теперь все было настроено для переноса данных.

Перенос базы данных

В первую очередь хочу отметить, что это НЕ одношаговый процесс. Если Вы предполагаете, что все произойдет мгновенно каким-либо сказочным образом, то Вас ожидает глубокое разочарование. В состав Delphi C/S входит программа с именем Datapump.exe; она установлена в BDE-каталоге и называется "Data Migration Wizard" ("Мастер переноса данных"). Datapump - хороший инструмент, но он имеет свои ограничения - не ожидайте от него невозможного и не воспринимайте как "панацею". Иногда вам придется закатать рукава и самим написать тот или иной инструмент. Если у вас нет Datapump, то придется написать его самим. Но в данном случае, предположим, что есть уже готовый.

Самые большие проблемы: имена полей и типы полей.

Совет: сразу планируйте, что для этого потребуется не одна итерация. При использовании Datapump для выявления проблем, советую вам постепенно вносить изменения в исходную базу данных и исходный код базы.

Подробнее: вам придется сделать ее, КАК МИНИМУМ, ДВАЖДЫ : один раз для разработки, а второй для эксплуатационной базы данных! Вы обязательно должны документировать весь процесс преобразования. Помните, что процесс переноса эксплуатационной части пройдет гладко только при идеальной отработке этого процесса.

Самые большие изменения в базе данных придется вносить в:

  • Имена полей
  • Вторичные индексы

Можете попытаться предварительно выполнить эти операции, или же в несколько циклов с использованием datapump. Datapump создает две paradox-таблицы -одну перед "переносом", а вторую после. В первой таблице отображаются изменения, которые будут внесены в вашу базу данных, а во второй - изменения, которые произошли.

Давайте рассмотрим изменения в именах файлов

Лучше всего, если исходные таблицы, а затем код, будут модифицированы до выполнения окончательного переноса. В этом случае, можно быть уверенным, что ваш код будет работать после переноса.

  • Чаще всего необходима замена пробелов символами "_" (Delivery Date будет выглядеть как DELIVERY_DATE)
  • Вторым наиболее частым случаем будет наличие поля с именем "Date", которое является зарезервированным словом
  • Проблему составят также и другие зарезервированные слова: VALUE, ORDER, GROUP, SET
  • И, наконец, проконтролируйте отсутствие запрещенных символов (#, @, и так далее).

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

Базовый процесс, выполняемый вручную

Выполните:

  • С помощью Windows Explorer, запустите Datapump
  • Выберите исходный псевдоним (VMS)
  • Выберите целевой псевдоним (VMX) Войдите в систему с паролем SYSDBA
  • Выберите все таблицы
  • Нажмите кнопку upsize
  • Запишите копию данного отчета в файл, создающий Paradox-таблицу
  • Закройте Datapump
  • Используя Database Desktop, посылайте запрос о наличии сбоев до тех пор, пока не будут выявлены все сбои

Чтобы просмотреть результаты, запустите WISQL, затем выберите File/Connect to Database

  • C:Program FilesBorlandIntrBaseVMXVM.gdb
  • Войдите в систему с паролем SYSDBA

Чтобы просмотреть, что сделала программа Datapump,

  • Extract/SQL Metadata for Database
  • Для просмотра содержания таблицы, SELECT * FROM [tablename]
  • Закройте WISQL

Примечание: если Вы выполнили "select" один раз, программа запросит, следует ли продолжать работу. Можно ответить и "да" и "нет".

Преобразование эксплуатационных данных

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

  • будет дублировать в новый каталог мою paradox-базу данных без самих данных
  • позволит собрать все исходные данные в каталог преобразуемых исходных данных
  • переместит преобразуемую исходную базу данных в мою IB-базу данных
  • (используя пакетный перенос).

Между шагами 2 и 3, я запустил Datapump, чтобы создать IB-базу данных из пустой продублированной базы данных, полученной в шаге 1.

Перенос кода базы

Здесь перед нами стоят простые задачи:

  • скомпилировать;
  • сделать, чтобы он работал,
  • и сделать так, чтобы он работала корректно.

Даже если Вы законченный оптимист и считаете, что работаете с простейшими компонентами, вам все-таки придется кое-что сделать. Ни одно приложение, даже самое изощренное, не заработает с первого раза; и не обращайте внимания на то, что Вам пообещали.

Сейчас я хочу рассказать о тех уроках, которые получил при переносе базы данных. У Вас ситуация может сложиться по-другому, в этом случае, некоторые из описанных мной шагов Вам не потребуются, но ведь никто не знает, что может произойти... Существуют различные модели работы с InterBase-данными; про каждую из них можно было бы написать отдельный доклад. Выбор модели, которую использовал я, был основан на чистой необходимости. И она работала, хотя, возможно, и не была лучшей.

Сначала создайте каталог для нового проекта, ibProjects.

  • В ibProjects, создайте каталог для данного проекта (мой назывался ees).
  • В каталоге проекта, создайте каталог данных и исходный каталог.
  • Если Вы используете w2w, скопируйте главный каталог w2w в каталог ibProjects. Проделайте то же самое для всех сторонних компонентов, имеющих собственные каталоги. Помните, что Вы осуществляете перенос, перемещение. Первоначально переносится все, но затем остается только необходимое.
  • Удалите файлы с расширением .dof, .dsm и .dsk.

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

Регистрационное имя приложения

В первую очередь Вы должны создать автоматическое соединение со своей IB-базой данных. Это избавит Вас от ввода регистрационного имени при каждом соединении, а ваших пользователей - от ввода их регистрационного имени.

  В BDE (при закрытом Delphi) измените Paradox-псевдоним своего приложения на другой.

Затем, находясь в Delphi,

  • File/New Datamodule
  • Вставьте TDatabase -компонент
  • Измените его имя на DBConnection
  • Установите в Alias новый BDE-псевдоним для своей IB-базы данных
  • Установите DatabaseName на старый Paradox BDE-псевдоним
  • Дважды щелкнув по нему мышью, откройте редактор свойств
  • Нажмите кнопку Defaults
  • Приведите Defaults в соответствие с указанными ниже

SERVER NAME=c:apps4eesvm.gdb
USER NAME=SYSDBA
PASSWORD=mypassword

  • Пометьте галочками, не разрывая соединения
  • Проверьте, чтобы приглашение не было помечено
  • Задайте блоку имя dmKey
  • Сохраните блок как IBKey

Откройте свой проект. Если Вы действительно все из него удалили, то откроется только исходный файл (.dpr). Просмотрите менеджер проекта. Нажмите кнопку Add и добавьте в проект IBKey. В исходном файле (.dpr), в первую очередь следует создать модуль данных.

И что действительно замечательно, так это то, что другие модули данных смогут использовать это соединение, НЕ обращаясь к модулю данных в операторе uses!!!

Теперь Вы готовы к тому, чтобы начать последовательно добавлять блоки проекта, удаляя из них все, относящееся к Paradox. Я предлагаю начать с dfms, т.к. иногда формы не открываются, если поля не определены правильно.

В КОДЕ (И dfms-, и pas-файлы)

Проблемы и их решение

 AsBoolean изменить на TStringField. В DFM, задать size =1
 AsDateField изменить на TDateTimeField
 IndexName изменить на IndexFieldNames
 DatabaseName проверить, чтобы все они обращались к старому Paradox- псевдониму. При первоначальном переносе у вас должен быть  только один псевдоним.
 .db, .dbf, etc Удалить расширения у ВСЕХ имен таблиц
 TDBCheckBox проверить, чтобы ValueWhenChecked, ValueWhenUnchecked соответствовали размеру поля. Если поле определено как CHAR(1) или VARCHAR(1), то установится значение "TRUE".

Если Вы используете Woll2Woll-компоненты, следует внести еще некоторые изменения.

wwDBComboLookup восстановить параметры поиска Lookup Property проверить ВСЕ псевдонимы и имена таблиц в поисковых строках, особенно в DFMs. В противном случае, ваша форма не откроется даже, если будет скомпилирована!

 wwDBComboLookup восстановить параметры поиска
 Lookup Property проверить ВСЕ псевдонимы и имена таблиц в поисковых строках, особенно в DFMs. В противном случае, ваша форма не откроется даже, если будет скомпилирована!

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

Оптимизация сервера

Лично я выполнял эту работу просто из интереса, т.к. мои приложения работали нормально. Но настало время сделать так, чтобы они заработали замечательно, переместив код из исполняемого приложения на сервер. Во многих отношениях это длительный процесс. В своем докладе я хочу затронуть некоторые наиболее важные области. По мере работы над проектом, я обнаружил много новых подсказок.

Предупреждение: Ваша первостепенная задача - сделать так, чтобы приложение заработало; вторая задача - оптимизировать код так, чтобы он мог использовать все преимущества серверной части системы. Очень заманчиво не выполнять эту последовательность и начать оптимизировать код еще во время переноса. Что же, но пусть это не собьет вас с верного пути...

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

  • Общий обзор
  • Синхронизация базы данных
  • Переход от TTables к TQueries
  • Генераторы ключей
  • Модель клиент-серверных событий с точки зрения Delphi

Общий обзор

В первую очередь: размещение кода. Это обязательное практическое правило: если код включает в себя поведение базы данных, установите его на сервере; в противном случае, установите его в Delphi. Например, для оценки данных используйте процедуры предварительной оценки и обновления на сервере. Для обновления экранов после записи используйте Delphi. Помните, что пользователь манипулирует значениями пользовательского интерфейса, а не значениями базы данных. Они станут значениями базы данных только после того, как будут в нее корректно записаны. Поэтому Вам придется полагаться на события Delphi и на события BDE-базы данных. Было бы замечательно, если бы OnNewRecord было событием баз данных, но это невозможно. Это означает, что для инициализаций новых записей вам придется использовать Delphi.

Возможно, проще было бы рассматривать интерфейс пользователя просто как шаблон для управления доступом к данным. "On New Record" представляет собой искусственное событие, позволяющее подготовить пользователя к заполнению шаблона ввода данных. Вы можете использовать хранимые процедуры для восстановления значений по умолчанию и тому подобных операций, но все равно, Вы будете просто подготавливать экран для заполнения его пользователем. Сервер активизируется только после того, как Вы начнете передачу отобранных данных. Затем Вы получите результаты (если таковые имеются) с триггера "before post". И снова, к сожалению, нет способа, позволяющего связать их вместе в единое полное сообщение. Сервер может только послать их обратно по одному (если только Вы не найдете время запрограммировать сервер на выполнение предложения "if then if then else else if then").

Кроме того, даже если применять те же правила, события при передаче новой записи отличаются от событий при обновлении старой. Триггер "before Post" используется только для обновления существующей строки. Чтобы применить эти правила к новой записи, потребуется триггер "before insert".

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

При работе с запросами следует уделять особое внимание как поддержанию сбалансированной производительности, так и синхронизации с пользовательским интерфейсом. При работе с TTables, BDE обеспечивает синхронизацию связанных курсоров. При работе с запросами дело обстоит совсем по-другому. Удаление строки не вызывает автоматического обновления другого запроса, которое отразило бы данное изменение. Это особенно верно при работе с окнами просмотра. Здесь обновление приходится выполнять вручную.

Связывание запроса через источник данных - это НЕ то же самое, что связывание с TTABLES через MasterSource и MasterField. Если Вы добавите строку в "связанный элемент", то вам также придется задать главные связующие значения. При использовании TTables, BDE сделает это за вас, автоматически добавив связующие значения для связанных ключевых полей. При работе с запросами Вам придется сделать это вручную, чтобы избежать ошибок противоречия ключей.

Главное, Вы должны принять для себя следующую установку: сервер должен работать, а Ваша программа должна отражать состояние данных на сервере.

Синхронизация базы данных

Одним из решений, которое Вам потребуется принять, связано с тем, как Вы будете отбирать данные с сервера. В IB отсутствует одно из свойств, поддерживаемых в Paradox. Это "автообновление" позволяющее всем пользователям почти сразу же видеть внесенные изменения. Для решения этой проблемы Вам придется сконфигурировать BDE.

В BDE Administrator, откройте закладку Configuration, затем последовательно откройте Drivers, Native, INTRBASE и установите DriverFlags на 512. Это установка для Repeatable Read/Hard Commit. Такая установка приводит к повышению производительности; однако, необходим компромисс, помогающий избежать взаимоблокировок (когда каждая из двух или более транзакций пытается заблокировать остальные), В результате, другие пользователи смогут видеть внесенные вами изменения и наоборот.

Переход от TTABLES к TQueries

Главное различие между Paradox- и клиент-серверными приложениями заключается в том, что Paradox-приложения поддерживают перемещение пользователя между таблицей/списком данных в одном окне и панелью с управляющими элементами редактирования, - в другом. Как только пользователь введет новую строку или внесет изменения в уже существующую, эти изменения сразу же отразятся в таблице данных. Обычно в клиент-серверном приложении список отображает результаты одного запроса, а управляющие элементы редактирования связаны с другим запросом. Следовательно, изменения, вносимые во второй запрос, НЕ будут автоматически отображаться в таблице данных. Несмотря на то, что для TQuery может быть скомпилирован Refresh-метод, он не будет работать. Для обновления списка Вам придется закрыть запрос и открыть его снова. А это не так просто. Если Вы проводили редактирование, то можете установить закладку. Если Вы добавили новую строку, то Вам самим придется искать эту новую строку...

  • Начните с установки TQuery-компонентов рядом с вашими Ttable-компонентами
  • Добавьте в SQL-свойство следующее: SELECT * FROM [TableName]
  • Установите RequestLive на True
  • Измените DataSet-свойство в DataSource для TTable на TQuery

Чтобы связать TQueries вместе в отношение Главный/Частный, используйте для Detail TQuery следующий SQL:

  • SELECT * FROM [TableName] WHERE (JOBID = :JOBID)
  • Затем установите DataSet-свойство в DataSource для Detail TQuery на DataSource, связанный с Master TQuery.

Это будет работать, но помните, что "SELECT * FROM ATABLE", по сути, ничем не отличается и не будет работать лучше, чем TTable! С течением времени Вы захотите оптимизировать запросы так, чтобы они выбирали только те строки и поля, которые Вам необходимы. Я предпочитаю создавать ОКНА, данные в которых пользователь может фильтровать, перемещаясь, таким образом, по базе данных; найдя нужные данные, пользователь нажимает кнопку Edit, в результате открывается форма со всеми редактируемыми полями. Поля будут содержать результаты запроса "SELECT * FROM ATABLE WHERE (KEYFIELD = :KEYFIELD)", а KEYFIELD-параметр будет взят из текущей строки окна.

Генераторы ключей

Это очень просто. Создайте генератор, напишите процедуру получения значения от генератора, а затем получайте это значение в OnNewRecord-событии в Delphi.

 CREATE GENERATOR ZKEYS;
 CREATE PROCEDURE SP_NEW_KEY
 RETURNS (ID INTEGER)
 AS
 BEGIN ID = GEN_ID (ZKEYS, 1);
 END
 Query1.OnNewRecord
 begin
 with Query1 do
   FieldByname('FIELD1').AsInteger := GetNewKey;
 end;

Модель клиент-серверного события с точки зрения Delphi

В Delphi, BDE и IB (или какой-либо другой клиент-серверной базе данных) поддерживаются разные потоки событий.

  • "Before"-события на сервере означают "до того, как действие может быть завершено".
  • "Before"-события в Delphi и BDE означают "до того, как действие будет запущено (или изменится режим)".

То есть "BeforeInsert"-событие в Delphi не соответствует IB "Before Insert"-событию. Фактически цепочку событий можно представить следующим образом:

  1. Delphi Before Insert
  2. Delphi On New Record
  3. Delphi After Insert
  4. Delphi Before Post
  5. InterBase Before Insert
  6. InterBase After Insert
  7. Delphi After Post

или

  1. Delphi Before Edit
  2. Delphi After Edit
  3. Delphi Before Post
  4. InterBase Before Update
  5. InterBase After Update
  6. Delphi After Post

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

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

Но это было еще до того, как я понял, как следует работать с триггерами.

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

 /* this table's field holds a Carriage Return */
   Create Table ZCR (CR CHAR (2)); 
CREATE TABLE X (
                      FIELD1 INTEGER NOT NULL,
                      FIELD2 ...,
                      ...
                      VALIDATED INTEGER,
                      PRIMARY KEY (FIELD1));
 CREATE TRIGGER TR_X_BI FOR X
   ACTIVE BEFORE INSERT
   AS
   DECLARE VARIABLE CR CHAR(2);
   DECLARE VARIABLE ERRS VARCHAR (255);
   BEGIN
   ERRS = '';
   SELECT CR FROM ZCR INTO :CR;
   IF (NEW.FIELD2 IS NULL) THEN
     ERRS = 'Field2 is required.'//:CR;
   IF (NEW.FIELDn IS NULL) THEN
     ERRS = :ERRS//'Fieldn is required.'//:CR;
   IF (:ERRS > '') THEN
     BEGIN
     NEW.VALIDATED = GEN_ID (ZERRORS, 1);
     INSERT INTO ZERRORLOG (ERRORID, ERRORMSG) VALUES
    (NEW.VALIDATED,
   :ERRS);
   END
   ELSE
     NEW.VALIDATED = NULL;
   END
   CREATE TRIGGER TR_X_AI FOR X
   ACTIVE AFTER INSERT
   AS
   BEGIN IF (NEW.VALIDATED)
   THEN
     BEGIN
     END
   END

В своем Delphi-приложении я использую следующее:

 function GetNewKey: LongInt;
 var sp: TStoredProc;begin
 sp := TStoredProc.Create (nil);
 with sp do
   begin
   DatabaseName := Framework.MasterAlias;
   StoreProcName := 'SP_GET_KEY';
   Params.CreateParam (ftInteger, 'ID', ptOutput);
   Prepare;
   ExecProc;
   Result := ParamByName('ID').AsInteger;
   UnPrepare;
   Free;
   end;
 
 Query1.OnNewRecord
 begin 
 with Query1 do
   FieldByname('FIELD1').AsInteger := GetNewKey;
 end;
 
Query1.AfterInsert;
 begin
 with Query1 do
   ParamByName('FIELD1').AsInteger :=
 FieldByName('FIELD1').AsInteger;
 end;
 
 Query1.AfterPost;
 begin
 with Query1 do
   begin
 {you have to close/open in order to see changes made by the
   server}
   DisableControls;
   Close;
   Open;
   EnableControls;
   if not FieldByName('VALIDATED').IsNull then
     begin
     SP_GET_ERROR.ParamByName('ID').AsInteger :=
       FieldByName('VALIDATED').AsInteger;
     SP_GET_ERROR.Prepare;
     SP_GET_ERROR.ExecProc;
     Framework.ReportServerError
       (SP_GET_ERROR.ParamByName('ERRMSG').AsString);
     SP_GET_ERROR.UnPrepare;
    end;
   end; 

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

Трудности

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

Что касается данного проекта, IB 4.2.2 является последней версией для NW4.11. В последующих версиях был исправлен ряд ошибок и внесены полезные изменения, которые отсутствуют в предыдущих версиях. К счастью, в первой половине 1999 года ожидается версия IB5.5 для NW4.11.

Главные проблемы, с которыми нам пришлось столкнуться:

  • Исчезающие запросы. Нельзя, чтобы пользователь прекращал работать с запросом, пока Вы не проверите, что этот запрос выполнен успешно.
  • В 4.2.2 запрещены изменения метаданных зарегистрированными пользователями. Этим Вы можете разрушить свою базу данных (что скорее всего и произойдет).
  • Точная настройка поведения не автоматизирована. Требуется различный подход со стороны программиста и со стороны пользователя, особенно если они привыкли иметь доступ ко "всем" данным.
  • Необходимо решить ряд вопросов, связанных с конфигурацией (например, аппаратное завершение, программное завершение, повторные считывания, максимально допустимое количество строк).
  • Иногда проскакивают ошибки между BDE, SQL-Links и IB. Они редки и противоречивы. Просто будьте внимательны.
  • "Очистка " и "сборка мусора". Отключите Auto-sweeping (автоочистку) сами. В противном случае, ваши пользователи могут оказаться втянутыми в гигантскую очистку в самый напряженный момент работы.
  • Выбор протокола. TCP/IP является наилучшим выбором, но потребуется работа для его установки и конфигурирования. SPX является простейшим вариантом, но при его использовании страдает производительность. (Нам пришлось использовать SPX из-за некоторых требований периферийного аппаратного обеспечения). С первой попытки соединение с некоторыми компьютерами может и не произойти, но при последующих попытках они будут соединяться быстро и надежно.

Достижения

Последовательность наших действий и полученные результаты:

  • Начавшаяся 22 сентября подготовка Delphi/Paradox-приложений к переносу;
  • весь процесс занял 2 недели
  • 3 октября всерьез начали работу с IB
  • 1 ноября готовы Dispatch/Billing, Operations, Crewing и HR
  • Первый кирпич в будущей системе: NOS не обновилась
  • Второй кирпич в будущей системе: выбран нужный протокол, и установлены рабочие станции
  • 4 из 4 законченных приложений полностью перенесены и, находясь в эксплуатации, работают согласовано.

Решены проблемы:

  1. блокировочных файлов
  2. устаревших индексов
  3. низкой производительности
  4. почти полностью завершена оптимизация сервера

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


Страница сайта http://www.interface.ru
Оригинал находится по адресу http://www.interface.ru/home.asp?artId=4204