Теория СУБД

ЧТО ДАЛЬШЕ?

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

КАК СОЗДАТЬ И ИСПОЛЬЗОВАТЬ БАЗУ ДАННЫХ

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

БАЗА ДАННЫХ: ЗАЧЕМ И ПОЧЕМУ?

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

В первом случае можно, конечно, ограни­читься обыкновенными записями в тексто­вом файле (например, в документе Word), тем более что туда легко можно заносить разнообразную "списочную" информацию: сведения о своих друзьях-абонентах, их ад­ресах проживания и т.п. Способ представле­ния и размещения информации в этом слу­чае ты придумаешь сам. К примеру, построч­но запишешь: "Иванов, Иван, Иванович, 223-5485, ул. Декабристов, 18/1-64", "Сергей Сер­геевич Сидоров, 375-6986, пр. Ленина, д.18, кв. 49" и т.д. Что же плохого в такой органи­зации данных?

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

Однако описанная организация данных не позволит сделать ни первое, ни второе. Де­ло в том, что упорядочить информацию в текстовом файле достаточно сложно. Гораз­до проще сделать это без всякого компьюте­ра, имея сведения, записанные на картонных карточках :). Машина не сможет даже выб­рать правильно номера домов и квартир, по­тому что они могут быть записаны по-разно­му. Это для тебя записи "18/1-64" и "д. 18, корп. 1, кв. 64" — одно и то же, а для компью­тера это совершенно разные вещи. А если взять второй упомянутый пример по учету посещаемости форума, то здесь Word’у во­обще "не объяснить", где IP-адрес машины, а где дата подключения этой машины, которая нужна для подсчета посещений за опреде­ленный период.

Чтобы научить глупую машину безоши­бочно искать и систематизировать данные, надо прежде всего сообщить ей правила иг­ры (соглашения) о способах представления данных. Такой процесс называется структу­рированием информации, и он производит­ся путем введения типов: текстовых, число­вых и т.п. А также форматов данных (напри­мер, формат даты). Для таких структуриро­ванных данных придумали специальный вид файлов — базу данных (БД). Другими слова­ми, база данных предназначена для хране­ния некоторого объема структурированных данных под определенным именем во внеш­ней памяти.

КАКИЕ ОНИ БЫВАЮТ?

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

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

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

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

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

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

Наконец, доктор наук математик Э.Ф. Кодд (США) придумал реляцион­ную модель, гениальную по своей простоте. Единственной структурой данных, которую видит пользователь, является двухмерная таблица. Назва­ние "реляционная" происходит от слова relation (англ. "отношение"). Кодд придумал также основные опе рации, которые легко могут обрабо­тать данные в таблицах и получить результат в виде новой таблицы. При­чем уникальность таких манипуляций данными в том, что за одну операцию можно обработать одновременно все данные таблицы и даже нескольких таблиц. И тебе не придется писать ни­каких циклических процедур, обраба­тывая каждую запись отдельно!

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

ЧТО ТАКОЕ ПРАВИЛЬНАЯ БАЗА ДАННЫХ?

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

За хранение данных в базе, их обра­ботку и взаимодействие с прикладны­ми программами отвечает отдельный класс программ — системы управления базами данных (например, MS Access, FoxPro, MS SQL Server, Oracle и дру­гие). Они отличаются друг от друга функциональностью, производитель­ностью, стоимостью и т.п., но, в прин­ципе, все предназначены для реше­ния вышеуказанных задач. Если хо­чешь заставить СУБД правильно вы­полнять свои функции и сопровож­дать базу данных, постарайся органи­зовать свою работу так, чтобы соблю­дались оба принципа. Иначе тебе при­дется в основном бороться с самой СУБД. Что часто и случается :).

НУЖНА НАГЛЯДНАЯ СХЕМА!

Как ты уже понял, при построе­нии "правильной базы данных" мно­гое зависит от ее структуры, то есть схемы. Из каких таблиц и атрибутов должна состоять схема базы данных? Какие атрибуты выбрать в качестве ключевых? Надо ли связывать эти таблицы между собой? Подобные вопросы могут возникнуть у кого угодно, и чтобы ответить на них, тре­буется научиться моделировать схе­му базы данных. Для этого были при­думаны специальные диаграммы "сущность-связь" (ER-диаграммы), которые позволяют легко и наглядно проектировать структуру баз данных без привязки к конкретным СУБД. Методика, согласно которой исполь­зуются ER-диаграммы, оказалась нас­только успешной и полезной на прак­тике, что легла в основу целого клас­са программных продуктов, так назы­ваемых CASE-средств проектирова­ния информационных систем. Наибо­лее распространенная программа это­го класса — Erwin 

А КАК ЭТО СДЕЛАТЬ?

Главная проблема, которую тре­буется решить при создании базы данных, — создать для нее такую структуру, которая бы обеспечивала минимальное дублирование инфор­мации и упрощала процедуры обра­ботки и обновления данных, предс­тавленных набором таблиц. Для того чтобы облегчить твою жизнь, теоре­тики баз данных предложили универ­сальный способ решения этой проб­лемы. Этот способ сформулирован в виде специальных требований к орга­низации данных в ходе проектирова­ния, которые получили названия нор­мальных форм (НФ). Первые три нор­мальные формы оказались самыми живучими и распространились боль­ше других.

Согласно требованиям первой нор­мальной формы, все атрибуты табли­цы должны быть простыми, то есть состоять из одного неделимого эле­мента данных. Например, если сде­лать в базе данных атрибут "Адрес",то в него можно будет заносить зна­чения данных типа "г. Москва, 3-я улица Строителей, д. 25, кв. 12". Но определить, из какого города чело­век с таким адресом и существует ли такой же адрес в другом городе, тебе будет, поверь, очень сложно, потому что придется писать целую процеду­ру обработки текстовой записи, что­бы вычленить город.

Вторая нормальная форма требует соблюдения условий первой НФ, а также дополнительно каждый неклю­чевой атрибут должен однозначно зависеть только от первичного клю­ча. Имеются в виду функциональные зависимости из реальной предметной области. Здесь возникают проблемы с выявлением зависимостей, если первичный ключ является состав­ным, то есть состоит из нескольких атрибутов.

Таблица находится в третьей нор­мальной форме, если она удовлетво­ряет требованиям второй НФ и если при этом любой неключевой атрибут зависит от ключа нетранзитивно (термин понятен по примеру из жиз­ни — транзитный, промежуточный вок­зал). Транзитивной является такая зависимость, при которой какой-либо неключевой атрибут зависит от дру­гого неключевого атрибута, а тот, в свою очередь, уже зависит от ключа.

СХЕМА ЕСТЬ — УМА НЕ НАДО?

После определения основных объектов и характеризующих их ат­рибутов надо продумать "поведен­ческие" аспекты твоей базы данных. Другими словами, определить, что будет происходить при вставке, кор­ректировке и удалении реальных за писей. Останутся ли при этом данные в твоей базе правильными? Не поя­вится ли в ней противоречивая ин­формация? Эти вопросы порождают известную в теории проблему обес­печения целостности данных. Целост­ность бывает двух видов: целост­ность сущностей и целостность по ссылкам.

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

С обеспечением требований по ссылкам на другие таблицы дело обс­тоит сложнее. Лучше показать это на примере. Допустим, ты разрабатыва­ешь базу данных для сопровождения своего форума, и тебе надо хранить информацию о зарегистрированных пользователях. Каждый пользова­тель состоит в определенной группе, в соответствии с которой ему назна­чены права (например, administrators, moderators, registered, banned и т.д.). При правильном проектировании структуры у тебя появятся две свя­занные таблицы:

USERS (id_user, user_login, user_mail, user_icq, fk_id_group), первичный ключ id_user;

GROUPS (id_group, name_group, rights) пер­вичный ключ id_group

Атрибут fk_id_group появляется в таблице USERS не потому, что номер группы является собственным свой­ством пользователя, а лишь для то­го, чтобы при необходимости восста­новить полную информацию о груп­пе. Значение атрибута fk_id_group в любой строке таблицы USERS долж­но соответствовать значению атри­бута id_group в некоторой строке таблицы GROUPS. Такой атрибут на­зывается внешним ключом (foreign key), поскольку его значения одноз начно характеризуют объекты, представленные строками некоторо­го другого, внешнего отношения (то есть задают значения их первичного ключа). Отношение, в котором опре­делен внешний ключ, ссылается на соответствующее отношение, в кото­ром такой же атрибут является пер­вичным ключом.

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

Каким образом обеспечить ссылоч­ную целостность? Понятно, что при обновлении ссылающегося отноше­ния (например, в таблице USERS вставляешь новые строки или кор­ректируешь значения внешнего клю­ча, то есть переводишь пользовате­ля в новую группу) достаточно сле­дить за тем, чтобы не появлялись не­корректные значения внешнего клю­ча. Но как быть при удалении из таб­лицы строки, к которой ведет ссыл­ка? Предусмотрены две возможные операции: каскадирование (cascade) или ограничение (restrict). Эти опера­ции можно установить на связь меж­ду двумя таблицами.

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

Необходимо также предусмотреть технологию того, что будет происхо­дить при попытке обновления пер­вичного ключа отношения, на кото­рое ссылается некоторый внешний ключ. Здесь имеются те же возмож­ности, что и при удалении: можно каскадировать или ограничить опе­рацию. Например, ты захотел изме-ненить id_group в таблице GROUP на форуме и одновременного отразить все изменения на заинтересованных пользователях в таблице USERS. Тогда установи операцию каскадиро­вания при обновлении данных на связь между этими таблицами.

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