Статья написана уже давно. Первая ее часть (анализ быстродействия для клиент-серверных реализаций) совершенно потеряла актуальность после выхода в свет Access-2000 и возможность разрабатывать ADP-приложения. Некоторые идеи второй части также претерпели существенные изменения с того времени
Некоторые соображения по поводу разработки крупных проектов на Access-97 (оригинал) (из личного опыта) Статья написана уже давно. Первая ее часть (анализ быстродействия для клиент-серверных реализаций) совершенно потеряла актуальность после выхода в свет Access-2000 и возможность разрабатывать ADP-приложения. Некоторые идеи второй части также претерпели существенные изменения с того времени. Условные сокращения: АГ - Андрей Гордиенко (автор статьи) КЛ - Лукьянов К.Ю. ИН - Ирина Николаева NSA - Сергей Новиков (сайт http://nsa.chat.ru) Андрей Гордиенко Ни в коей мере не претендую на знание истины в конечной инстанции. Хочу поделиться своими соображениями по поводу разработки крупных проектов. Возможно, кому-то эта информация поможет. Все сказанное ниже относится к Access-97 в составе русского MS Office-97. MS Access НЕ является подходящим инструментом для крупных проектов. Это первая и важная мысль, которую нужно "подумать" прежде чем браться за проект. КЛ: Не хватает определения крупного проекта, обсуждая с коллегами вашу статью, пришли к выводу, что не всегда проект с большим количеством таблиц, запросов, отчетов можно назвать крупным. АГ -> КЛ:Согласен. Но и дать четкое определение крупного проекта, по крайней мере я, не могу. Интутитивно полагаю таковым проект, разработка которого направлена на решение большого количества задач предметной области, среди которых имеются трудно поддающиеся систематизации. MS Access - очень удобный и простой инструментарий для быстрой разработки НЕБОЛЬШИХ приложений для небольшого количества пользователей в самые короткие сроки. Что ему не хватает для того, чтобы быть подходящим для крупных проектов? Вкратце они сводятся к набору основных отличий MS Access от SQL-сервера: NSA: С этим я КАТЕГОРИЧЕСКИ не согласен. К тому же сравнивать MS Access с SQL сервером нет смысла. В Access реализована структура файл-сервера. И, я думаю, нет смысла доказывать, что файл-сервер - это вам не SQL-сервер. Это и так ясно. Для больших проектов, естественно, необходимо использовать SQL-сервер. Для того, чтобы иметь право называться хорошим инструментом для разработки клиентской части в клиент-серверной архитектуре, необходимо иметь удобные средства для вызова хранимых процедур (иначе о каком тонком клиенте можно говорить). Не спорю - в Access можно вызвать хранимую процедуру, передать ей параметры, и получить от нее выходную информацию, но это делается "левой ногой через правое ухо". А ведь вызов хранимой процедуры для нормального инструментария - это одна строчка скрипта - Недостаточная надежность хранения данных. База данных может в любой момент разрушиться, и восстановить ее будет невозможно.
NSA: Честно говоря у меня надежность хранения данных никогда еще не подводила (не считая одного раза давным-давно в Access 2.0). Хотя нет. Было несколько раз. Электричество отключалось. После восстановления все работало отлично. Хотя теоретически (и практически) SQL сервер надежнее. > АГ->NSA: Относительно надежности хранения данных - у меня слетала база уже два раза. Причем, никакое "восстановление" и "упаковка" не помогли - только восстановление из архива. Слет происходит часто, если работает много пользователей (более 10) - и из них некоторые периодически аварийно завершают работу. Я полагаю, разрушение базы происходит в момент, когда данные по транзакции из временного файла переписываются в базу, но операция завершается аварийно. Довольно часто при этом слетают индексы (с возможностью восстановления сжатием базы). У меня возникло ощущение, что транзакции в Access не совсем соответствуют своему названию. На MAUG я видел много воплей о помощи тех, чьи базы разрушились без возможности восстановления. - Невозможность проведения операции архивирования данных без прерывания работы всех пользователей.
ЛК: Не знаю где это можно делать на лету или что- то не понял, а может имеются в виду какие сервисы SQL сервера. АГ-> ЛК: Совершенно верно, имеются в виду сервисы SQL сервера. ЛК: Я использую такой вариант: делаю специальную базу архив; а ней макрос типа Удалить таблицу1, Импортировать таблицу1, ит.д.; запускаю эту базу из BAT-файла при помощи START два раза, первый раз запускаю макрос который удаляет таблицы сохраненные в прошлый раз и импортирует заново, второй раз сжимаю. И фиолетово что кто-то работает, попутно у меня ситуация когда часть таблиц архивировать не обязательно. АГ-> ЛК:Предложенный метод архивирования не лишен оригинальности. Однако, он имеет существенные огрехи. А как же бизнес-модель в виде связей между таблицами и контроль логической целостности? По крайней мере в базе данных-приемнике, как я понимаю, их нет (иначе архивирование таким способом просто не пройдет). То, что процесс архивирования проходит без выдачи сообщений об ошибках может создать ложное ощущение безопасности, которое зачастую гораздо вреднее излишней обеспокоенности. Проблемы могут проявиться после операции восстановления архива, причем не сразу, а спустя длительное время, когда пользователи "наткнутся" на проявление нарушения логической целостности (а это уже снежный ком проблем - отделить информацию, восстановленную из архива от того, что к ней было впоследствии добавлено уже нелегко). Откуда же может взяться нарушение логической целостности? Представьте, что Таблица1 и Таблица5 связаны отношением один-ко-многим с контролем логической целостности. ПОСЛЕ того, как Вы скопировали Таблицу1 (пока копируются Таблицы2..4) пользователи добавили в связку Таблица1 - Таблица5 некоторое количество записей. Когда Вы копируете Таблицу5, в ней эти записи уже появились. Но в Таблице1, уже сохраненной в архиве, соответсвующих им записей нет! Я не утверждаю, что предложенный метод архивирования совсем плох. По крайней мере, он не приводит к накоплению подобных рассогласований между таблицами при многократных архивированиях. В тех случаях, когда требование непрерывности работы пользователей (круглосуточная эксплуатация) является преобладающим, а потерей некоторого количества информации в результате логического рассогласования таблиц можно пренебречь, он, возможно, и подойдет. Мне обычно приходится работать с системами, в которых утеря даже одной записи совершенно недопустима. - Отсутствие интеграции встроенного механизма защиты MS Access с защитой на уровне операционной системы. По этой причине каждый пользователь вынужден будет два раза "осуществлять вход" - один раз в сеть, второй раз в Access, причем в общем случае он может при этом использовать разный Login и пароль. Это может показаться не очень существенным, но пользователей это может раздражать.
- Ограничение числа пользователей. Файл-серверная технология, в соответствии с которой строится работа в многопользовательском режиме, ограничивает количество одновременно работающих пользователей числом 10 - 15 человек (для 10-мегабитной сети может оказаться достаточной 3 - 5 человек). При большем количестве происходит перегрузка локальной сети до такой степени, что время реакции системы на действия пользователя начинает превышать любые мыслимые и немыслимые границы.
NSA: Тут мне тоже трудно судить. У меня в Сочи работает одновременно ~10 ползователей. Часть на 10-мегабитной, часть на 100-мегабитной. "Тормозов" не ощущается. Хотя при работе по технологии файл-сервер действительно - чем больше пользователей -> тем больше потоки данных по сети-> тем больше загрузка сети. - Ограничение на размеры таблиц. Не смотря на то, что в таблицы может быть записано теоретически 2^32 (размер поля "счетчик") записей, реально при количестве записей порядка ста тысяч возникают весьма существенные задержки (до десятков секунд) при обращении к этим таблицам. Связано это опять-таки с файл-серверной технологией. SQL-запросы выполняются на рабочем месте клиента. Для того, чтобы запрос мог выбрать 10 записей из 100 000, он должен в общем случае загрузить с сетевого диска в память рабочей станции все 100 000 записей по сети. Пропускная способность сети - это то узкое место, на которое вы можете наткнуться даже при количестве пользователей, равном единице.
- На роль клиента какого-либо SQL-сервера MS Access также мало подходит. Дело в том, что работать с SQL-сервером он может только через ODBC-драйвер, который существенно замедляет обращение к данным. Простое перемещение к последней записи набора данных по запросу к SQL-серверу "select * from MyTable" может привести к задержке почти в минуту, если таблица содержит порядка сотни тысяч записей. Кроме того, вы не сможете так легко и свободно использовать вызов хранимых процедур, принимающих и возвращающих данные через параметры. Для этого придется заниматься программной "эквилибристикой". Для этих целей больше подходит продукт, который может работать по именованным каналам (протокол Named Pipes), например, Delphi.
- Крупный проект, как правило, требует работает множества программистов над одной крупной задачей, разбиваемой на модули. MS Access для этого плохо приспособлен.
Однако, не смотря на выше перечисленные аргументы, вы либо ваше руководство может, тем не менее, прийти к решению разрабатывать крупный проект средствами MS Access. Причин тому может быть множество, среди которых: - Доступность и низкая цена
NSA:Низкая цена чего? Access-а? Или услуг программистов? - Менее жесткие требования к квалификации программиста
NSA: Во-первых я не согласен с тем, что программисты на Access менее квалифициронные, чем, например, на Delphi. Специалист - он специалист и есть. "Беда" Access-а в том, что любой моло-мальски грамотный человек может "слепить" простенькую базу данных с помощью мастеров, не зная при этом ни строчки кода. Даже, кстати, и не подозревая о его существовании. И после этого он считает, что уже готов написать кому-то что-то за деньги. Кстати действительно квалифицированных программистов на Access - единицы. Я знаком лично только с двумя (не считая себя). А услуги квалифицированного программиста ВСЕГДА СТОЯТ ДОРОГО. Мне, кстати, интересно посмотреть на то "руководство", которое решит: "Нам нужна база. Пусть ее напишут на Access. Квалификации-то большой для этого не требуется." Это как надо себя не любить! АГ->NSA: Относительно квалификации программистов - честно говоря, я ожидал подобную реакцию, хотя ни в коей мере не хотел никого обидеть. Тем не менее, хочу напомнить, что для того, чтобы пользователь смог увидеть тривиальное TreeView в форме, сделанной программистом, нужно, чтобы этот программист кроме Access знал что-то еще. В Delphi этого стандартный инструмент, не требующий никаких изворотов. Сразу уточню, что я не являюсь сторонником Delphi - я просто констатирую факт, стараясь быть объективным (хотя я злостный поклонник именно Access). Программист, сумевший нормально вставить в проект TreeView, да еще так, чтобы этот проект смог нормально устанавливаться на любом компьютере - это уже АС. Но речь идет не только и не столько об этом. Сравните количество времени, нужное "чайнику", знающему только начала Бейсика, для изучения Access и того же C++ с тем, чтобы он смог сделать простейшую программку по ведению домашнего бюджета. Естественно, программист, который сможет это сделать на C++, сможет сделать и многое другое, чего напрямую средствами Access не реализуется. Вот что я имел в виду, говоря о квалификации. - Интеграция в Access как возможностей для построения интерфейса клиентской части (чего нет, к примеру, в MS SQL SERVER 6.5), встроенных case-средств (чего нет в том же MS SQL SERVER 6.5), мощного механизма по созданию и модификации серверной части (в архитектуре файл-сервера). К примеру, в том же MS SQL SERVER 6.5 невозможно так просто, как это делается в Access изменить настройки ключевых полей после того, как таблица уже создана - нужно заново создать аналогичную таблицу, скопировать вручную данные из исходной таблицы, после чего удалить исходную таблицу.
- Высокая скорость разработки приложения
Таким образом, если вы все-таки решили использовать MS Access, то следующие усилия должны быть направлены на преодоление перечисленных выше недостатков MS Access. А именно: - Продумать технологию параллельной работы программистов над проектом
- Продумать механизм автоматического архивирования данных
- Продумать механизмы защиты информации и администрирования
Использование встроенных механизмов защиты "в лоб" в крупном проекте настолько неудобно, что почти невозможно. Помножьте тысячу таблиц, столько же запросов, несколько сот форм и отчетов на пару десятков групп пользователей. Сколько потребуется администратору сделать шевелений мышкой, чтобы корректно распределить права доступа? К тому же, ни один нормальный человек не сможет удержать в голове и оперативно отследить взаимодействие всех объектов такого крупного проекта (всекие уникумы, перемножающие в уме 50-значные числа не в счет) . Только по этой причине администратор может оставить несколько дыр в защите и "перекрыть кислород", там где этого делать нельзя. Во второй части статьи содержатся конкретные предложения по решению этой проблемы. В дополнение к сказанному небольшая фантазия на ситуацию из жизни: Вам (администратору) звонит руководство и требует дать объяснения по поводу того, почему некий Иванов имеет доступ к секретной информации, к которой он доступа иметь не должен. Ваша реакция? Сначала Вы должны убедиться в том, что Иванов действительно имеет доступ к секретной информации. Для этого Вы входите в систему под именем Иванова, пытаетесь получить доступ к информации и убеждаетесь в том, что в защите действительно имеется дырка. После этого вы должны вычислить, каким образом у Иванова оказываются завышенные права доступа. Вы проверяете перечень групп, в которые входит Иванов и выясняете, что он входит в состав 8 групп пользователей. После этого Вы должны проверить права доступа к нескольким объектам системы (вспомнить бы еще - к каким) по каждой из 8 групп пользователей - чтобы выяснить, через какую из этих групп пользователь получил права доступа. Проверяя 5-ю группу, вы обнаруживаете, что она действительно имеет права доступа к секретной информации. Вы эти права снимаете, и сообщаете руководству, что проблема устранена. Спустя несколько минут у Вас без конца начинает трезвонить телефон - другие члены 5-й группы (не Иванов) на повышенных тонах пытаются выяснить, на каком основании у них убрали права доступа к нужной в работе информации. Пока Вы пытаетесь разобраться с этой ситуацией, Вам повторно звонит руководство и еще более гневно повторяет вопрос: "Почему Иванов по-прежнему имеет доступ к секретной информации, ведь Вы сказали, что проблема решена?". Спустя какое-то время тщательная проверка показывает, что Иванов имеет доступ к этой же информации еще и как член 8-й группы. А теперь представьте, что таких Ивановых - 200 человек, Вы -системный администратор, и у Вас вычитают половину месячной зарплаты за каждый обнаруженный в защите прокол. В своем модуле администрирования я предусмотрел средства для просмотра всех разрешений на доступ к макрообъектам всех пользователей (просто в форме без муторных операций входа в систему под чужим имененм) с выводом перечня групп, через которые эти разрешения предоставлены. Имеются что-либо подобное во встроенных в Access средствах защиты? Кроме того, под макрообъектом подразумевается СОВОКУПНОСТЬ объектов Access. А администрирование через модуль администрирования позволяет в самой системе задать схему взаимодействия объектов и правила распределения разрешений с уровня макрообъектов на объекты Access - поэтому администратору не требуется быть уникумом, перемножающим в уме 50-значные числа. - Продумать технологию интеграции объектов системы таким образом, чтобы сходные по функциям объекты делать на базе шаблонов (заготовок), а не с нуля
- Продумать технологию развития проекта после того, как первые его части введены в эксплуатацию
- Продумать технологию перехода к архитектуре "клиент-сервер" в далеком или не очень далеком будущем
О технологии "клиент-сервер" Возможно, вы тешите себя надеждой на то, что в дальнейшем сможете перевести свой проект на технологию "клиент-сервер". В таком случае необходимо заранее построить структуру проекта таким образом, чтобы его можно было перевести на работу с SQL-сервером наименьшими усилиями. Если вы полагаетесь на всякого рода мастера или самостоятельные case-средства (например, ErWin), то ожидания вас могут обмануть. Я пытался использовать MS Upsizing Tools v 8.10.0128 для перевода серверной базы данных (содержащей только таблицы и связи между ними) в MS SQL Server v 6.5. Не то, чтобы СОВСЕМ ничего не вышло, но по крайней мере половина информации перенеслась некорректно. В частности, не на всех таблицах установился PRIMARY KEY там, где он стоит в Access. Некорректно построились поля типа "счетчик" - в SQL Server они потеряли способность к автоинкрементации. Подозреваю, что проблемы связаны с несовместимостью данного визарда с русским Accessом, хотя все таблицы и наименования полей я задавал только латинскими буквами. Необходимость "перелопачивать" вручную после визарда все самому плюс отсутствие возможности изменить настройки ключа на уже созданной таблице (см. выше) приводит к тому, что трудоемкость перевода таблиц с использованием визарда соизмерима с трудоемкостью "рукопашного" перевода. Пытался построить структуру таблиц и связей между ними в ErWin, а затем качнуть их в Access и в SQL Server. В Access качает терпимо (особых нарушений логики я не заметил). А вот в SQL Server - врет. Толи я с ним плохо разобрался, то ли его плохо кракнули. Есть там еще одна проблема. На сколько я понял, он годится только для стартовой подготовки проекта. Если таблицы уже заполнены информацией, и вы производите модификацию проекта параллельно с его эксплуатацией - для этих целей он вообще не годится. Ну и кроме того, он однобокий и работает только в направлении ОТ ErWin. Структуру базы, изначально сделанной в Access, качнуть в ErWin мне не удалось. Таким образом, на сегодняшний день мне не известны средства, позволяющие легко и просто перевести серверную БД Access в БД SQL Server (возможно, они известны кому-либо из читателей - с удовольствием выслушаю советы и пожелания). Есть слабая надежда на то, что к моменту подрумянивания проекта такие средства найти удастся. Однако, сильно на это я не рассчитываю, и полагаюсь больше на собственные силы. А по сему собственный проект разрабатываю таким образом, чтобы "рукопашный" перенос в SQL Server был наименее болезненным. Для этого в одном из модулей сделал глобальные функции обращения к запросам и таблицам через объект Recordset. В специальной "системной" таблице заведен перечень всех остальных таблиц и указано место нахождение таблицы - в БД Access или БД SQL Server. В дальнейшем при переносе таблицы на SQL-сервер достаточно будет поставить галочку в соответствующей записи "системной" таблицы, чтобы все программные обращения к "переехавшей" таблице переадресовать на SQL Server. Аналогично с SQL-запросами. Функция программного создания объекта Recordset на базе "на ходу компонующегося" текста SQL принимает дополнительный параметр, через который сообщается место запуска запроса. В зависимости от источников данных запрос может быть запущен в Access, а может быть переадресован серверу. Поскольку диалекты языка SQL в Access и на сервере различаются, добавляю минимальный синтаксический анализатор, который модифицирует уже сформированный запрос на SQL Access в Transact-SQL (убирает завершающую точку с запятой, заменяет конструкции с nz() и т.п.). При программировании в среде Access нужно сразу учесть ограничения SQL-сервера. В частности, он сможет открыть только на чтение запрос, в котором есть фраза SORT BY (а Access может и на запись!). Перевод на SQL-сервер серверной части Access-ного проекта (БД, содержащая только таблицы и связи между ними) может не только не привести к существенному ускорению в работе, но и замедлить работу, поскольку обращение идет через ODBC-драйвер, а объемы передаваемой по сети информации не уменьшатся. Для получения ускорения придется переводить на SQL-сервер все запросы (в форме View), по возможности использовать курсоры и хранимые процедуры. Одним словом, переносить существенную долю обработки данных с клиентской части на серверную (это принято называть переходом от технологии "толстого" клиента к технологии "тонкого" клиента). Такой переход должен производиться поэтапно. На первом этапе производится постепенный перенос таблиц без получения существенного выигрыша в быстродействии. На втором этапе - все запросы переадресовываются на сервер (это может еще больше снизить быстродействие). На третьем - придется переводить запросы в View и хранимые процедуры сервера. Это довольно трудоемко, но только на этом этапе реально получить выигрыш от перехода на технологию "клиент-сервер". Проводился такой эксперимент. Создавали таблицу на MS SQL Server 6.5 объемом около 300000 записей. Затем пытались интерактивно с ней работать из Access двумя способами: - Через связанную таблицу (линк)
- Через запрос к SQL серверу с текстом: select * from MyTable
- Через линк к View с текстом: select * from MyTable
В первом и в третьем случае перемещение к последней записи производилось почти мгновенно. Во втором - с задержкой на пару минут. Я пришел к выводу, что работать SQL сервером лучше через линки к View, которые "видны" в Access как присоединенные таблицы. NSA:Про средство перевода Access на SQL Server. В SQL Server 7.0 оно есть. Встроенное. У меня все заработало с первого раза. С SQL Server 6.5 сложнее. Но тоже выход есть. В ближайшее время выложу на страницу программу перевода таблиц и связей Access 97 в SQL Server 6.5. Эта программа - дипломный проект Евгения Митяшева. Сам я ей еще не пользовался, но по отзывам работает без ошибок и делает все, что от нее требуется. Причем корректно. О скорости работы. При переводе базы на SQL Server 7.0 (прилинкованные таблицы) при "серьезном" объшете данных для статистики (вложенные груповые запросы 7-и уровней) удалось получить результ, превсходящий по скорости "родные" прилинкованные таблицы Access-а в 20-25 раз. О базовой структуре системы, ее объектах и администрировании Откровенно говоря, я бы не взялся за Access, если бы нормально все работало в 1С:Предприятии v 7.5 (с SQL-сервером). Просто великолепная концепция этого продукта полностью перечеркнута качеством его реализации. Такое количество ошибок и глюков отсутствует даже в микромягких бета-версиях. Попытка наладить надежный контакт с производителем для устранения выявленных ошибок не удалась не смотря на то, что продукт был приобретен официально. Однако, концепция объектов там выбрана очень красиво, и при программировании на Access я стараюсь придерживаться тамошних базовых понятий, хотя привношу собственные идеи и подходы. Вкратце дело обстоит так. Базовые объекты проекта должны быть не "таблицы", "запросы", "формы", "макросы" и "модули", а "справочники", "регистры", "журналы", "документы", "отчеты". Эти понятия гораздо ближе к предметной области (далее они называются "макрообъекты"), и по этим понятиям легче всего разбить проект на модули, чтобы убить сразу трех зайцев: - Обеспечить параллельную работу над проектом нескольких программистов
- Ускорить разработку, используя заготовки (шаблоны) объектов
- Обеспечить удобное администрирование в дальнейшем
Ниже вкратце рассмотрена структура и назначение каждого макрообъекта. Справочник - это просто перечень чего-либо, в общем случае открытый для пополнения и корректировки. Пример - справочник контрагентов, справочник номенклатуры. Он предназначен для построения аналитических разрезов регистров и журналов, для ссылок из других справочников (нормализация и удобство работы), для ссылок из документов. В общем случае он может быть иерархическим, причем с большим количеством уровней иерархии (группы товара - подгруппы товара - :. - номенклатурная позиция). Одни справочники могут "подчиняться" другим. К примеру, справочник "Города" может подчиняться справочнику "Страны". В этом случае каждая запись справочника "Города" имеет внутреннюю привязку к определенной записи справочника "Страны". При выборе записи из справочника "Города" сначала необходимо выбрать запись в справочнике "Страны", после чего в подчиненном справочнике производится выбор из числа только тех городов, которые находятся в указанной стране. Для иерархического справочника необходимо задать правила его ведения. Можно ли на одном уровне размещать группы и простые элементы, задать перечень полей, указать, какие из них используются только для групп, какие только для простых элементов, а какие в обоих случаях, какие из полей работают в механизме наследования. Поскольку проект крупный, наверняка он выполняется для солидной конторы, для которой фраза "коммерческая тайна" не является пустым звуком. Посему необходимо разграничение доступа к справочникам на уровне записи. Механизм защиты на уровне записи должен предусматривать возможность закрытия от редактирования верхних уровней иерархического справочника определенным группам пользователей (иначе какой-нибудь кладовщик случайно может удалить или испортить наименование целой группы товаров). Если в справочниках содержится конфиденциальная информация, для определенного круга пользователей даже на просмотр должно выводиться не все содержимое справочника, а только разрешенное ему для просмотра. Следует сказать пару слов о механизме наследования. В номенклатурном справочнике для группы товара может быть задан перечень технических характеристик (выбирается из справочника "Характеристики"). При добавлении любых записей в номенклатурную группу "Насосы" они автоматически наследуют перечень характеристик из родительской записи. При этом остается возможность его откорректировать, уточнить и дополнить. К примеру, для подгруппы "Насосы К8/18" можно задать значение некоторых характеристик: высота=0,321м, ширина=0,257м, длина=0,768м, масса=64кг. При дальнейшем вводе конкретного насоса в эту подгруппу он наследует перечень характеристик ВМЕСТЕ С ИХ ЗНАЧЕНИЯМИ. Для характеристик, значения которых не заданы, они запрашиваются в диалоге (поскольку предполагается, что для простого элемента справочника они должны быть заданы). Механизм наследования позволяет сильно облегчить работу по заполнению справочника и его ведению (подправить пару технических характеристик проще, чем вводить весь их перечень от начала до конца и задавать значения). К слову, поиск товара по характеристикам с учетом имеющихся остатков - задача, которую я не видел реализованной ни в одной тиражируемой системе оперативного учета. Для устранения возможной путаницы, ошибок ввода и т.п. при ведении иерархического справочника, для вновь созданной группы должны фиксироваться правила ввода информации внутри этой группы (допускается ли ввод подгрупп, простых элементов, или и того и другого), а также справочная информация для облегчения дальнейшей группировки. К примеру, руководитель отдела ввел в номенклатурный справочник группу "Насосы" и указал, что внутри этой группы должны формироваться только товарные подгруппы (не наименования конкретных насосов!), причем подгруппы формируются по признаку ("Марка насосной части"). Когда рядовой сотрудник захочет добавить в группу "Насосы" новый насос, у него перед глазами "повиснет" информация о том, ЧТО он должен вводить на следующем уровне - "Марка насосной части!". Волей-неволей отпадет желание группировать товар на этом уровне по мощности двигателя. Представляете, что будет в справочнике, если каждый начнет группировать по одному ему ведомым критериям? Справочник на первый взгляд кажется объектом, хранящим информацию статического плана. Однако, это не совсем так Допустим, у вас есть справочник контрагентов, и в нем запись "Фирма ЛТД", которая задействована в каких-то документах, регистрах и т.д. Через два года контрагент присылает вам извещение о том, что он изменил название на "Фирма ДДТ". Что выбудете делать? Откорректируете запись справочника? Тогда при попытке распечатать любой старый документ в нем будет фигурировать новое название. Если оставите старое наименование, оно будет распечатываться в новых документах и тоже все испортит. Если вы создадите отдельную запись с отдельным наименованием, система никогда не догадается, что обороты по двум разным контрагентам "Фирма ЛТД" и "Фирма ДДТ" необходимо сгруппировать вместе, поскольку на самом деле это один и тот же контрагент. Подобных примеров можно привести множество (смена фамилии при выходе замуж, смена ставки налога и т.д.). Справочник должен иметь возможность корректно отрабатывать подобные ситуации. Ну и желательно предоставить администратору пути выяснить в дальнейшем, кто ввел в справочник некорректную запись или удалил жизненно важную информацию. ИН: Смена названий фирм заказчиков. Пусть несколько лет назад был заказчик "ЛТД". В отделе заказов работает другой человек, который не знает, что был когда-то такой заказчик. Приходят заявки от фирмы "ДДТ", эта запись будет введена, как новая. Т.е. ошибочное введение двух записей будет все равно, просто по незнанию. АГ->ИН: Приведенный тобой пример лишь частный случай подобных рассогласований, причем достаточно редкий. Более часто встречается другой. Один юзер ввел в справочник "ЗАО ДДТ", а другой в этот же день "ДДТ ЗАО", причем под разными идентификаторами - как две разные организации. А на самом деле это одна и та же организация. Увидеть такую ошибку трудно, особенно если записей в справочнике очень много - они попадут по алфавитной сортировке в разные концы справочника. Такая проблема действительно существует, и она неразрешима в принципе никакими методами программирования. Решается она только частично (снижением вероятности возникновения подобных ситуаций) и только организационно. Из практики нашей фирмы это решается вбиванием в мозги каждому юзеру жестких правил формирования записей в справочнике - например, части наименований, касающиеся организационной формы (ЗАО, ООО, ИЧП, СП и т.д), вида деятельности (НПП, НПО, УЦ и т.д) проставляются всегда в конце. А корень наименования идет в начале. Тем не менее, остается вероятность разного выделения корня наименования разными сотрудниками из некоторых заковыристых наименований (вроде "СП НПП РКЦ УКНЦ ГлавРыбСнабСбытЦелюлозБумага имени левого поворота на Бабушкино ВТФ металлических изделий"). ИН: Через некоторое время выясняется, что "ЛТД" и "ДДТ" одно и то же.Т.е. должна быть функция, которая присваивает двум записям одинаковые идентификаторы. я правильно думаю? АГ->ИН: Правильно. Только не все так просто. В своей разработке я не использую возможностей сливания двух разных записей справочника в одну, хотя в принципе это и можно сделать. Дело в том, что такая операция не отразится на документах, и станет трудно выявляемой. В моей системе это делается корректировкой всех электронных документов, которые сослались на "ДДТ" - их идентификаторы аналитик переустанавливаются на "ЛТД". При корректировке документов автоматически уничтожаются все обороты регистров, сделанные по прежней редакции документов. И создаются новые. Далее запись справочника "ДДТ" оказывается "сиротой" - на нее не привязан ни один документ. Ее удаляют. Она удаляется логически, но пользователя это мало интересует. Зато это интересует руководителя, который может поднять из истории прежние редакции всех измененных документов (ссылающиеся на логически удаленную запись справочника) и определить автора произведенных модификаций. Когда запись "ДДТ" логически удалена, можно исправить запись "ЛТД" на "ДДТ". При этом в прежних учетных периодах она будет выглядеть как "ЛТД", а в новых - как "ДДТ". Предложенное тобой решение проблемы позволяет выявить произведенные изменения только через журнализацию справочника. Тому, же, кто не заинтересовался журналом изменения записей справочника, а только недоуменно смотрит на документы (ранее оформленные на другую фирму) и не содержащей в журнале-истории никаких изменений придется теряться в догадках, что, собственно, произошло. Все это делается с помощью концепции "не удаляй физически". Любая запись любого справочника имеет "системные" поля, в которые записывается специальная информация - календарная дата и время создания, учетная дата и время создания (начиная с которой запись считается действительной в учете), ссылка на пользователя, создавшего запись, календарная дата и время удаления, учетная дата и время удаления (начиная с которой запись считается недействительной в учете) и ссылка на пользователя, удалившего запись. Если запись справочника действительна в настоящий момент, то три последних поля пустые. Если кто-либо производит корректировку записи, старая запись помечается как удаленная, причем фиксируется дата календарная, учетная и автор удаления (кто корректировал), а в справочнике автоматически появляется новая запись с тем же самым ID, но с новым содержимым полей. Первые три "системные" поля у нее совпадают с содержимым трех последних "системных" полей в удаленной записи. При многократных корректировках создается множество записей, причем в любой момент учетного времени не более одной из них может считаться действительной. Если же запись удаляется пользователем, она помечается как удаленная (с записью соответствующей информации в системные поля), а новая более не создается. При такой схеме не возникает проблемы с исправлением "Фирмы ЛТД" на "Фирму ДДТ". Справочник сам спросит, с какого учетного времени зафиксировать это изменение. В прежних учетных периодах во всех документах действительной будет фигурировать запись "Фирма ЛТД", а в новых "Фирма ДДТ". Поскольку у записей одинаковый ID, и обороты по ним будут собираться как по одному контрагенту. А если системный администратор захочет посмотреть, кто когда что изменял или удалял, у него всегда есть такая возможность - эта информация лежит в самом справочнике, и не нужен отдельный LOG на каждый чих (в который, по сути все записывается по второму разу, да еще пойди найди то, что нужно:). КЛ: У меня вопрос: как быть со связями один-ко-многим, и всеми прелестями с этим связанными типа целостность, неужели все опять самому программировать. АГ ->КЛ: Связи действительно несколько усложнятся. Они будут уже не на уровне таблиц, а на уровне запросов к таблицам. А прелести, о которых Вы говорите, действительно упрощают жизнь программиста, но в прикладной области привносят неразрешимые проблемы, о которых программисты, плохо вникнувшие в предметную область, не задумывается (либо по неведению или нехватке времени, либо специально в защитных целях - чтобы сильно себя не утруждать). Я уже много шишок набил обо всякие подводные камни и со временем стал относиться к предметной области как к святыне, рабом которой быть обязан. ИН: В статье вкратце описан механизм подмены физического удаления/модификации записей на логическое удаление и добавление новых записей. Насколько я понимаю, при таком механизме неизбежно появление огромного числа записей, которые отличаются друг от друга только датой создания. Пользователь чисто психологически не может нажать кнопку "Закрыть без сохранения", даже если ни каких изменений не было!!! Т.о. переполнение ненужными записями. Или проверка, действительно ли были произведены изменения, что приведет к увеличению времени выполнения. Выход? АГ -> ИН: Ну, это же совсем просто :). Кнопку "сохранить" можно сделать доступной только в том случае, если данные на самом деле изменились (Dirty=true). Это во-первых. Во-вторых, для многострочных документов совсем не обязательно для каждой новой редакции сохранять копии всех строк - и тех, которые изменились, и тех, которые не изменялись. Достаточно вести историю по каждой отдельной строке документа. А история всего документа будет выглядеть как группировка времени модификации всех подчиненных строк (по всем их историям). Для того, чтобы получить состояние документа на определенную дату, доастаточно отфильтровать из истории записи, действовавшие на нужный момент - и всё! Есть немаловажный момент. История изменения всего, что лежит и лежало в регистрах не ведется. Если в какой-то документ вносятся изменения то происходит следующее: 1. Изменяется статус документа из состояния "беловик" в состояние "черновик". При этом из регистров уничтожается все движение, произведенное данным документом. 2. Документ модифицируется пользователем. Это может быть длительный процесс с многократными сохранениями, перерывами в работе и т.д. При этом никаких изменений в регистрах и в истории модификации документа не происходит. Для всех остальных пользователей документ доступен только на просмотр. Модифицировать его может только тот, кто установил на нем метку "черновик". 3. Когда модификация документа закончена, на весь документ устанавливается метка "беловик". В этот момент происходит журнализация изменений документа, причем только по тем его строкам, которые действительно претерпели изменение. И одновременно производится обработка события "ввод документа", которое приводит к формированию соответствующих движений по регистрам. Движение регистра вторично и однозначно определяется алгоритмом обработки события (ввода/коррекции документа). Вполне достаточно вести журнализацию ИСТОЧНИКОВ движения. Необходимо учесть, что справочник может иметь несколько визуальных форм. Например, подробный для просмотра (основная часть GRID-образная), краткий для выбора (основная часть GRID-образная), для ввода/корректировки простого элемента (бланкоподобная на одну запись), для ввода/корректировки группы (бланкоподобная на одну запись), для вывода на печать (отчет!). Запись справочника идентифицируется уникальным ID. Справочник содержит некоторую совокупность объектов MS Access (таблиц, форм, запросов, отчетов и т.д.). В специальной "системной" таблице прописывается набор компонентов Access, из которых строится конкретный справочник. Специально помечается главная форма (используемая для просмотра и выбора). Специально помечается главная таблица (в которой хранится ID записи справочника). Через эту же "системную" таблицу, хранящую описание компонентов справочника, производится администрирование на уровне объектов типа "справочник" (права доступа). В связке с этой "системной" таблицей хранится схема размножения прав доступа к справочнику на компоненты уровня Access. Если в дальнейшем администратор будет изменять права доступа к справочнику, специальный модуль администрирования сам найдет все формы, таблицы, запросы и т.д. и изменит к ним права доступа на уровне MS Access. Как обычно, проект разбивается на серверную часть (с таблицами) и клиентскую часть (все остальное). При изменении прав доступа пользователей к справочнику модуль администрирования прописывает права доступа к таблицам в серверной части и к линкам в клиентской части, за которой работает администратор. Кроме того, он корректирует права доступа к запросам и формам в клиентской части, ЗА КОТОРОЙ РАБОТАЕТ АДМИНИСТРАТОР. После этого в клиентской части в специальной таблице VERSION увеличивается на единицу номер версии, а сама клиентская часть копируется на сетевой диск, с которого должно производиться ее "размножение" по остальным пользователям. При очередном запуске приложения с места какого-нибудь кладовщика запускается не сама клиентская часть, а небольшой MDB-файл, который сравнивает номер версии клиентской части на сетевом диске "для размножения" с номером версии клиентской части на локальном диске самого кладовщика. Если версии не совпадают, запускается копирование клиентской части с "диска размножения" на локальный диск пользователя, после чего свежескопированной клиентской части передается управление. Так производится автоматическое обновление версий клиентской части и размножение прав доступа к объектам по всем клиентским частям. Может, все это работает не так оперативно, как хотелось бы, но лучше придумать не удалось. КЛ: А я запускаю задачу BAT-файлом, который всегда копирует клиентскую часть на локальный диск пользователя, помимо перечисленных удобств это избавляет от необходимости сжимать клиентскую часть. Также можно смело создавать временные таблицы, при необходимости, прямо в клиентской части. АГ->КЛ: Очень простое и изящное решение. Возможно, оно кому-то больше подойдет. У него, правда, есть один небольшой минус. Запуск приложения сопровождается неизбежной задержкой на копирование клиентской части даже тогда, когда в этом нет необходимости. В моей схеме такая задержка возникает только в случае, если действительно возникла необходимость в копировании. Некоторые "ушлые" пользователи могут сами подкорректировать и BAT-файл, и ярлык, и все остальное. Для ускорения запуска программы они могут в тайне от Вас закомментировать в BAT-файле команду копирования - и Ваша схема начнет давать сбои. Есть один нюанс, о котором в статье я умолчал. Номер версии прописывается не только в клиентскую часть на диске для размножения. Он прописывается и в серверную часть. При попытке запуска клиентской части с несовпадающим номером версии (проверяется по данным серверной части) запуск клиентской части прерывается. Это как раз для защиты от "ушлых" пользователей. С помощью такого подхода решается еще одна серьезная проблема - взаимные помехи работающих пользователей и развивающего систему программиста. Чаще всего изменения производятся именно в клиентской части. Программист может снять копию с действующей клиентской части, внести в нее изменения, переустановить номер версии и "вывалить" на сетевой диск для размножения. Размножится по пользователям она сама. Пара слов о том маленьком MDB-файле, который сверяет и копирует версии. Сам он не обновляется. Поэтому именно в нем я храню индивидуальные настройки пользователя. В частности, там сохраняется информация о том, в какие места каких справочников пользователь заглядывал в последний раз. В связи с этим, даже после смены версии, справочник у пользователя развернется именно на том месте, где он его "смотрел" в последний раз. Это исключает лишние шевеления пользователя, связанные с навигацией по справочнику для попадания в нужный ему раздел при каждом его открытии (представьте, что некий коммерсант работает в основном только с одной группой товара - зачем его заставлять каждый раз искать в справочнике эту группу?). Любое обращение к справочникам производится через вызов глобальной функции, которой передается имя справочника, код действия (выбор, просмотр/редактирование и т.д.) и место начального позиционирования. Регистр - это аналог бухгалтерского счета. На нем учитывается ДВИЖЕНИЕ ресурсов. Под ресурсами понимаются деньги, материальные ценности, ценные бумаги, дебиторская и кредиторская задолженность и т.д. По одному регистру может вестись учет движения одновременно нескольких ресурсов (например, суммового выражения товара в рублях, в валюте, и количества в натуральном выражении - итого 3 ресурса). Для отслеживания движения в определенных аналитических разрезах, у регистра назначаются аналитические уровни (в виде набора ссылок на справочники). Структура регистра должна быть построена таким образом, чтобы легко и удобно можно было строить запросы типа "каков остаток такой-то номенклатуры товара (в рублях, валюте, в штуках) на всех складах на такую-то дату?", "сколько пришло такой-то номенклатуры товара (в рублях, валюте, в штуках) на такой-то склад с такой-то по такую-то дату?", "сколько ушло товара в суммовом выражении всех номенклатур со всех складов с такой-то по такую-то дату?" и т.д. Кстати, в данном случае, "Склад" и "Номенклатура товара" выступают в качестве аналитических разрезов регистра "Товар" в виде ссылок на соответствующие справочники. Очевидно, что объем регистра может очень быстро расти. Типичная проблема - как достаточно быстро получить остатки по регистру на любую учетную дату, если отражается в нем только движение? Обычно регистр дробится на отрезки определенной протяженности, между которыми фиксируются промежуточные значения остатков. Фиксировать остатки после КАЖДОГО движения не рационально - слишком много в регистре будет информации, которую можно получить расчетным путем, и слишком велико потребление вычислительных ресурсов на пересчет всех остатков при проведении операций "задним числом". Однако, завязывать систему на операции "закрытия учетного периода" тоже некрасиво. В крупной системе пользователи должны иметь возможность вполне нормально работать в разных учетных периодах без каких-либо взаимных помех. Я предпочитаю, чтобы промежуточные остатки по регистру вообще не привязывались к учетному периоду. В своем проекте я привязываю их к критическому количеству прошедших по регистру движений. К примеру, промежуточные остатки могут подсчитываться после каждых 100 движений регистра с одинаковыми аналитическими признаками. В этом случае регистр сам сохранит несколько промежуточных значений остатков даже на протяжении одного дня, если по регистру "товары" за 1 день произошло около 300 движений одной номенклатуры на одном и том же складе. И в то же время по регистру "Ценные бумаги" может несколько кварталов учетного времени вообще не подсчитываться промежуточный остаток, если по этому регистру осуществляются единичные движения. Для получения остатков на запрошенную пользователем дату система извлекает из регистра ближайший к запрошенной дате промежуточный остаток (хранящийся в регистре), добавляет приход и вычитает расход за период до запрошенной даты и расчетным путем получает остаток на запрошенную дату. Технология расчета промежуточных остатков через 100 записей гарантирует, что при этом будет просмотрено не более 100 записей, и время ответа системы будет приемлемым. Регистр, как и справочник, представляет некоторую совокупность таблиц, запросов, форм и отчетов. Основные идеи по администрированию и организации совпадают с аналогичными для справочника. Следует заметить лишь некоторые особенности этого макрообъекта. Должно существовать специфическое право на закрытие/открытие учетных периодов регистра. Оно дается руководителям определенного уровня (например, главному бухгалтеру). В случае, если за прошедший период произведена выверка регистра (например, движения денежных ресурсов по кассе), все документы за этот период обработаны, и необходимо исключить возможность случайного проведения какого-либо документа этим периодом, главный бухгалтер должен иметь возможность "закрыть" регистр за этот учетный период. Ну, соответственно, и открыть в случае необходимости. Движение в регистре может происходить только при сохранении в системе электронных документов. Каждое движение в регистре хранит ссылку на вызвавший это движение документ (первопричину). В случае внесения изменений в документ или его удаления движение в регистре корректируется соответствующим образом (см. замечание о принципе "не удаляй физически"). К регистру могут быть привязаны особые функции контроля. Например, запрет движения, либо выдача предупреждения регистра при отрицательных остатках на складе, либо превышении лимита по кассе. Кстати, об отрицательных остатках товара. Почти все известные мне программные продукты "спотыкаются" об один и тот же подводный камень, который не мешало бы иметь в виду. Представьте, что на начало месяца на складе имеется 10 единиц товара. Юзер берет из вороха первичных документов на столе первый попавшийся (а там сверху обычно лежат те, которые сдали позже). Ему попадается накладная от 22 числа на отпуск 8 единиц товара. Он ее вводит в систему. Регистр проверяет: на 22 число есть 10 единиц товара, значит 8 можно отпустить - и молча выполняет операцию. После этого юзер берет следующую накладную - тоже расходную, но от 11 числа на 7 единиц товара и вводит ее в систему. Регистр проверяет: на 11 число есть 10 единиц товара, значит 7 можно отпустить. И опять молча разрешает операцию. А в результате по двум накладным отпущено 7+8=15 единиц, на 5 больше, чем было изначально. 22 числа возникает отрицательный остаток -5 единиц, который система не отловила. Контроль неотрицательности остатков должен производиться не на момент выполнения операции, а на всем протяжении учетного времени начиная от момента выполнении операции до самой последней совершенной операции. Любые движения регистра производятся через процедуру, которой передаются аргументы: имя регистра, код действия (приход/расход/вступит.остатки), набор аналитик для выполнения движения, набор ресурсов. Получение сводной информации об остатках или оборотах по регистру с любой степенью сворачивания или разворачивания аналитик производятся через пару процедур (одна для оборотов, другая для остатков). Документ - это некоторая форма, содержащая набор унарных полей (для одного документа ровно одно значение, например, поставщик в накладной) и grid-образную часть (каждое поле в которой может иметь множество значений, например, наименование номенклатуры в накладной). Документ может быть помечен как "черновик". В этом случае он просто сохраняется в архиве черновых документов, не вызывая никакого движения по регистрам. Если при вводе нового документа в систему на нем не поставлена метка "черновик", либо эта метка снята, запускается процедура проведения документа, которая привязывается к настройкам документам и вызывает движение по регистрам. Эта процедура выполняется в режиме транзакции. Если выполнить все движения регистров по какой-либо причине не удалось, производится откат транзакции, и документ возвращается в состояние до попытки его сохранения. Обработка проведения - это модуль, привязанный к форме документа и запускаемый в момент попытки сохранения документа, либо снятия метки "черновик". В случае внесения изменений в документ, старый вариант документа логически уничтожается (с откатом всех произведенных им движений в регистрах) и создается новый - в новой редакции (с повторным движением регистров). Таким образом, имеется возможность просмотра истории изменений одного и того же документа. Документ имеет, как правило, две формы - экранную для ввода и коррекции информации (форма MS Access) и печатную для вывода на бумагу (отчет MS Access). Документ так же, как регистр и справочник, представляет некоторую совокупность таблиц, запросов, форм и отчетов. Так же задействован в общей схеме администрирования и управления правами доступа. Документ одного вида может иметь специальные модули формирования на базе ранее оформленного документа, в том числе другого вида. Так, по ранее оформленному счету должна иметься возможность сформировать накладную и счет-фактуру, по накладной - счет и счет-фактуру, по счету-фактуре - накладную и счет. Должна иметься возможность установки гипертекстовых ссылок из одних документов в другие, с ними связанных (независимо от того, одинаковый у них тип или разный, и к каким учетным периодам они относятся). Должны быть разработаны специальные функции для обращения к полям документов и процедуры для запуска "бланка" документа для ввода нового документа, корректировки или удаления существующего. Журнал - массив информации, в который заносится информация о сохраненных в системе документах. Он играет роль своего рода предметного указателя для быстрого поиска нужного документа. В системе необходимо иметь один общий журнал, в котором аккумулируется информация о ВСЕХ документах ВСЕХ типов, по одному журналу на один вид документа (для поиска документа нужного вида), а так же произвольное количество журналов, в которые записывается информация о документах разного вида, объединяемых по какому-либо признаку (например, все виды накладных - приходные, расходные, на внутреннее перемещение и т.д.). Таким образом, во время записи документа в систему он проходит регистрацию в нескольких журналах. Настройки журнала содержат информацию о перечнях документов, которые должны проходить регистрацию в журнале и схему привязки полей журнала и документов. Журнал содержит поисковые средства, с помощью которых можно произвести поиск или отбор документов по совокупности полей журнала, а также простейшие средства для расчета итогов по числовым полям журналов (например, "всего продано на сумму:"). В случае удаления документа, он полностью из журнала не удаляется, лишь помечается как удаленный. Журнал документов имеет дополнительный атрибут прав доступа, через который администратор определяет, какие пользователи могут просматривать в журнале удаленные и/или отредактированные документы в прежних редакциях. Журнал так же, как справочник, регистр и документ, представляет некоторую совокупность таблиц, запросов, форм и отчетов. Так же задействован в общей схеме администрирования и управления правами доступа. Отчет - пожалуй, о нем особенно говорить нечего. Отчет - он и в Африке отчет. Единственное, что хотелось бы обратить внимание. Отчет-макрообъект может включать множество отчетов MS Access, запускаемых через форму MS Access (тоже компонента отчета в терминах системы). В форме обычно запрашиваются варианты компоновки отчета, группировки информации, степени детализации и т.п. Естественно, он также задействован в общей схеме администрирования и управления правами доступа. Просмотров: 15976
Ваш коментарий будет первым | | |