Базы данных. Практикум
.pdfполнительного времени на счет, дополнительной памяти и т.п.), и иногда часто используемые интегральные показатели, такие как сумма счета, можно внести в таблицу. Следует помнить, что, введя, например, поле «Сумма по счету» при каждой коррекции табличной части необходимо пересчитывать и обновлять значение этого поля, так что экономия ма- шинных ресурсов получается весьма сомнительной.
Помимо обсуждавшихся выше атрибутов в рассматриваемую сущность добавлено поле «Оператор», в которое будет заноситься лицо, выписавшее счет.
Теперь о реквизитах покупателя (клиента). Этих реквизитов много (название фирмы, телефон, факс, адрес и т.д.). В соответствии с правилом 7 их следует разнести по разным полям. Очевидно, эти поля будут функ- ционально связаны между собой, и, если они не входят все в первичный ключ, будет нарушено Правило 6. Единственный способ решения этой проблемы – вынесение всех реквизитов клиентов в отдельный класс объ- ектов, (отдельную сущность) назовем его «Фирмы», а затем связать этот класс с классом «Счета на товар». Прежде всего заметим, что число кли- ентов хотя и может быть довольно большим, но все же ограничено. Да- лее, если и не каждый, то многие клиенты будут покупать неоднократно, так что есть смысл вместо занесения в таблицу «Товарные счета» всех атрибутов клиента хранить в ней только код клиента. Этот метод являет- ся естественным расширением правила 8 (об использовании словарей). Очевидно, код клиента должен быть уникальным, что сразу же подсказы- вает выбор первичного ключа для таблицы «Клиенты» – им будет поле «Код клиента». Сущность «Клиенты» далее обсуждать и использовать в базе данных не будем, чтобы не усложнять нашу задачу.
Перейдем к табличной части счета. Возвращаясь к табл. 4, видим, что в табличную часть должны входить наименование, единица изме- рения, цена, количество, ставка НДС, сумма НДС и сумма оплаты за данный товар. Наименование, единица измерения и, с некоторой на- тяжкой, ставка НДС (поскольку она может изменяться) относятся к постоянно используемым атрибутам, поэтому есть смысл поступить с ними так же, как с клиентами – создать отдельную таблицу «Номенк- латура», включить в нее поля «Наименование», «Единица измерения», «Ставка НДС» и, конечно, «Код товара» в качестве первичного ключа.
Тогда таблица «Товары по счетам» будет включать следующие поля: «ПНС» – внешний ключ для связи с таблицей «Товарные сче- та», «Код товара», «Цена» и «Количество».
Осталось разобраться со стоимостными показателями – стоимость товара и НДС за данный товар. Они функционально зависят от дру-
31
гих полей в таблице («Цена» и «Количество»), а посему в соот- ветствии с правилом 6 включать их в таблицу нежелательно. С дру- гой стороны как уже отмечалось, из этого правила можно делать ис- ключения. Однако в данном случае трудно найти аргументы в пользу включения этих полей.
Моделирование статических объектов
Под статическими объектами понимаются такие, которые на про- тяжении всего периода работы с приложением не изменяют свои ат- рибуты (или изменяют их достаточно редко).
Если, скажем, документ появляется, используется и оседает на хранение или уничтожается, то атрибуты товара (наименование, еди- ница измерения, и др.) остаются постоянными, независимо от теку- щего наличия товара в продаже.
В нашей задаче статистическим объектом является номенклатура товаров. Полное моделирование товарной номенклатуры требует очень многих допущений по классификации товаров и по выделению отдельного объекта в классе «Номенклатура» (например, считать ли товары, полученные от разных поставщиков или производителей, одним объектом). Принципиальных сложностей здесь нет, но обсуж- дение деталей займет слишком много места.
Поэтому ограничимся такими атрибутами, как «Код», «Наимено- вание товара», «Ставка акциза» и «Ставка НДС».
Моделирование динамических объектов
Динамический объект – это объект, атрибуты которого изменяются со временем, обычно в процессе выполнения производственной опе- рации. Изменяться могут как сами значения атрибутов, например сумма оплаты за товар при выплате по частям, так и набор атрибутов.
Проиллюстрируем сказанное выше на примере единичного акта про- дажи товара. Чтобы отслеживать состояние сделки хотелось бы иметь некий объект, позволяющий отслеживать состояние акта продажи.
Снова попробуем создать нечто типа «учетной карточки» этого объекта. Прежде всего, он должен содержать сведения о клиенте и о заказе (заказанные товары, вид оплаты, вид доставки). Оказывается, что такой объект фактически уже имеется. В табл. 2 он назван груп- пой информации «Заказы».
Нетрудно видеть, что класс объектов «Заказы» тоже будет иметь структуру документа. В качестве табличной части здесь выступают заказанные товары, причем структура табличной части в точности
32
повторяет структуру таблицы «Товары по счету» (если заменить внешний ключ на поле «Номер заказа», которое введем чуть ниже).
В заголовок входят поля «Код клиента»; «Вид оплаты» – оче- видно, как код, подставляемый из таблицы «Виды оплаты» (прави- ло 8); «Вид доставки» – аналогично виду оплаты.
Кроме того сюда включаются дата заказа – для экономии места под названием «Заказано», и поле «Принял» – для лица, принявшего заказ. Введем первичный ключ «Номер заказа».
Итак, создан класс объектов (сущность) «Заказы», который опи- сывает производственную операцию «Оформление заказа». Но ведь на самом деле мы хотели создать объект (класс объектов), который позволял бы отслеживать состояние акта продажи, а не описывать одну операцию!
Такого объекта, который бы описывался реляционной структурой, специально создавать не нужно. В самом деле, какие атрибуты характе- ризуют динамику нашего процесса? Наверное, даты выполнения (завер- шения) соответствующих операций. Можно, конечно, детализировать структуру, например, ввести даты начала операции, завершения операции и так далее. Что это нам даст? Рассмотрим операцию «Выписка счета на товар». Если в таблицу «Товарные счета», рассмотренную выше, доба- вить внешний ключ «Номер заказа» и связать ее в качестве подчиненной с таблицей «Заказы», мы естественным образом будем контролировать состояние операции «Выписка товарного счета», проверяя наличие свя- занной с записью соответствующего заказа записи в таблице «Товарные счета». Это можно будет проделывать с помощью некоторого запроса, включающего реквизиты заказа и реквизиты счета. Отсутствие реквизи- тов счета для данного заказа будет свидетельствовать о том, что данная операция еще не выполнена, а наличие даты выписки (в поле «Выписан») будет свидетельствовать о завершении операции.
Аналогичным образом можно контролировать операцию «Вы- писка отпускных документов». Если же отлажено четкое взаимодей- ствие между подразделениями, и от выписки отпускных документов до отпуска товара происходит достаточно малый период времени, то можно считать, что выполнение этой операции гарантировано обес- печивает выполнение операции «Отпуск товара». Если же эту опера- цию требуется контролировать отдельно, можно ввести в таблицу «Заказы» поле даты / времени отпуска товара.
Более интересной проблемой является обеспечение контроля оп- латы, поскольку последняя может быть растянута во времени (оплата по частям).
33
Создадим подчиненную таблицу «Оплата» и свяжем ее с таблицей «Заказы» по «Номеру заказа». Эта таблица будет содержать ин- формацию о виде поступления денег (через кассу, по перечислению и т.д.), поступившую сумму и реквизиты платежных документов. Можно было бы связать «Оплата» со счетами, однако в этом случае придется выписывать счета для всех продаж (наличный расчет, реа- лизация), что явно не рационально.
Таким образом, большая часть операций контролируется без соз- дания специальных классов контроля. В этом нам очень помогло то, что практически к каждой производственной операции можно привя- зать некий документ или, на худой конец, электронную «учетную карточку» операции.
Если же для некоторой операции не рационально вводить учет- ную карту, например, для операции резервирования товара (если вводить отдельную заявку на резервирование, вся информация, со- держащаяся в ней, дублирует информацию из таблицы «Заказы» и «Товарный счет» или «Заказанные товары»), то достаточно ввести поля контрольных дат для этой операции в таблице «Заказы».
Набор выделенных сущностей и логические связи между ними (т.е. концептуальная схема базы данных) представлены на рис. 3.
Рис. 3. Концептуальная схема базы данных
34
На схеме связи разных видов имеют разную маркировку. Связь с символами 1 и «бесконечность» означает естественное соединение, т.е. соединение записей, в которых связанные поля таблиц совпада- ют. Одинарные стрелки (без символов) означают внешние соединения (левые или правые). Левое соединение предполагает соединение всех записей из дополнительной таблицы и тех записей основной табли- цы, в которых связанные поля совпадают. Правое соединение пред- полагает соединение всех записей из основной таблицы и тех запи- сей дополнительной таблицы, в которых связанные поля совпадают.
Замечание: иногда в литературе вместо термина соединение ис- пользуется термин объединение. Неудачный термин, если вспомнить, что в реляционной алгебре есть особая операция объединения.
Отметим, что внешние соединения особенно полезны при опреде- лении того, какие из значений в связанных таблицах вызывают про- блемы целостности на уровне ссылки – какие из них были созданы, когда значения внешнего ключа не соответствовали значениям пер- вичного ключа в связанных таблицах. Особенно такие соединения полезны, если вам необходимо конвертировать большие объемы не упорядоченной информации в таблицы реляционной базы данных. Этим можно сэкономить массу времени при решении проблемы це- лостности. Другие особенности использования внешних соединений будут рассмотрены в разделе 4.
Подведем итоги.
1.Для моделирования динамических объектов используется тот факт, что изменения объекта обычно связаны с выполнением тех или иных производственных операций, порождающих либо документ, либо некий «виртуальный» бланк учета этих операций.
2.Состояние динамического объекта контролируется по наличию или отсутствию записей этих документов (бланков), или же, если вводить такой бланк в качестве отдельного класса нерационально, – по полям дат выполнения операции.
3.Первичное состояние динамического объекта обычно модели- руется классической структурой документа.
3.2. Логическая схема базы данных
Логическое проектирование базы данных предполагает описание базы данных в контексте используемой СУБД, т.е. с учетом ее осо- бенностей.
35
Ниже приведены структуры всех таблиц для СУБД Access, кото- рые будут использоваться в проектируемом приложении (рис. 4).
36
Рис. 4. Структура таблиц базы данных
Мы не приводим здесь логической схемы базы данных, так как в нашем случае она полностью совпадает с концептуальной схемой, рассмотренной выше.
Это «совпадение» обусловлено двумя причинами:
•все сущности однозначно отображены в таблицах;
•отсутствуют индексы, представления и другие объекты базы данных.
37
4. РАЗРАБОТКА ЗАПРОСОВ
4.1. Задание связей
Наиболее сложным моментом при конструировании запроса явля- ется задание связей между таблицами. Запрос, состоящий из одной таблицы, в этом отношении существенно проще – достаточно не до- пускать грамматических ошибок в написании условий отбора.
Часть этих связей конструктор запроса выводит сам. Это проис- ходит в двух случаях.
1.Если связи между выбранными таблицами задавались на стадии проектирования таблиц.
2.Если в двух выбранных таблицах есть поля с одинаковыми на- званиями и в одной из них это поле является ключом.
В первом случае тип связи наследуется от заданного на стадии проектирования, во втором – система задает естественное соедине- ние, то есть в запросе будут использоваться только те записи, в кото- рых значения связывающих полей совпадают.
Прежде всего, отметим, что необязательно использовать выведен- ные построителем связи – их можно удалить или изменить на тре- буемые.
И здесь появляется первая проблема – какие именно связи вам по- требуются?
Перечислить все правила, по которым нужно задавать связи для тех или иных запросов, вряд ли возможно, ибо слишком велико ко- личество вариантов. Поэтому только продемонстрируем на несколь- ких примерах возможные ошибки при задании связей, а остальное придет к вам из практики.
Прежде всего, посмотрим, что может произойти при использова- нии естественных соединений.
Рассмотрим простейший пример. В таблице «Заказы» «Виды оп- латы» задается кодом. Если требуется просмотреть перечень заказов
суказанием видов оплат, то необходимо создать запрос из двух таб- лиц: «Заказы» и «Виды оплаты». Связь будет осуществляться по по- лю «Виды оплаты» в таблице «Заказы» и «Код оплаты» в таблице «Виды оплаты».
Если задать естественное соединение, то в запросе будут выведе- ны только те заказы, для которых код оплаты указан. К сожалению, операторы при вводе нового заказа могут забывать задать вид опла-
38
ты, и, просмотрев такой запрос, можно обнаружить, что в перечне заказов весьма многие отсутствуют. Можно, конечно, сделать поле «Вид оплаты» в таблице «Заказы» обязательным для заполнения, од- нако это невозможно сделать для всех случаев, в которых придется связывать различные таблицы.
Отсюда первая рекомендация: крайне осторожно используйте в запросах естественные соединения.
При использовании внешних соединений возникают другие про- блемы. Первая проблема – какие именно записи надо вывести. Ска- жем, если в вышеприведенном примере задать внешнее объединения типа «Объединение ВСЕХ записей из «Виды оплаты» и только тех записей из «Заказы», в которых связанные поля совпадают», то ре- зультирующая таблица будет содержать все виды оплат, однако те заказы, в которых код оплаты не указан, снова будут отсутствовать.
Другая проблема, гораздо более неприятная, вытекает из самого способа построения запросов из нескольких таблиц. Построение та- ких запросов производится последовательно: результат соединения двух таблиц соединяется с третьей; последний с четвертой и так да- лее. Если связи заданы таким образом, что в зависимости от выбора порядка такого соединения результат запроса получается различным (а это обычно свидетельствует о том, что в запросе появились неяв- ные связи – многие ко многим, запрещенные в реляционной модели), система отказывается строить такой запрос, причем проверка пра- вильности задания связей производится только при попытке про- смотреть результат запроса (если вы создадите такой «неправиль- ный» запрос и, не просматривая записи, сохраните его, система ни- как не отреагирует). Обычно в этом случае выдается системное со- общение «Запрос содержит неявные внешние соединения», и система отказывается выводить результирующую таблицу, хотя и не возра- жает против его сохранения.
Чтобы обнаружить, где ошибка, можно удалять по одной связи и пытаться просматривать результирующую таблицу, пока системное сообщение не перестанет появляться.
Можно сформулировать эмпирическое правило проверки кор- ректности задаваемых связей, правда только для того случая, когда все связи являются внешними соединениями: На схеме QBЕ-запроса
все стрелки, показывающие внешние соединения, должны быть
направлены в одну сторону.
Поясним сказанное на таблицах из приведенного выше примера, добавив еще таблицу «Вид доставки».
39
На рис. 5 показан неправильно построенный запрос: стрелки свя- зей направлены друг к другу и обе «входят» в таблицу «Заказы». Та- кой запрос система не пропустит.
Рис. 5. Пример неправильного соединения таблиц
Пример правильного, хотя и не очень полезного, соединения по- казан на рис. 6.
Рис. 6. Пример правильного, но не очень полезного соединения
На самом деле для получения полного списка заказов с указанием вида оплат и вида поставки, если они введены, надо использовать соединение, показанное на рис. 7.
Рис. 7. Пример правильного соединения с получением полезного результата
40
