Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Тарасов С. В. СУБД для программиста. Базы данных изнутри

.pdf
Скачиваний:
80
Добавлен:
29.11.2021
Размер:
4.08 Mб
Скачать

ALTER DATABASE pass ADD FILEGROUP fg_pass_images;

/* Добавляем в группу физический файл */ ALTER DATABASE pass ADD FILE (

NAME = 'images_data',

FILENAME = 'G:\imgdata\images_data.ndf') TO FILEGROUP fg_pass_images;

/* Создаём таблицу изображений на отведенной файловой группе

*/

USE pass;

CREATE TABLE passage_images ( id_passage integer NOT NULL , image_num tinyint NOT NULL , image_data image NOT NULL , CONSTRAINT PK_passage_images

PRIMARY KEY CLUSTERED (id_passage, image_num) ) ON fg_pass_images;

СУБД PostgreSQL также позволяет задать для создаваемой таблицы её физическое пространство размещения (tablespace).

/* Создаём табличное пространство */

CREATE TABLESPACE images_data LOCATION '/mnt/imgdata/';

/* Создаём таблицу на пространстве */ CREATE TABLE passage_images (

id_passage integer NOT NULL , image_num smallint NOT NULL , image_data bytea NOT NULL , CONSTRAINT PK_passage_images

PRIMARY KEY (id_passage, image_num) ) TABLESPACE images_data;

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

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

171

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

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

Следует заметить, что реляционные СУБД, поддерживающие XML на уровне встроенного типа, позволяют осуществлять поиск и по всей совокупности хранящихся в колонке документов, но с худшей производительностью, чем по столбцам таблицы. Соответствующая функциональность аналогична предлагаемой NoSQL СУБД. О работе с XML будет рассказано в специальной главе раздела «Программирование».

Нужно ли моделировать?

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

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

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

Несмотря на наличие нескольких нотаций (IDEF1, ER, метод Баркера и др.), все они имеют принципиальное сходство и содержат относительно

172

небольшое число элементов (сущности, связи, атрибуты...). Если взять для сравнения гротескный эклектический UML, то количественная разница будет сразу заметна.

Использование графического инструментария для моделирования (автоматизированного проектирования) имеет два основных назначения:

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

генерация SQL-скриптов (сценариев инициализации) для одной или нескольких целевых СУБД

Конечно, если приложение оперирует десятком таблиц, то можно и моделировать, и хранить «картинку» с полной детализацией непосредственно в дефрагментированном мозге программиста или даже в коллективном сознании всего трудового коллектива. Однако, уже при 30-50 таблицах начинаются проблемы, прежде всего при изменении структуры. Назначениеколоноки другихэлементовсхемытоже надогде-то описывать, и вскоре рядом с проектом разработки приложения в инструментальной среде будет вестись какая-нибудь электронная таблица, постоянно запаздывающая в связи с очередными изменениями. В варианте ведения описаний непосредственно в программном коде тоже возникает немало проблем как дублирования текстов комментариев в файлах программ разных слоёв, так и их ручного переноса при реструктуризации.

Давайте рассмотрим простой пример, показывающий отличие ручного описанияотавтоматизированногопроектированиябазыданных. Вкачестве инструментария возьмём бесплатный pgModeler (http://www.pgmodeler.com.br), распространённый в процессах разработки для СУБД PostgreSQL.

Также следует отметить, что многие среды разработки, такие как Visual Studio или Delphi содержат урезанные по возможностям встроенные средствадляработыстаблицами, колонкамиипрочимиэлементамимодели непосредственно в соединении с СУБД.

173

Моделирование против ручного кодирования: пример

Предположим, в приложении необходимо вести учёт контактов и связанных с ними компаний: адреса, телефоны, «явки».

Программист №1 (П1) владеет средствами автоматизированного проектирования. Открыв среду pgModeler, он добавляет на экран две новых таблицы — «Компании» и «Контакты», в свойствах создаёт колонки соответствующих типов, затем соединяет их отношением «один-ко- многим». Получается примерно такая картинка (см. рис. 50).

Нажав на панели слева кнопку «Source», можно увидеть SQL-код, генерируемый средой.

Рис.50. Модель на первом этапе

Рис.51. Просмотр генерируемого кода

Сгенерированный код можно скопировать в буфер обмена и непосредственно выполнить в консоли SQL-запросов СУБД.

174

Можно поступить иначе: экспортировать (кнопка «Export») нашу схему непосредственно на сервер, определив соответствующие параметры соединения с СУБД.

Рис.52. Экспорт схемы БД

Необходимо также экспортировать схему в файл, сохранить его под заданным именем, пусть это будет create_model.sql, и добавить в соответствующее депо (repository) вашей системы контроля версий исходников программ (надеюсь, вы уже используете SVN, Git или другие средства контроля).

175

После всех проведённых П1 манипуляций в СУБД будут созданы две таблицы с соответствующими колонками, двумя первичными и одним внешним ключом.

Программист №2 (П2) предпочитает работать непосредственно с исходными кодами на SQL. Он просто открывает в обычном редакторе с подсветкой синтаксиса новый файл и пишет примерно такой текст.

CREATE TABLE public.companies( id_company serial,

name varchar(50), address varchar(100),

CONSTRAINT pk_companies PRIMARY KEY (id_company)

);

CREATE TABLE public.contacts( id_contact serial, first_name varchar(50), last_name varchar(50), phone varchar(16), email varchar(120), id_company integer,

CONSTRAINT pk_contacts PRIMARY KEY (id_contact)

);

ALTER TABLE public.contacts

ADD CONSTRAINT fk1_companies_contacts

FOREIGN KEY (id_company)

REFERENCES public.companies (id_company);

Затем файл также сохраняется под именем create_model.sql, добавляетсявсистемуконтроляверсийивыполняетсясконсолинацелевом СУБД-сервере. Результат тот же.

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

Но для любого софтостроения характерна необходимость внесения изменений.

176

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

П1 открывает модель, в свойствах таблицы добавляет две колонки «Город» и «Почтовый индекс» соответствующих типов. Вновь экспортирует схему в уже имеющийся файл create_model.sql, и пересоздаёт структуру на СУБД-сервере.

П2, пожимая плечами, открывает в редакторе исходный файл create_model.sql, и дописывает в оператор создания таблицы две строки

CREATE TABLE public.companies( id_company serial,

name varchar(50), address varchar(100), city varchar(30), postal_code varchar(10),

CONSTRAINT pk_companies PRIMARY KEY (id_company)

);

П2 сохраняетфайл, прогоняетеговSQL-консолии, еслинебылоошибок, также архивирует в депо.

Как можно заметить, на втором этапе никакой разницы в операциях практически нет, трудоёмкость уравнялась.

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

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

177

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

Рис.53. Модель на третьем этапе

П2, почесав затылок, открывает файл и начинает править код. Так, вопервых, таблицу «Адреса» нужно создавать раньше остальных, чтобы добавлять отграничения внешних ключей сразу после создания двух связанных таблиц, сохраняя структурность линейного текста. Во-вторых, надо удалить соответствующие колонки из оператора создания таблицы компаний. В-третьих, надо добавить в обе таблицы колонки id_address для внешнего ключа. Кстати, они будут NULL или NOT NULL? Поставим NULL, пусть не всегда вводят адрес. Да, надо ещё добавить комментарии для структурности, а то там потом не найдёшь нужное место глазами без глобального поиска.

178

Витогемаленькийпреждефайлраспухаетнаглазахиприобретаетновый вид.

-- Адреса

CREATE TABLE public.address( id_address serial, street varchar(50), postal_code varchar(10), city varchar(30),

CONSTRAINT pk_address PRIMARY KEY (id_address)

);

-- Компании

CREATE TABLE public.companies( id_company serial,

name varchar(50), id_address integer,

CONSTRAINT pk_companies PRIMARY KEY (id_company)

);

ALTER TABLE public.companies

ADD CONSTRAINT fk_address_companies FOREIGN KEY (id_address)

REFERENCES public.address (id_address);

-- Контакты

CREATE TABLE public.contacts( id_contact serial, first_name varchar(50), last_name varchar(50), phone varchar(16), email varchar(120), id_company integer, id_address integer,

CONSTRAINT pk_contacts PRIMARY KEY (id_contact)

);

ALTER TABLE public.contacts

ADD CONSTRAINT fk1_companies_contacts FOREIGN KEY (id_company)

REFERENCES public.companies (id_company); ALTER TABLE public.contacts

ADD CONSTRAINT fk_address_contacts FOREIGN KEY (id_address)

REFERENCES public.address (id_address);

179

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

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

Чтобы убедиться в согласованности получившейся схемы БД, П1 перед сохранением нажимает кнопку проверки («Validate»).

П2 вынужденноначинаетделатьто, что на жаргоне называется«лопатить код», неизбежно внося в него человеческие ошибки, потому что правка текста в одном месте (добавление или удаление связей) должна быть

строго согласована с соответствующей правкой в других его местах

(добавление/удаление столбцов, ограничений, индексов, содержащих удалённые колонки и т.д.).

Теперь представьте типовую ситуацию - появился третий программист (П3) в подмогу одному из двух. Как вы думаете, П3 поймёт текущую постановку задачи быстрее глядя на модель, изображённую на рис. 51 или пристально смотря на вышеприведённый кусок SQL-кода?

Наш пример был игрушечным, а как изменится скорость реструктуризации схемы БД если таблиц станет 50, потом 100, 200, 1000? Какова вероятность внесения ошибки при ручном изменении SQL-скрипта посравнениюсперерисовкойэлементовмоделинаграфическойсхеме? Для меня ответы на все эти вопросы не только очевидны, но и многократно подтверждены практикой.

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

180